use a mod's icon (or a mod category's icon) as its curseforge page's favicon
// ==UserScript==
// @name curseforge mod favicon
// @namespace https://github.com/adrianmgg
// @version 1.1.0
// @description use a mod's icon (or a mod category's icon) as its curseforge page's favicon
// @author amgg
// @match https://www.curseforge.com/*
// @match https://legacy.curseforge.com/*
// @grant none
// @run-at document-end
// @license MIT
// @compatible chrome
// @compatible firefox
// ==/UserScript==
function getCategoryPageIcon() {
// get category links (need to look inside `nav`s specifically because otherwise other dropdowns will also get matched)
const categoryIcons = [...document.querySelectorAll('nav .category-list-item [data-value="category-link"]')]
.filter(el => el.querySelector(':scope > .bg-primary-0') !== null) // just the selected ones
.map(el => el.querySelector(':scope > div > figure > img')) // get icon inside it
.map(img => img.src);
// if there's multiple we want the last one, for the cases when a sub-category is selected
return categoryIcons[categoryIcons.length - 1] ?? null;
}
function getModPageIcon() {
const fromIconElem = document.querySelector('.project-avatar > a > img')?.src ?? null;
const fromOpengraphTags = document.querySelector('html > head > meta[property="og:image"]')?.content ?? null;
// some specific pages don't have og tags, so we use the icon element as a fallback on those only
if(/\/relations\/(dependents|dependencies)\/?$|\/issues\/?$/.test(window.location.pathname)) {
return fromIconElem;
} else {
return fromOpengraphTags;
}
}
function getIconUrl() {
if(window.location.hostname === 'legacy.curseforge.com') {
return getCategoryPageIcon() ?? getModPageIcon() ?? null;
} else {
const nextData = JSON.parse(document.getElementById('__NEXT_DATA__')?.textContent ?? '{}');
return nextData?.props?.pageProps?.project?.avatarUrl;
}
}
(function() {
const iconUrl = getIconUrl();
if(iconUrl === null) return;
// get the current favicon elem (if any) and swap its url
const faviconLink = document.querySelector('link[rel~="icon"]');
if(faviconLink !== null) faviconLink.href = iconUrl;
// watch for any favicon elems added later and swap out their urls
(new MutationObserver(function(mutations, observer) {
for(const mutation of mutations) {
for(const node of mutation.addedNodes) {
if(node.nodeType === Node.ELEMENT_NODE && node.nodeName === 'LINK' && node.matches('[rel~="icon"]')) {
node.href = iconUrl;
}
}
}
})).observe(document.head, {subtree: false, childList: true});
})();