Created
November 17, 2023 08:32
-
-
Save jgottula/7829b077660d3c57e2d230dab55cd7e5 to your computer and use it in GitHub Desktop.
Make the Reddit web experience on iPhone/iPad less miserable (RIP Apollo, fuck /u/spez)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// STOP FUCKING WITH MY REDDIT PAGES FROM SEARCH RESULTS! | |
// (remove referer from reddit links on google) | |
// for use with custom JS injection feature of StopTheMadness MobileSafari extension | |
'use strict'; | |
const DEBUG = (str) => { console.debug(str); }; | |
// generate a string of the path to the given element | |
// from the document root, in CSS selector form | |
const elem_path = (elem, arr = []) => { | |
const first = (arr.length == 0); | |
// REMOVE ME REMOVE ME REMOVE ME | |
// TypeError: undefined is not an object (evaluating 'elem.tagName.toLowerCase') | |
if (elem === undefined) DEBUG(`elem_path[${arr.length}]: elem undefined`); | |
if (elem.tagName === undefined) DEBUG(`elem_path[${arr.length}]: elem.tagName undefined`); | |
if (elem.tagName.toLowerCase === undefined) DEBUG(`elem_path[${arr.length}]: elem.tagName.toLowerCase undefined`); | |
// REMOVE ME REMOVE ME REMOVE ME | |
if (elem.id !== '') { | |
arr.push(`${elem.tagName.toLowerCase()}#${elem.id}`); | |
} else { | |
arr.push(elem.tagName.toLowerCase()); | |
} | |
let str; | |
if (elem.parentElement !== null) { | |
str = elem_path(elem.parentElement, arr); | |
} else { | |
arr.reverse(); | |
str = arr.join(' > '); | |
} | |
if (first) console.assert(elem.matches(str), elem); | |
return str; | |
}; | |
// given an <a> element: | |
// - check if its href goes to reddit.com | |
// - set referrerpolicy: no-referrer | |
// - add to rel: noreferrer | |
const fixup = (elem) => { | |
console.assert(elem.tagName === 'A', elem.tagName); | |
if (elem.tagName !== 'A') return; | |
let url; | |
try { | |
url = new URL(elem.href); | |
} catch (e) { | |
DEBUG(`fixup('${elem.href}'): INVALID`); | |
return; | |
} | |
if (!url.hostname.match(/^(.*\.)?reddit\.com$/i)) { | |
DEBUG(`fixup('${elem.href}'): SKIP`); | |
return; | |
} else { | |
DEBUG(`fixup('${elem.href}'): MATCH`); | |
} | |
// attribute: referrerpolicy | |
const rp1 = elem.referrerPolicy; | |
// NOTE: only overwrite value if necessary! | |
// (otherwise we may infinitely loop mutations) | |
if (elem.referrerPolicy !== 'no-referrer') { | |
elem.referrerPolicy = 'no-referrer'; | |
} | |
const rp2 = elem.referrerPolicy; | |
if (rp1 !== rp2) DEBUG(`fixup('${elem.href}'): referrerPolicy: '${rp1}' --> '${rp2}'`); | |
// attribute: rel | |
const r1 = elem.rel; const rl1 = elem.relList.value; | |
if (!elem.relList.contains('noreferrer')) { | |
elem.relList.add('noreferrer'); | |
} | |
const r2 = elem.rel; const rl2 = elem.relList.value; | |
if (r1 !== r2) DEBUG(`fixup('${elem.href}'): rel: '${r1}' --> '${r2}'`); | |
if (rl1 !== rl2) DEBUG(`fixup('${elem.href}'): relList: [${rl1}] --> [${rl2}]`); | |
}; | |
// do a one-time scan of ALL <a> elements in the document | |
const scan_all = () => { | |
for (const elem of document.getElementsByTagName('a')) { | |
fixup(elem); | |
} | |
}; | |
// ongoing monitoring | |
const monitor = () => { | |
const observer = new MutationObserver((records, observer) => { | |
const elems = new Set(); | |
DEBUG(`monitor: got ${records.length} records`); | |
for (const rec of records) { | |
DEBUG(`monitor: got '${rec.type}' record for: ${elem_path(rec.target)}`); | |
for (const elem of rec.addedNodes) { // childList | |
DEBUG(`monitor: added: ${elem_path(elem)} [href: ${elem.href}]`); | |
if (elem.tagName === 'a') elems.add(elem); | |
for (const child of elem.getElementsByTagName('a')) { | |
DEBUG(`monitor: added: child: ${elem_path(child)} [href: ${child.href}]`); | |
elems.add(child); | |
} | |
} | |
for (const elem of rec.removedNodes) { // childList | |
// TODO | |
// target | |
// previousSibling | |
// nextSibling | |
} | |
if (rec.attributeName !== null) { // attributes | |
DEBUG(`monitor: attr(${rec.attributeName}): ${elem_path(rec.target)} [href: ${rec.target.href}]`); | |
elems.add(rec.target); | |
// target | |
// attributeName | |
// attributeNamespace | |
// oldValue [maybe] | |
} | |
} | |
DEBUG(`monitor: collected ${elems.size} elements`); | |
for (const elem of elems) { | |
fixup(elem); | |
} | |
}); | |
const options = { | |
subtree: true, | |
childList: true, | |
attributes: true, | |
attributeFilter: ['href', 'rel', 'referrerpolicy'], | |
attributeOldValue: false, // maybe? | |
characterData: false, | |
characterOldData: false, | |
}; | |
// html > body > div#main | |
observer.observe(document.getElementById('main'), options); | |
}; | |
// setup not-quite-initial scan | |
document.addEventListener('DOMContentLoaded', (event) => { | |
DEBUG(`==== DOMContentLoaded ====`); | |
scan_all(); | |
}); | |
// setup ongoing monitoring | |
monitor(); | |
// initial scan | |
DEBUG(`==== INITIAL ====`); | |
scan_all(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment