XCancel.com to X.com Redirector

Replaces XCancel.com links with x.com

От 08.04.2025. Виж последната версия.

// ==UserScript==
// @name         XCancel.com to X.com Redirector
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Replaces XCancel.com links with x.com
// @author       Cucco (Modified)
// @match        *://*/*
// @grant        none
// @run-at       document-start
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    // Function to replace all x.com links in DOM elements
    function replaceXCancelLinks() {
        // Replace href attributes in anchor tags
        const links = document.querySelectorAll('a[href*="x.com"]');
        links.forEach(link => {
            link.href = link.href.replace(/xcancel\.com/g, 'x.com');
        });

        // Replace x.com in text content (optional)
        const textNodes = [];
        const walker = document.createTreeWalker(
            document.body,
            NodeFilter.SHOW_TEXT,
            null,
            false
        );

        let node;
        while (node = walker.nextNode()) {
            // Check text nodes in the body, avoiding script and style tags
            const parentTag = node.parentElement?.tagName?.toLowerCase();
            if (parentTag !== 'script' && parentTag !== 'style' && node.nodeValue.includes('x.com')) {
                textNodes.push(node);
            }
        }

        textNodes.forEach(node => {
            node.nodeValue = node.nodeValue.replace(/xcancel\.com/g, 'x.com');
        });
    }

    // Run when DOM is loaded initially
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', replaceXCancelLinks);
    } else {
        // DOM is already loaded
        replaceXCancelLinks();
    }


    // Use MutationObserver for better performance and reliability than setInterval
    const observer = new MutationObserver((mutationsList) => {
        for(const mutation of mutationsList) {
            if (mutation.type === 'childList' || mutation.type === 'characterData') {
                // Check added nodes or changed text content
                 replaceXCancelLinks();
                 // Optimization: Disconnect briefly if not expecting frequent updates,
                 // or scope the replacement logic more narrowly based on mutation targets.
                 break; // No need to re-run for every mutation in the batch
            }
        }
    });

    // Start observing the document body for configured mutations
    observer.observe(document.body || document.documentElement, { childList: true, subtree: true, characterData: true });


    // Intercept and modify URL before navigation via XMLHttpRequest
    const originalOpen = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function() {
        if (arguments[1] && typeof arguments[1] === 'string') {
            arguments[1] = arguments[1].replace(/xcancel\.com/g, 'x.com');
        }
        return originalOpen.apply(this, arguments);
    };

    // Intercept fetch requests
    const originalFetch = window.fetch;
    window.fetch = function() {
        let url = arguments[0];
        let options = arguments[1];

        if (url && typeof url === 'string') {
            url = url.replace(/xcancel\.com/g, 'x.com');
            arguments[0] = url;
        } else if (url && url instanceof Request) {
             // Create a new Request object with the modified URL
             const newUrl = url.url.replace(/xcancel\.com/g, 'x.com');
             // Clone the request to modify it, as Request objects are immutable
             arguments[0] = new Request(newUrl, {
                 ...url // Spread operator might not copy all properties perfectly, depending on browser
                 // Manually copy necessary properties if needed:
                 // method: url.method,
                 // headers: url.headers,
                 // body: url.body,
                 // mode: url.mode,
                 // credentials: url.credentials,
                 // cache: url.cache,
                 // redirect: url.redirect,
                 // referrer: url.referrer,
                 // integrity: url.integrity
             });
        }
        return originalFetch.apply(this, arguments);
    };

    // Ensure the script stops observing when the page unloads
     window.addEventListener('unload', () => {
        if (observer) {
            observer.disconnect();
        }
     });

})();