0.1% Chance Global Element Deletion with Enhanced Persistence

Gives a 0.1% chance to delete anything on any page, remembers deletions globally using multiple methods, and monitors dynamically loaded elements.

当前为 2024-08-27 提交的版本,查看 最新版本

// ==UserScript==
// @name         0.1% Chance Global Element Deletion with Enhanced Persistence
// @namespace    Fists
// @version      1.5
// @description  Gives a 0.1% chance to delete anything on any page, remembers deletions globally using multiple methods, and monitors dynamically loaded elements.
// @author       You
// @license      CC BY 4.0; https://creativecommons.org/licenses/by/4.0/
// @match        *://*/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    const STORAGE_KEY = 'globalDeletedNodes_v4';

    // Load deleted nodes from localStorage
    let deletedNodes = JSON.parse(localStorage.getItem(STORAGE_KEY)) || [];

    // Function to generate XPath for a node
    function getXPathForElement(element) {
        const idx = (sib, name) => sib 
            ? idx(sib.previousElementSibling, name || sib.localName) + (sib.localName == name) 
            : 1;
        const segs = elm => !elm || elm.nodeType !== 1 
            ? [''] 
            : elm.id && document.getElementById(elm.id) === elm 
            ? [`id("${elm.id}")`] 
            : [elm.localName.toLowerCase() + (elm.className ? `[@class="${elm.className}"]` : '') + `[${idx(elm)}]`, ...segs(elm.parentNode)];
        return segs(element).join('/');
    }

    // Function to generate a selector for a node
    function getSelectorForElement(element) {
        if (element.id) {
            return `#${element.id}`;
        }
        if (element.className) {
            return `.${element.className.split(' ').join('.')}`;
        }
        return element.tagName.toLowerCase();
    }

    // Function to generate a robust identifier for a node
    function getNodeIdentifier(element) {
        const xpath = getXPathForElement(element);
        const selector = getSelectorForElement(element);
        const tagInfo = `${element.tagName}_${element.className}_${element.id}_${element.name}_${element.innerText.length}`;
        return { xpath, selector, tagInfo };
    }

    // Function to check if a node has been previously deleted
    function isNodeDeleted(identifiers) {
        return deletedNodes.some(deleted => 
            deleted.xpath === identifiers.xpath ||
            deleted.selector === identifiers.selector ||
            deleted.tagInfo === identifiers.tagInfo
        );
    }

    // Function to store deleted node identifiers
    function storeDeletedNode(identifiers) {
        if (!isNodeDeleted(identifiers)) {
            deletedNodes.push(identifiers);
            localStorage.setItem(STORAGE_KEY, JSON.stringify(deletedNodes));
        }
    }

    // Function to delete an element by various methods
    function deleteElement(element, identifiers) {
        if (element) {
            element.remove();
            storeDeletedNode(identifiers);
            console.log('Element deleted and stored:', identifiers);
        }
    }

    // Function to apply deletions using stored identifiers
    function applyStoredDeletions() {
        deletedNodes.forEach(identifiers => {
            // Try XPath first
            const xpathResult = document.evaluate(identifiers.xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
            let node = xpathResult.singleNodeValue;

            // Try CSS Selector if XPath fails
            if (!node) {
                node = document.querySelector(identifiers.selector);
            }

            // Final fallback: Tag information search
            if (!node) {
                const allNodes = document.querySelectorAll('*');
                node = [...allNodes].find(el => getNodeIdentifier(el).tagInfo === identifiers.tagInfo);
            }

            // Delete the node if found
            if (node) {
                deleteElement(node, identifiers);
            }
        });
    }

    // Function to attempt deletion of any node
    function tryDeleteNode(node) {
        const identifiers = getNodeIdentifier(node);
        if (Math.random() < 0.001 && !isNodeDeleted(identifiers)) { // 0.1% chance
            deleteElement(node, identifiers);
        }
    }

    // Observer to watch for new elements, attributes, and nodes being added or modified in the DOM
    const observer = new MutationObserver(mutations => {
        mutations.forEach(mutation => {
            mutation.addedNodes.forEach(node => {
                if (node.nodeType === Node.ELEMENT_NODE) {
                    tryDeleteNode(node);
                }
            });
        });
    });

    // Configuration to observe child nodes and text content
    observer.observe(document.documentElement, {
        childList: true,
        subtree: true,
        characterData: true
    });

    // Initial pass to try deleting elements that are already loaded
    const allNodes = document.querySelectorAll('*');
    allNodes.forEach(element => {
        tryDeleteNode(element);
    });

    // Apply deletions from stored identifiers
    applyStoredDeletions();
})();