Torn – Hide Notes Button (site-wide)

Hides the Notes button across all Torn pages, including dynamic loads and shadow roots.

// ==UserScript==
// @name         Torn – Hide Notes Button (site-wide)
// @namespace    ASTA-MK
// @version      1.0
// @license      MIT
// @description  Hides the Notes button across all Torn pages, including dynamic loads and shadow roots.
// @match        https://www.torn.com/*
// @run-at       document-start
// @grant        GM_addStyle
// ==/UserScript==

(function () {
  'use strict';

  // Hide via CSS as early as possible (works if not inside a shadow root)
  try {
    if (typeof GM_addStyle === 'function') {
      GM_addStyle(`#notes_panel_button{display:none !important;}`);
    } else {
      const style = document.createElement('style');
      style.textContent = `#notes_panel_button{display:none !important;}`;
      document.documentElement.appendChild(style);
    }
  } catch {}

  // Helper: hide an element robustly
  function hideEl(el) {
    if (!el) return;
    el.setAttribute('hidden', 'true');
    el.setAttribute('aria-hidden', 'true');
    el.style.setProperty('display', 'none', 'important');
    el.style.setProperty('visibility', 'hidden', 'important');
    el.style.setProperty('pointer-events', 'none', 'important');
  }

  // Search a root (document or shadowRoot) for matches
  function hideInRoot(root) {
    if (!root || !root.querySelectorAll) return;
    // Primary target by ID, plus a safe fallback by title (in case ID changes)
    const candidates = root.querySelectorAll(
      '#notes_panel_button, button#notes_panel_button, button[title="Notes"]#notes_panel_button, button[title="Notes"]'
    );
    candidates.forEach(hideEl);
  }

  // Recursively observe document + any shadow roots
  function observeRoot(root) {
    if (!root) return;

    // Initial pass
    hideInRoot(root);

    const mo = new MutationObserver((mutations) => {
      for (const m of mutations) {
        // Check added nodes directly
        m.addedNodes && m.addedNodes.forEach((node) => {
          if (node.nodeType !== 1) return; // ELEMENT_NODE
          // If this node is the button or contains it, hide it
          if (
            node.id === 'notes_panel_button' ||
            (node.matches && node.matches('button[title="Notes"]')) ||
            (node.querySelectorAll && node.querySelectorAll('#notes_panel_button, button[title="Notes"]').length)
          ) {
            hideInRoot(node.ownerDocument || document);
            hideInRoot(node.shadowRoot || node);
          }
          // If this node has a shadow root, observe inside it too
          if (node.shadowRoot) observeRoot(node.shadowRoot);
        });
      }
    });

    mo.observe(root, { childList: true, subtree: true });

    // Also hook into any existing shadow roots under this root
    if (root.querySelectorAll) {
      root.querySelectorAll('*').forEach((el) => {
        if (el.shadowRoot) observeRoot(el.shadowRoot);
      });
    }
  }

  // Start observing as soon as possible
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', () => observeRoot(document));
  } else {
    observeRoot(document);
  }

  // Extra: catch late-created top-level shadow roots (rare, but cheap)
  new MutationObserver(() => {
    document.querySelectorAll('*').forEach((el) => {
      if (el.shadowRoot && !el.shadowRoot.__tornNotesObserved) {
        el.shadowRoot.__tornNotesObserved = true;
        observeRoot(el.shadowRoot);
      }
    });
  }).observe(document.documentElement, { childList: true, subtree: true });
})();