Torn Chat Blocker

Aggressively blocks Torn Chat with a visual toggle switch and per-page always-block via long press.

// ==UserScript==
// @name         Torn Chat Blocker
// @namespace    https://greasyfork.org/en/users/1431907-theeeunknown
// @version      1.2
// @description  Aggressively blocks Torn Chat with a visual toggle switch and per-page always-block via long press.
// @author       TR0LL
// @license      MIT
// @match        https://www.torn.com/*
// @grant        GM_addStyle
// @grant        unsafeWindow
// @run-at       document-start
// ==/UserScript==
 
(function() {
    'use strict';
 
    // --- Configuration ---
    const DEBUG_MODE = false; // Set to true for detailed console logging, false for production
    const CHAT_URL_PATTERNS_TO_BLOCK = [
        // General chat patterns
        /https:\/\/www\.torn\.com\/chat/,
        /https:\/\/www\.torn\.com\/builds\/chat\//,
        /chat-worker\.js/,
        /fetch-worker\.js/,
        /https:\/\/www\.torn\.com\/js\/chat/,
 
        // Specific script files
        /https:\/\/www\.torn\.com\/builds\/chat\/app\.[a-f0-9]+\.js/,
        /https:\/\/www\.torn\.com\/builds\/chat\/vendors\.[a-f0-9]+\.js/,
        /https:\/\/www\.torn\.com\/builds\/chat\/runtime\.[a-f0-9]+\.js/,
 
        // Specific CSS files
        /https:\/\/www\.torn\.com\/builds\/chat\/app\.[a-f0-9]+\.css/,
        /https:\/\/www\.torn\.com\/builds\/chat\/vendors\.[a-f0-9]+\.css/,
 
        // Ultra-aggressive chat asset blocking
        /torn\.com\/builds\/chat\/.*\.js/,
        /torn\.com\/builds\/chat\/.*\.css/,
        /torn\.com\/builds\/chat\/.*\.json/,
        /torn\.com\/builds\/chat\/.*\.png/,
        /torn\.com\/builds\/chat\/.*\.jpg/,
        /torn\.com\/builds\/chat\/.*\.svg/,
        /torn\.com\/builds\/chat\/.*\.woff/,
        /torn\.com\/builds\/chat\/.*\.mp3/,
 
        // Sendbird related patterns
        /sendbird\.(com|io)/,
        /api\.sendbird\.com/,
        /ws\.sendbird\.com/,
        /sb\.scorpion\.io/,
        /^\wss?:\/\/.*sendbird/,
        /.*\.sendbird\..*/i,
 
        // Broader chat patterns
        /torn\.com\/.*chat/i,
        /torn\.com\/.*sendbird/i,
 
        // Added rule for profile-mini (Example, adjust as needed)
        /https:\/\/www\.torn\.com\/builds\/profile-mini\//
    ];
 
    const TOGGLE_BUTTON_ID = 'torn-chat-blocker-toggle';
    const LOCAL_STORAGE_KEY_GLOBAL_BLOCK = 'tornChatBlockingEnabled';
    const LOCAL_STORAGE_KEY_ALWAYS_BLOCK_PAGES = 'tornChatAlwaysBlockedPages';
    const DEBOUNCE_DELAY = 250;
    const LONG_PRESS_DURATION = 750; // ms
 
    // --- Global State ---
    let isBlockingEnabled = localStorage.getItem(LOCAL_STORAGE_KEY_GLOBAL_BLOCK) !== 'false'; // Defaults to true
    let alwaysBlockedPages = new Set(JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEY_ALWAYS_BLOCK_PAGES) || '[]'));
    let domObserver = null;
    let longPressTimer = null;
    let isLongPressFlag = false; // Distinguish long press from click
 
    // --- Logging Utility ---
    function log(...args) {
        if (DEBUG_MODE) {
            console.log('Torn Chat Blocker:', ...args);
        }
    }
    function warn(...args) {
        if (DEBUG_MODE) {
            console.warn('Torn Chat Blocker:', ...args);
        }
    }
 
    // --- CSS for the toggle button (Switch Style) ---
    GM_addStyle(`
        #${TOGGLE_BUTTON_ID} {
            position: fixed;
            top: 10px;
            right: 10px;
            z-index: 9999;
            width: 50px;
            height: 26px;
            border-radius: 13px;
            cursor: pointer;
            transition: background-color 0.3s ease, border-color 0.3s ease;
            box-shadow: 0 1px 3px rgba(0,0,0,0.1), inset 0 1px 1px rgba(0,0,0,0.1);
            box-sizing: border-box;
            -webkit-appearance: none;
            -moz-appearance: none;
            appearance: none;
            padding: 0;
            font-size: 0;
            line-height: 0;
            color: transparent;
            /* Default background/border will be set by specific classes */
        }
 
        #${TOGGLE_BUTTON_ID}::after { /* The slider knob */
            content: "";
            position: absolute;
            top: 2px;
            left: 2px;
            width: 20px;
            height: 20px;
            background-color: white;
            border-radius: 50%;
            box-shadow: 0 1px 3px rgba(0,0,0,0.4);
            transition: transform 0.3s ease;
            box-sizing: border-box;
        }
 
        /* State when global blocking is ON (GREEN) */
        #${TOGGLE_BUTTON_ID}.blocking-on {
            background-color: #388e3c; /* Green */
            border: 1px solid #2e7d32; /* Darker green */
        }
        #${TOGGLE_BUTTON_ID}.blocking-on::after {
            transform: translateX(24px); /* Knob right */
        }
 
        /* State when global blocking is OFF (RED) */
        #${TOGGLE_BUTTON_ID}.blocking-off {
            background-color: #d32f2f; /* Red */
            border: 1px solid #c62828; /* Darker red */
        }
        #${TOGGLE_BUTTON_ID}.blocking-off::after {
            transform: translateX(0); /* Knob left */
        }
 
        /* State when current page is ALWAYS BLOCKED (ORANGE) */
        #${TOGGLE_BUTTON_ID}.blocking-always {
            background-color: #FFA500; /* Orange */
            border: 1px solid #E69500; /* Darker orange */
        }
        #${TOGGLE_BUTTON_ID}.blocking-always::after {
            transform: translateX(24px); /* Knob right (appears "ON") */
        }
 
        #${TOGGLE_BUTTON_ID}:hover {
             filter: brightness(1.1);
        }
    `);
 
    // --- Core Blocking Logic ---
    function isCurrentPageAlwaysBlocked() {
        return alwaysBlockedPages.has(window.location.href);
    }
 
    // Determines if chat/sendbird assets should be blocked for the given requestUrl
    function shouldBlockUrl(requestUrl) {
        const pageIsAlwaysBlocked = isCurrentPageAlwaysBlocked();
        const effectiveBlockingActive = isBlockingEnabled || pageIsAlwaysBlocked;
 
        if (!effectiveBlockingActive) {
            return false; // Neither global nor page-specific override says block
        }
 
        // If blocking is active (globally or for this page), check patterns
        if (requestUrl && typeof requestUrl === 'string') {
            for (const pattern of CHAT_URL_PATTERNS_TO_BLOCK) {
                if (pattern.test(requestUrl)) {
                    log(`Blocking request to ${requestUrl} by pattern ${pattern} (Global: ${isBlockingEnabled}, PageAlwaysBlocked: ${pageIsAlwaysBlocked})`);
                    return true;
                }
            }
            // Fallback keyword check
            if (requestUrl.includes('chat') || requestUrl.includes('sendbird')) {
                if (!CHAT_URL_PATTERNS_TO_BLOCK.some(p => p.test(requestUrl))) { // Log only if not caught by a specific pattern
                    log(`Blocking request to ${requestUrl} by keyword fallback (Global: ${isBlockingEnabled}, PageAlwaysBlocked: ${pageIsAlwaysBlocked})`);
                }
                return true;
            }
        }
        return false;
    }
 
    // Determines if DOM elements related to chat should be hidden on the current page
    function pageShouldHaveElementsHidden() {
        return isBlockingEnabled || isCurrentPageAlwaysBlocked();
    }
 
    // --- Request Interception ---
    const originalFetch = unsafeWindow.fetch;
    const originalXHRopen = unsafeWindow.XMLHttpRequest.prototype.open;
    const originalWebSocket = unsafeWindow.WebSocket;
 
    unsafeWindow.fetch = function(...args) {
        const requestInfo = args[0];
        const url = (typeof requestInfo === 'string') ? requestInfo : requestInfo.url;
        if (shouldBlockUrl(url)) {
            log(`Blocking fetch request to ${url}`);
            return Promise.reject(new Error(`Torn Chat Blocker: Request to ${url} blocked`));
        }
        return originalFetch.apply(unsafeWindow, args);
    };
 
    unsafeWindow.XMLHttpRequest.prototype.open = function(...args) {
        const method = args[0];
        const url = args[1];
        if (shouldBlockUrl(url)) {
            log(`Preparing to block XHR ${method} request to ${url}`);
            this._blockedUrl = url;
            this._isBlockedByScript = true;
        } else {
            this._isBlockedByScript = false;
        }
        return originalXHRopen.apply(this, args);
    };
 
    const originalXHRSend = unsafeWindow.XMLHttpRequest.prototype.send;
    unsafeWindow.XMLHttpRequest.prototype.send = function(...args) {
        if (this._isBlockedByScript && this._blockedUrl) {
            log(`Preventing XHR send for ${this._blockedUrl}`);
            const xhrInstance = this;
            setTimeout(() => {
                const errorEvent = new ProgressEvent('error');
                if (typeof xhrInstance.onerror === 'function') xhrInstance.onerror(errorEvent);
                if (typeof xhrInstance.onloadend === 'function') xhrInstance.onloadend(errorEvent);
                try {
                    xhrInstance.dispatchEvent(new Event('error'));
                    xhrInstance.dispatchEvent(new Event('loadend'));
                } catch (e) {
                    warn('Error dispatching events on blocked XHR:', e);
                }
            }, 10);
            return;
        }
        return originalXHRSend.apply(this, args);
    };
 
    unsafeWindow.WebSocket = function(url, protocols) {
        if (shouldBlockUrl(url)) {
            log('Blocking WebSocket connection to', url);
            const fakeWS = {
                url: url, protocol: protocols && protocols.length > 0 ? protocols[0] : '',
                readyState: 3, bufferedAmount: 0, extensions: '', binaryType: 'blob',
                CONNECTING: 0, OPEN: 1, CLOSING: 2, CLOSED: 3,
                send: function() { log('Fake WebSocket: send called on blocked WS'); return false; },
                close: function(code, reason) {
                    log('Fake WebSocket: close called on blocked WS', code, reason);
                    this.readyState = this.CLOSING;
                    setTimeout(() => {
                        this.readyState = this.CLOSED;
                        const closeEvent = new CloseEvent('close', { code: code || 1006, reason: reason || "Connection blocked", wasClean: false });
                        if (typeof this.onclose === 'function') this.onclose(closeEvent);
                        try { this.dispatchEvent(closeEvent); } catch(e) {warn('Error dispatching close on fake WS', e);}
                    }, 0);
                },
                onopen: null, onclose: null, onerror: null, onmessage: null,
                _listeners: {},
                addEventListener: function(type, listener) {
                    if (!this._listeners[type]) this._listeners[type] = [];
                    this._listeners[type].push(listener);
                },
                removeEventListener: function(type, listener) {
                    if (!this._listeners[type]) return;
                    this._listeners[type] = this._listeners[type].filter(l => l !== listener);
                },
                dispatchEvent: function(event) {
                    if (!this._listeners[event.type]) return true;
                    this._listeners[event.type].forEach(cb => (typeof cb === 'function' ? cb.call(this, event) : cb.handleEvent(event)));
                    return !event.defaultPrevented;
                }
            };
            setTimeout(() => {
                const errorEvent = new Event('error');
                if (typeof fakeWS.onerror === 'function') fakeWS.onerror(errorEvent);
                try { fakeWS.dispatchEvent(errorEvent); } catch(e) {warn('Error dispatching error on fake WS', e);}
                const closeEvent = new CloseEvent('close', { code: 1006, reason: "WebSocket blocked by script", wasClean: false });
                if (typeof fakeWS.onclose === 'function') fakeWS.onclose(closeEvent);
                try { fakeWS.dispatchEvent(closeEvent); } catch(e) {warn('Error dispatching close on fake WS', e);}
                fakeWS.readyState = fakeWS.CLOSED;
            }, 5);
            return fakeWS;
        }
        const wsInstance = new originalWebSocket(url, protocols);
        return wsInstance;
    };
    // Ensure prototype and static constants are correctly set up if needed by Torn's code
    if (originalWebSocket) {
        unsafeWindow.WebSocket.prototype = originalWebSocket.prototype;
        unsafeWindow.WebSocket.CONNECTING = originalWebSocket.CONNECTING;
        unsafeWindow.WebSocket.OPEN = originalWebSocket.OPEN;
        unsafeWindow.WebSocket.CLOSING = originalWebSocket.CLOSING;
        unsafeWindow.WebSocket.CLOSED = originalWebSocket.CLOSED;
    }
 
 
    // --- UI Toggle Button ---
    function updateToggleButton() {
        const button = document.getElementById(TOGGLE_BUTTON_ID);
        if (!button) return;
 
        button.classList.remove('blocking-on', 'blocking-off', 'blocking-always');
        let ariaLabel = '';
 
        if (isCurrentPageAlwaysBlocked()) {
            button.classList.add('blocking-always'); // Orange
            ariaLabel = 'Page Always Blocked. Click to remove from always-block list.';
            button.setAttribute('aria-checked', 'true'); // Visually "on"
        } else if (isBlockingEnabled) {
            button.classList.add('blocking-on'); // Green
            ariaLabel = 'Global Chat Blocking: ON. Click to turn OFF. Long press to always-block this page.';
            button.setAttribute('aria-checked', 'true');
        } else {
            button.classList.add('blocking-off'); // Red
            ariaLabel = 'Global Chat Blocking: OFF. Click to turn ON. Long press to always-block this page.';
            button.setAttribute('aria-checked', 'false');
        }
        button.setAttribute('aria-label', ariaLabel);
        button.setAttribute('role', 'switch');
    }
 
    function showNotification(message, backgroundColor) {
        const notification = document.createElement('div');
        notification.textContent = message;
        Object.assign(notification.style, {
            position: 'fixed', bottom: '20px', left: '50%', transform: 'translateX(-50%)',
            backgroundColor: backgroundColor, color: 'white',
            padding: '10px 20px', borderRadius: '5px', zIndex: '10000',
            boxShadow: '0 2px 10px rgba(0,0,0,0.2)'
        });
        document.body.appendChild(notification);
        setTimeout(() => notification.remove(), 3500);
    }
 
    function addToggleButton() {
        if (document.getElementById(TOGGLE_BUTTON_ID)) return;
        const button = document.createElement('button');
        button.id = TOGGLE_BUTTON_ID;
 
        const handleLongPress = () => {
            isLongPressFlag = true; // Set flag
            const currentPageUrl = window.location.href;
            if (!alwaysBlockedPages.has(currentPageUrl)) {
                alwaysBlockedPages.add(currentPageUrl);
                localStorage.setItem(LOCAL_STORAGE_KEY_ALWAYS_BLOCK_PAGES, JSON.stringify(Array.from(alwaysBlockedPages)));
                log(`Long press: Added ${currentPageUrl} to always-block list.`);
                showNotification(`Page added to always-block list. Refresh for full effect.`, '#FFA500'); // Orange notification
                updateToggleButton();
                if (pageShouldHaveElementsHidden()) { // Check if blocking should now be active
                    blockChatElementsInDOM(); // Ensure DOM observer is active
                }
            } else {
                log(`Long press: ${currentPageUrl} is already always-blocked.`);
                // Optional: showNotification(`${currentPageUrl} is already always-blocked.`, '#FFA500');
            }
        };
 
        const clearLongPressTimer = () => {
            if (longPressTimer) clearTimeout(longPressTimer);
            longPressTimer = null;
        };
 
        // Mouse events
        button.addEventListener('mousedown', (e) => {
            if (e.button !== 0) return; // Only left click
            isLongPressFlag = false; // Reset flag
            clearLongPressTimer();
            longPressTimer = setTimeout(handleLongPress, LONG_PRESS_DURATION);
        });
        button.addEventListener('mouseup', (e) => {
            if (e.button !== 0) return;
            clearLongPressTimer();
            // Click event will handle logic if not a long press
        });
        button.addEventListener('mouseleave', clearLongPressTimer);
 
        // Touch events
        button.addEventListener('touchstart', (e) => {
            isLongPressFlag = false; // Reset flag
            clearLongPressTimer();
            longPressTimer = setTimeout(handleLongPress, LONG_PRESS_DURATION);
            // e.preventDefault(); // Could prevent scroll, be cautious
        }, { passive: true }); // Passive if not preventing default
 
        button.addEventListener('touchend', (e) => {
            clearLongPressTimer();
            if (isLongPressFlag) {
                e.preventDefault(); // Prevent click event firing after a long touch
            }
            // Click event will handle logic if not a long press
        });
        button.addEventListener('touchcancel', clearLongPressTimer);
 
 
        button.addEventListener('click', (e) => {
            if (isLongPressFlag) { // If flag is set, it was a long press; reset and ignore click
                isLongPressFlag = false;
                e.stopImmediatePropagation(); // Prevent other click listeners if any
                return;
            }
 
            const currentPageUrl = window.location.href;
            if (isCurrentPageAlwaysBlocked()) {
                // Click on ORANGE switch: Remove from always-block
                alwaysBlockedPages.delete(currentPageUrl);
                localStorage.setItem(LOCAL_STORAGE_KEY_ALWAYS_BLOCK_PAGES, JSON.stringify(Array.from(alwaysBlockedPages)));
                log(`Clicked to unblock always-blocked page: ${currentPageUrl}`);
                showNotification(`Page removed from always-block list. Refresh for full effect.`, isBlockingEnabled ? '#388e3c' : '#d32f2f');
                updateToggleButton();
                if (!pageShouldHaveElementsHidden() && domObserver) {
                    domObserver.disconnect();
                    log('DOM Observer disconnected as page is no longer effectively blocked.');
                }
            } else {
                // Click on GREEN/RED switch: Toggle global blocking
                isBlockingEnabled = !isBlockingEnabled;
                localStorage.setItem(LOCAL_STORAGE_KEY_GLOBAL_BLOCK, isBlockingEnabled.toString());
                log(`Toggled Global Resource Blocking: ${isBlockingEnabled ? 'ON' : 'OFF'}`);
                showNotification(`Global Resource Blocking ${isBlockingEnabled ? 'enabled' : 'disabled'}. Refresh for full effect.`, isBlockingEnabled ? '#388e3c' : '#d32f2f');
                updateToggleButton();
 
                if (pageShouldHaveElementsHidden()) {
                    blockChatElementsInDOM();
                } else {
                    if (domObserver) domObserver.disconnect();
                    log("Global Resource Blocking OFF and page not always-blocked. DOM Observer potentially stopped. Refresh to restore elements.");
                }
            }
        });
 
        if (document.body) {
             document.body.appendChild(button);
        } else {
            window.addEventListener('DOMContentLoaded', () => {
                if (document.body) document.body.appendChild(button);
            });
        }
        updateToggleButton(); // Initialize button state
    }
 
    // --- Debounce Utility ---
    function debounce(func, wait) {
        let timeout;
        return function executedFunction(...args) {
            const later = () => {
                clearTimeout(timeout);
                func(...args);
            };
            clearTimeout(timeout);
            timeout = setTimeout(later, wait);
        };
    }
 
    // --- Block elements in DOM (Primarily for chat UI) ---
    const chatSelectors = [
        'div[id*="chat"]', 'div[class*="chat"]', 'div[id*="sendbird"]', 'div[class*="sendbird"]',
        'iframe[src*="chat"]', 'iframe[src*="sendbird"]', '#chatRoot', '.chat-box',
        '.chat-container', '.chat-wrapper', '*[id*="chat-"]', '*[class*="chat-"]',
        '*[id*="-chat"]', '*[class*="-chat"]', '*[id*="sendbird-"]', '*[class*="sendbird-"]',
        'div[aria-label*="chat" i]', 'section[aria-label*="chat" i]'
    ];
 
    function hideMatchedElements() {
        if (!pageShouldHaveElementsHidden()) {
             log('DOM element hiding is OFF for this page. Previously hidden elements will remain hidden until refresh.');
             // We don't attempt to unhide, refresh is cleaner.
            return;
        }
        log('Scanning and hiding chat-related DOM elements...');
        chatSelectors.forEach(selector => {
            try {
                document.querySelectorAll(selector).forEach(el => {
                    if (el.id === TOGGLE_BUTTON_ID || el.closest(`#${TOGGLE_BUTTON_ID}`)) return;
                    if (el.style.display !== 'none') {
                        log('Hiding DOM element matching selector:', selector, el);
                        el.style.setProperty('display', 'none', 'important');
                        el.style.setProperty('visibility', 'hidden', 'important');
                        el.style.setProperty('opacity', '0', 'important');
                        el.style.setProperty('pointer-events', 'none', 'important');
                        el.dataset.tornChatBlocked = 'true';
                    }
                });
            } catch (e) {
                warn('Error applying selector:', selector, e.message);
            }
        });
    }
 
    const debouncedHideMatchedElements = debounce(hideMatchedElements, DEBOUNCE_DELAY);
 
    function blockChatElementsInDOM() {
        if (!pageShouldHaveElementsHidden()) {
            if (domObserver) {
                domObserver.disconnect();
                log('DOM Observer disconnected as blocking is not active for this page.');
            }
            return;
        }
        log('Actively scanning/hiding chat DOM elements. Ensuring DOM observer is running.');
        hideMatchedElements(); // Initial scan
 
        if (!domObserver || !domObserver.takeRecords().length) { // Check if observer exists and is active
            domObserver = new MutationObserver((mutations) => {
                if (!pageShouldHaveElementsHidden()) { // Re-check condition within observer callback
                    if (domObserver) domObserver.disconnect();
                    log('DOM Observer disconnected from within callback.');
                    return;
                }
                let needsRescan = false;
                for (const mutation of mutations) {
                    if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                        for (const node of mutation.addedNodes) {
                            if (node.nodeType === Node.ELEMENT_NODE) {
                                if (node.id === TOGGLE_BUTTON_ID) continue;
                                if (chatSelectors.some(sel => node.matches && node.matches(sel)) ||
                                    (node.querySelector && node.querySelector(chatSelectors.join(',')))) {
                                    needsRescan = true; break;
                                }
                            }
                        }
                    } else if (mutation.type === 'attributes') {
                        if (mutation.target.nodeType === Node.ELEMENT_NODE &&
                            chatSelectors.some(sel => mutation.target.matches && mutation.target.matches(sel))) {
                            needsRescan = true;
                        }
                    }
                    if (needsRescan) break;
                }
                if (needsRescan) {
                    log('DOM mutation detected, queueing re-hide for chat elements.');
                    debouncedHideMatchedElements();
                }
            });
 
            const observeTarget = document.body || document.documentElement;
            if (observeTarget) {
                 domObserver.observe(observeTarget, { childList: true, subtree: true, attributes: true });
                 log('DOM Observer started for chat elements.');
            } else { // Fallback if body not ready, though @run-at document-start + DOMContentLoaded should handle most
                 window.addEventListener('DOMContentLoaded', () => {
                    const target = document.body || document.documentElement;
                    if (target && pageShouldHaveElementsHidden()) { // Check again before observing
                        domObserver.observe(target, { childList: true, subtree: true, attributes: true });
                        log('DOM Observer started after DOMContentLoaded for chat elements.');
                    } else if (!target) {
                        warn("Failed to start DOM Observer: No body or documentElement found post-DOMContentLoaded.");
                    }
                 });
            }
        } else {
            log('DOM Observer already running.');
        }
    }
 
    // --- Initialization ---
    function initialize() {
        log('Initializing Torn Chat Blocker...');
        addToggleButton(); // This will also call updateToggleButton
 
        if (pageShouldHaveElementsHidden()) {
            log('Initial state requires blocking. Activating DOM blocking.');
            blockChatElementsInDOM();
        } else {
            log('Initial state does not require blocking.');
        }
 
        // Attempt to nullify global chat-related variables
        // This runs slightly after script start to catch variables defined by Torn's early scripts
        setTimeout(() => {
            if (pageShouldHaveElementsHidden()) { // Only nuke if blocking is active
                try {
                    const targetVars = ['chat', 'Chat', 'SendBird', 'sendbird', 'sb', '_sendbird', 'SENDBIRD'];
                    targetVars.forEach(v => {
                        if (typeof unsafeWindow[v] !== 'undefined' && unsafeWindow[v] !== null) { // Check if not already null
                            log(`Attempting to nullify unsafeWindow.${v}`);
                            try {
                                Object.defineProperty(unsafeWindow, v, { value: null, writable: false, configurable: false });
                            } catch (e) {
                                warn(`Failed to Object.defineProperty ${v}, falling back to simple null. Error: ${e.message}`);
                                try {
                                    unsafeWindow[v] = null;
                                } catch (e2) {
                                    warn(`Failed to even assign null to unsafeWindow.${v}. Error: ${e2.message}`);
                                }
                            }
                        }
                    });
                } catch (e) {
                    warn('Error while trying to nuke JS variables.', e.message);
                }
            }
        }, 200); // Increased delay slightly
    }
 
    if (document.readyState === 'loading') {
        window.addEventListener('DOMContentLoaded', initialize);
    } else {
        initialize();
    }
 
})();