Shadowlister(Torn Auto-Anon Toggle)

Toggle all item listings anonymous ON/OFF while keeping manual control

// ==UserScript==
// @name         Shadowlister(Torn Auto-Anon Toggle)
// @namespace    http://tampermonkey.net/
// @version      1.4
// @description  Toggle all item listings anonymous ON/OFF while keeping manual control
// @match        https://www.torn.com/page.php?sid=ItemMarket*
// @grant        none
// @run-at       document-idle
// ==/UserScript==

(function () {
  'use strict';

  let autoAnon = true;

  // Create container for button with drag header
  const buttonContainer = document.createElement('div');
  Object.assign(buttonContainer.style, {
    position: 'fixed',
    top: '50%',
    left: '10px',
    transform: 'translateY(-50%)',
    zIndex: '9999',
    borderRadius: '6px',
    overflow: 'hidden',
    opacity: 0.9,
    userSelect: 'none',
    border: '2px solid #0f0',
  });

  // Create drag header
  const dragHeader = document.createElement('div');
  dragHeader.textContent = '⋮⋮';
  Object.assign(dragHeader.style, {
    backgroundColor: '#0f0',
    color: '#222',
    padding: '2px 12px',
    fontSize: '10px',
    textAlign: 'center',
    cursor: 'grab',
    fontWeight: 'bold',
    lineHeight: '1',
  });

  // Create toggle button
  const toggleBtn = document.createElement('button');
  toggleBtn.textContent = 'Auto-Anon: ON';
  Object.assign(toggleBtn.style, {
    backgroundColor: '#222',
    color: '#0f0',
    border: 'none',
    padding: '8px 12px',
    fontSize: '14px',
    cursor: 'pointer',
    width: '100%',
    display: 'block',
  });

  toggleBtn.onclick = () => {
    autoAnon = !autoAnon;
    toggleBtn.textContent = `Auto-Anon: ${autoAnon ? 'ON' : 'OFF'}`;
    toggleBtn.style.color = autoAnon ? '#0f0' : '#f00';
    buttonContainer.style.borderColor = autoAnon ? '#0f0' : '#f00';
    dragHeader.style.backgroundColor = autoAnon ? '#0f0' : '#f00';
    applyToAllVisible();
  };

  // Assemble the container
  buttonContainer.appendChild(dragHeader);
  buttonContainer.appendChild(toggleBtn);

  // Make container draggable using the proven approach
  makeDraggable(buttonContainer, dragHeader);

  document.body.appendChild(buttonContainer);

  // Draggable functionality (based on working example)
  function makeDraggable(element, dragHandle) {
    let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
    
    dragHandle.onmousedown = dragMouseDown;
    dragHandle.ontouchstart = dragTouchStart;
    
    function dragMouseDown(e) {
      e = e || window.event;
      e.preventDefault();
      pos3 = e.clientX;
      pos4 = e.clientY;
      document.onmouseup = closeDragElement;
      document.onmousemove = elementDrag;
      dragHandle.style.cursor = 'grabbing';
    }
    
    function dragTouchStart(e) {
      const touch = e.touches[0];
      pos3 = touch.clientX;
      pos4 = touch.clientY;
      document.ontouchend = closeDragElement;
      document.ontouchmove = elementTouchDrag;
      dragHandle.style.cursor = 'grabbing';
    }
    
    function elementDrag(e) {
      e = e || window.event;
      e.preventDefault();
      pos1 = pos3 - e.clientX;
      pos2 = pos4 - e.clientY;
      pos3 = e.clientX;
      pos4 = e.clientY;
      
      const newTop = element.offsetTop - pos2;
      const newLeft = element.offsetLeft - pos1;
      
      // Keep within viewport bounds
      const maxX = window.innerWidth - element.offsetWidth;
      const maxY = window.innerHeight - element.offsetHeight;
      
      element.style.top = Math.max(0, Math.min(newTop, maxY)) + "px";
      element.style.left = Math.max(0, Math.min(newLeft, maxX)) + "px";
      element.style.transform = 'none';
    }
    
    function elementTouchDrag(e) {
      const touch = e.touches[0];
      pos1 = pos3 - touch.clientX;
      pos2 = pos4 - touch.clientY;
      pos3 = touch.clientX;
      pos4 = touch.clientY;
      
      const newTop = element.offsetTop - pos2;
      const newLeft = element.offsetLeft - pos1;
      
      // Keep within viewport bounds
      const maxX = window.innerWidth - element.offsetWidth;
      const maxY = window.innerHeight - element.offsetHeight;
      
      element.style.top = Math.max(0, Math.min(newTop, maxY)) + "px";
      element.style.left = Math.max(0, Math.min(newLeft, maxX)) + "px";
      element.style.transform = 'none';
    }
    
    function closeDragElement() {
      document.onmouseup = null;
      document.onmousemove = null;
      document.ontouchend = null;
      document.ontouchmove = null;
      dragHandle.style.cursor = 'grab';
    }
  }

  // Tick/Untick all visible checkboxes
  function applyToAllVisible() {
    const allBoxes = document.querySelectorAll('input[id^="itemRow-incognitoCheckbox-"]');
    allBoxes.forEach(box => {
      if (autoAnon && !box.checked) {
        box.click();
      } else if (!autoAnon && box.checked) {
        box.click();
      }
    });
  }

  // Observer for new rows
  const observer = new MutationObserver(mutations => {
    for (let mutation of mutations) {
      for (let node of mutation.addedNodes) {
        if (!(node instanceof HTMLElement)) continue;
        const checkboxes = node.querySelectorAll?.('input[id^="itemRow-incognitoCheckbox-"]') || [];
        checkboxes.forEach(checkbox => {
          if (autoAnon && !checkbox.checked) {
            checkbox.click();
          } else if (!autoAnon && checkbox.checked) {
            checkbox.click();
          }
        });
      }
    }
  });

  function initObserver() {
    if (location.hash.startsWith('#/addListing')) {
      observer.observe(document.body, { childList: true, subtree: true });
      applyToAllVisible(); // First batch
    }
  }

  window.addEventListener('hashchange', initObserver);
  window.addEventListener('load', initObserver);
  initObserver();
})();