Skip to content

Instantly share code, notes, and snippets.

@itslukej
Created October 13, 2025 01:00
Show Gist options
  • Select an option

  • Save itslukej/12d326dd9c87b081ff96d36da1c7a520 to your computer and use it in GitHub Desktop.

Select an option

Save itslukej/12d326dd9c87b081ff96d36da1c7a520 to your computer and use it in GitHub Desktop.
// ==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);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment