// ==UserScript== // @name Imgur.com → Imgur.lol // @namespace https://luke.gg/ // @version 1.3 // @description i.imgur.com -> imgur.lol // @author luke // @match *://*/* // @run-at document-start // @grant none // ==/UserScript== (function() { 'use strict'; const OLD_DOMAIN = /i\.(imgur\.com)/gi; const NEW_DOMAIN = 'imgur.lol'; /** ---- Stage 1: Early interception for preloaded elements ---- **/ // Override common URL-creating functions const origCreateElement = Document.prototype.createElement; Document.prototype.createElement = function(tag) { const el = origCreateElement.call(this, tag); const observer = new MutationObserver(() => rewriteAttributes(el)); observer.observe(el, { attributes: true }); return el; }; // Rewrite all URL-like attributes of an element function rewriteAttributes(node) { if (!node || !node.attributes) return; for (const attr of node.attributes) { if (typeof attr.value === 'string' && attr.value.includes('imgur.com')) { const newVal = attr.value.replace(OLD_DOMAIN, NEW_DOMAIN); if (newVal !== attr.value) node.setAttribute(attr.name, newVal); } } } /** ---- Stage 2: Text & Attribute replacement after DOM loads ---- **/ function rewriteAll(root=document.body) { if (!root) return; // Elements const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT); while (walker.nextNode()) rewriteAttributes(walker.currentNode); // Text nodes const textWalker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT); while (textWalker.nextNode()) { const node = textWalker.currentNode; if (node.nodeValue.includes('imgur.com')) { node.nodeValue = node.nodeValue.replace(OLD_DOMAIN, NEW_DOMAIN); } } } /** ---- Stage 3: Dynamic mutation observer ---- **/ const mo = new MutationObserver(muts => { for (const m of muts) { for (const node of m.addedNodes) { if (node.nodeType === 1) { // element rewriteAll(node); } else if (node.nodeType === 3) { // text if (node.nodeValue.includes('imgur.com')) { node.nodeValue = node.nodeValue.replace(OLD_DOMAIN, NEW_DOMAIN); } } } } }); /** ---- Stage 4: Apply once DOM is ready ---- **/ function init() { rewriteAll(); mo.observe(document.body, { childList: true, subtree: true }); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } /** ---- Stage 5: Intercept navigation clicks ---- **/ document.addEventListener('click', ev => { const a = ev.target.closest('a[href]'); if (a && a.href.includes('imgur.com')) { a.href = a.href.replace(OLD_DOMAIN, NEW_DOMAIN); } }, true); })();