Last active
May 22, 2025 06:54
-
-
Save shtrom/e9c25e103aaf2f8ede6f740787b50e33 to your computer and use it in GitHub Desktop.
Github BMO Linker GreaseMonkey script
This file contains hidden or 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
// ==UserScript== | |
// @name GitHub BMO Linker | |
// @version 3 | |
// @author Olivier Mehani <[email protected]> | |
// @downloadURL https://gist.github.com/shtrom/e9c25e103aaf2f8ede6f740787b50e33 | |
// @grant none | |
// @match https://github.com/* | |
// @run-at document-idle | |
// ==/UserScript== | |
const baseUrl = "https://bugzilla.mozilla.org/show_bug.cgi?id="; | |
const bugRE = /bug ([0-9]+)/i; | |
const logName = "GreaseMonkey GBL"; | |
let debugLog = function() {} ; | |
//debugLog = console.debug; | |
let count = 0; | |
const skippableTags = [ | |
//"A", | |
"IMG", | |
"INPUT", | |
"OBJECT", | |
"PRE" | |
]; | |
function linkifyBugs(s) { | |
return s.replace(bugRE, '<a href="' + baseUrl + '$1">$&</a>'); | |
} | |
function bugUrl(s) { | |
return s.replace(bugRE, baseUrl + '$1'); | |
} | |
function recursiveLinkifyBugs(el) { | |
if (el.childNodes === undefined) { | |
//debugLog(logName + ": no child nodes in", el); | |
return; | |
} | |
let i = el.childNodes.length; | |
while (n = el.childNodes[--i]) { | |
//debugLog(logName + ": processing node", i, n, "from", el); | |
if (n.nodeType == n.TEXT_NODE) { | |
// https://stackoverflow.com/a/12212700 | |
while (r = bugRE.exec(n.textContent)) { | |
debugLog(logName + ": found bug string in", n, "from", el); | |
n = n.splitText(r.index); | |
n = n.splitText(r[0].length); | |
linkTextNode = n.previousSibling; | |
linkNode = document.createElement('a'); | |
linkNode.href = bugUrl(linkTextNode.textContent); | |
linkNode.appendChild(linkTextNode); | |
debugLog(logName + ": replacement", linkNode); | |
el.insertBefore(linkNode, n); | |
} | |
} else if (n.nodeType == n.ELEMENT_NODE | |
&& !skippableTags.includes(n.tagName)) { | |
//debugLog(logName + ": going deeper into", n); | |
recursiveLinkifyBugs(n); | |
} | |
} | |
//debugLog(logName + ": finished for", el); | |
} | |
function callback (mutations) { | |
me = logName + "[" + (++count) + "]"; | |
//debugLog(me + ": mutations detected", mutations); | |
let addedNodes = []; | |
for (let m of mutations) { | |
//debugLog(me + ": mutation", m); | |
for (let n of m.addedNodes) { | |
if (n.innerHTML?.length && !skippableTags.includes(n.tagName)) { | |
//debugLog(me + ": added node", n); | |
addedNodes.push(n); | |
} | |
} | |
} | |
if (addedNodes.length > 0) { | |
//debugLog(me + ": linkifying new content from", addedNodes); | |
recursiveLinkifyBugs(addedNodes); | |
} | |
} | |
let observer = new MutationObserver(callback); | |
let observerOptions = { | |
childList: true, | |
subtree: true | |
}; | |
recursiveLinkifyBugs(document.body); | |
observer.observe(document.body, observerOptions); | |
console.info(logName + ": ready"); | |
return s.replace(bugRE, '<a href="' + baseUrl + '$1">$&</a>'); | |
} | |
function bugUrl(s) { | |
return s.replace(bugRE, baseUrl + '$1'); | |
} | |
function recursiveLinkifyBugs(el) { | |
if (el.childNodes === undefined) { | |
//debugLog(logName + ": no child nodes in", el); | |
return; | |
} | |
let i = el.childNodes.length; | |
while (n = el.childNodes[--i]) { | |
//debugLog(logName + ": processing node", i, n, "from", el); | |
if (n.nodeType == n.TEXT_NODE) { | |
// https://stackoverflow.com/a/12212700 | |
while (r = bugRE.exec(n.textContent)) { | |
debugLog(logName + ": found bug string in", n, "from", el); | |
n = n.splitText(r.index); | |
n = n.splitText(r[0].length); | |
linkTextNode = n.previousSibling; | |
linkNode = document.createElement('a'); | |
linkNode.href = bugUrl(linkTextNode.textContent); | |
linkNode.appendChild(linkTextNode); | |
debugLog(logName + ": replacement", linkNode); | |
el.insertBefore(linkNode, n); | |
} | |
} else if (n.nodeType == n.ELEMENT_NODE | |
&& !skippableTags.includes(n.tagName)) { | |
//debugLog(logName + ": going deeper into", n); | |
recursiveLinkifyBugs(n); | |
} | |
} | |
//debugLog(logName + ": finished for", el); | |
} | |
function callback (mutations) { | |
me = logName + "[" + (++count) + "]"; | |
//debugLog(me + ": mutations detected", mutations); | |
let addedNodes = []; | |
for (let m of mutations) { | |
//debugLog(me + ": mutation", m); | |
for (let n of m.addedNodes) { | |
if (n.innerHTML?.length && !skippableTags.includes(n.tagName)) { | |
//debugLog(me + ": added node", n); | |
addedNodes.push(n); | |
} | |
} | |
} | |
if (addedNodes.length > 0) { | |
//debugLog(me + ": linkifying new content from", addedNodes); | |
recursiveLinkifyBugs(addedNodes); | |
} | |
} | |
let observer = new MutationObserver(callback); | |
let observerOptions = { | |
childList: true, | |
subtree: true | |
}; | |
recursiveLinkifyBugs(document.body); | |
observer.observe(document.body, observerOptions); | |
console.info(logName + ": ready"); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment