Anti-UI Purge Library

Prevents specified DOM elements from being destroyed by page state changes

Dit script moet niet direct worden geïnstalleerd - het is een bibliotheek voor andere scripts om op te nemen met de meta-richtlijn // @require https://update.greasyfork.org/scripts/584836/1863342/Anti-UI%20Purge%20Library.js

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==UserScript==
// @name         Anti-UI Purge Library
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Prevents specified DOM elements from being destroyed by page state changes
// @author       Gemini
// @grant        none
// ==/UserScript==

(function(win) {
    'use strict';

    const protectedIDs = new Set();
    const protectedElements = new Map();

    // ==========================================
    // 1. NATIVE METHOD HOOKING (Proactive Defense)
    // ==========================================
    
    // Hook removeChild to intercept direct removal attempts
    const originalRemoveChild = Node.prototype.removeChild;
    Node.prototype.removeChild = function(child) {
        if (child && child.id && protectedIDs.has(child.id)) {
            console.warn(`[Anti-Purge] Blocked attempt to remove protected element: #${child.id}`);
            return child; // Pretend it was removed successfully
        }
        return originalRemoveChild.apply(this, arguments);
    };

    // Hook replaceChild
    const originalReplaceChild = Node.prototype.replaceChild;
    Node.prototype.replaceChild = function(newChild, oldChild) {
        if (oldChild && oldChild.id && protectedIDs.has(oldChild.id)) {
            console.warn(`[Anti-Purge] Blocked attempt to replace protected element: #${oldChild.id}`);
            // Insert the new child, but keep the old protected child
            this.insertBefore(newChild, oldChild);
            return oldChild;
        }
        return originalReplaceChild.apply(this, arguments);
    };

    // Hook innerHTML to catch blanket wipes (e.g., document.body.innerHTML = '')
    const originalInnerHTML = Object.getOwnPropertyDescriptor(Element.prototype, 'innerHTML');
    Object.defineProperty(Element.prototype, 'innerHTML', {
        set: function(value) {
            // Find if any protected elements are currently inside this container
            const elementsToSave = [];
            protectedIDs.forEach(id => {
                const el = this.querySelector(`#${id}`);
                if (el) elementsToSave.push(el);
            });

            // Allow the wipe
            originalInnerHTML.set.call(this, value);

            // Re-append the protected elements immediately
            elementsToSave.forEach(el => {
                this.appendChild(el);
                console.warn(`[Anti-Purge] Restored #${el.id} after innerHTML wipe.`);
            });
        },
        get: originalInnerHTML.get
    });

    // ==========================================
    // 2. MUTATION OBSERVER (Reactive Fallback)
    // ==========================================
    
    const observer = new MutationObserver((mutations) => {
        mutations.forEach(mutation => {
            mutation.removedNodes.forEach(node => {
                if (node.nodeType === Node.ELEMENT_NODE) {
                    // Check if the node itself is protected
                    if (protectedIDs.has(node.id)) {
                        document.body.appendChild(protectedElements.get(node.id));
                        console.warn(`[Anti-Purge] Observer restored orphaned element: #${node.id}`);
                    } 
                    // Check if the removed node *contained* our protected element
                    else {
                        protectedIDs.forEach(id => {
                            if (node.querySelector(`#${id}`)) {
                                document.body.appendChild(protectedElements.get(id));
                                console.warn(`[Anti-Purge] Observer restored element #${id} from purged parent container.`);
                            }
                        });
                    }
                }
            });
        });
    });

    win.addEventListener('DOMContentLoaded', () => {
        observer.observe(document.body, { childList: true, subtree: true });
    });

    // ==========================================
    // 3. EXPORTED API
    // ==========================================
    
    win.AntiPurge = {
        /**
         * Registers an element to be protected from DOM deletion.
         * @param {HTMLElement} element - The DOM node to protect. Must have a unique ID.
         */
        protect: function(element) {
            if (!element.id) {
                console.error('[Anti-Purge] Element must have an ID to be protected.');
                return;
            }
            protectedIDs.add(element.id);
            protectedElements.set(element.id, element);
            console.log(`[Anti-Purge] Now protecting: #${element.id}`);
        },

        /**
         * Unregisters an element, allowing normal deletion.
         * @param {string} id - The ID of the element to release.
         */
        release: function(id) {
            protectedIDs.delete(id);
            protectedElements.delete(id);
            console.log(`[Anti-Purge] Released protection for: #${id}`);
        }
    };

})(typeof unsafeWindow !== 'undefined' ? unsafeWindow : window);