User:SilverLocust/UserUnderline.js

// /** * User Underliner * --- * This script is a fork of User:Chlod/Scripts/UserHighlighter.js * ("UserHighlighter 4.1") from 31 July 2023 with three differences: * * (1) Instead of highlighting usernames (using "background-color:"), * this scripts adds line underneath (using "border-bottom: 3px solid"); * * (2) The list of extended-confirmed users is more up-to-date, since * this uses User:NovemBot/userlist.js (updated daily) instead of * User:Chlod/Scripts/UserHighlighter/excon.json (last updated May 2023) * * (3) Some colors are different. See User:SilverLocust/UserUnderline. * * @author theopolisme * @author Bellezzasolo * @author Amorymeltzer * @author Pythoncoder * @author Chlod * @author SilverLocust */ (function($, mw) { mw.hook('wikipage.content').add(async function { // Declare group enum const groups = { arbcom:       "+", autopatrolled:    "a", bureaucrat:       "b", checkuser:        "c", extendedconfirmed: "e", filemover:        "f", interfaceadmin:   "i", extendedmover:    "m", suppress:         "o", patroller:        "p", rollbacker:       "r", templateeditor:   "t", reviewer:         "v", sysop:            "s", steward:          "w", };

// i18n? const lang = { load_err: "An error occurred while loading UserHighlighter.", load_err_report: "Please report this to ." };

// Open IDB connection const idbConnectionRequest = indexedDB.open("userhighlighter", 1);

idbConnectionRequest.onupgradeneeded = function (event) { const db = idbConnectionRequest.result; db.createObjectStore("main", {keyPath: "key"}); };

await new Promise((res, rej) => {       idbConnectionRequest.onsuccess = res;        idbConnectionRequest.onerror = rej;    }).catch((error) => {        console.error(`${lang.load_err} ${lang.load_err_report}`, error);        mw.notify(load_err);        return;    });

const db = idbConnectionRequest.result; const transaction = db.transaction("main", "readonly");

// Helpers async function dbGet(store, key) { return new Promise((res, rej) => {           const get = transaction.objectStore(store).get(key);            get.onsuccess =  => { res(get.result); };            get.onerror = rej;        }); }   async function dbPut(store, object) { return new Promise((res, rej) => {           const put = db.transaction("main", "readwrite")                .objectStore(store).put(object);            put.onsuccess =  => { res(true); };            put.onerror = rej;        }); }

let users = null; const lastPull = await dbGet("main", "lastPull"); if (       lastPull == undefined         || Date.now - lastPull.value > (window.ADMINHIGHLIGHT_INTERVAL || 86400000) // 1 day    ) { console.log("[UH] Redownloading..."); const updatedList = {}; // Grab all groups except extended-confirmed const groupRequest = JSON.parse((await (await fetch( mw.config.get("wgScriptPath") + "/index.php?" + "action=raw" + "&ctype=application/js" + "&title=User:MDanielsBot/markAdmins-Data.js" )).text)           .trim            .replace(/\);/g, "") .replace(/mw.hook\(.+?\)\.fire\(/, ""));

for (const [user, userGroups] of Object.entries(groupRequest)) { let groupString = ""; if (userGroups.includes("arbcom")) groupString += groups.arbcom; if (userGroups.includes("autoreviewer")) groupString += groups.autopatrolled; if (userGroups.includes("bureaucrat")) groupString += groups.bureaucrat; if (userGroups.includes("checkuser")) groupString += groups.checkuser; if (userGroups.includes("filemover")) groupString += groups.filemover; if (userGroups.includes("interface-admin")) groupString += groups.interfaceadmin; if (userGroups.includes("extendedmover")) groupString += groups.extendedmover; if (userGroups.includes("suppress")) groupString += groups.suppress; if (userGroups.includes("patroller")) groupString += groups.patroller; if (userGroups.includes("rollbacker") || userGroups.includes("global-rollbacker")) groupString += groups.rollbacker; if (userGroups.includes("templateeditor")) groupString += groups.templateeditor; if (userGroups.includes("reviewer")) groupString += groups.reviewer; if (userGroups.includes("sysop")) groupString += groups.sysop; if (userGroups.includes("steward")) groupString += groups.steward;

updatedList[user] = groupString; }

// Grab extended confirmed const xconRequest = await (await fetch( mw.config.get("wgScriptPath") + "/index.php?" + "action=raw" + "&ctype=application/json" + "&title=User:NovemBot/userlist.js" )).json; for (const user of Object.keys(xconRequest.extendedconfirmed)) { if (updatedList[user] == null) updatedList[user] = groups.extendedconfirmed; else updatedList[user] += groups.extendedconfirmed; }

// PUSH dbPut("main", {           key: "users",            users: updatedList        }).then( => {            dbPut("main", { key: "lastPull", value: Date.now});        });

users = updatedList; } else { users = (await dbGet("main", "users")).users; }

ADMINHIGHLIGHT_EXTLINKS = window.ADMINHIGHLIGHT_EXTLINKS || false; ADMINHIGHLIGHT_NAMESPACES = [-1,2,3];

mw.loader.using(['mediawiki.util','mediawiki.Uri', 'mediawiki.Title'], function { // Modified by SilverLocust below       mw.util.addCSS("[class~=userhighlighter_excon] {border-bottom: 3px solid blue}");        mw.util.addCSS("[class~=userhighlighter_pcusr] {border-bottom: 3px solid gold}");        mw.util.addCSS("[class~=userhighlighter_rbckr] {border-bottom: 3px solid red}");        mw.util.addCSS("[class~=userhighlighter_ptusr] {border-bottom: 3px solid darkviolet}");        mw.util.addCSS("[class~=userhighlighter_flmvr] {border-bottom: 3px solid lime}");        mw.util.addCSS("[class~=userhighlighter_pgmvr] {border-bottom: 3px solid green}");        mw.util.addCSS("[class~=userhighlighter_temop] {border-bottom: 3px solid pink}");        mw.util.addCSS("[class~=userhighlighter_sysop] {border-bottom: 3px solid deepskyblue}");        mw.util.addCSS("[class~=userhighlighter_checkuser] {border-bottom: 3px solid aquamarine}"); mw.util.addCSS("[class~=userhighlighter_suppress] {border-bottom: 3px solid lightseagreen}"); mw.util.addCSS("[class~=userhighlighter_interface-admin] {border-bottom: 3px solid hotpink}"); mw.util.addCSS("[class~=userhighlighter_bureaucrat] {border-bottom: 3px solid orange}"); mw.util.addCSS("[class~=userhighlighter_arbcom] {border-bottom: 3px solid grey}"); mw.util.addCSS("[class~=userhighlighter_steward] {border-bottom: 3px solid black}"); // End of modifications by SilverLocust $('#article a, #bodyContent a, #mw_contentholder a').each(function(index,linkraw){           try {                var link = $(linkraw);                var url = link.attr('href');                if (!url || url.charAt(0) === '#') return; // Skip  elements that aren't actually links; skip anchors                if (url.lastIndexOf("http://", 0) !== 0 && url.lastIndexOf("https://", 0) !== 0 && url.lastIndexOf("/", 0) !== 0) return; //require http(s) links, avoid "javascript:..." etc. which mw.Uri does not support                var uri = new mw.Uri(url);                if (!ADMINHIGHLIGHT_EXTLINKS && !$.isEmptyObject(uri.query)) return; // Skip links with query strings if highlighting external links is disabled                if (uri.host == 'en.wikipedia.org') {                    var mwtitle = new mw.Title(mw.util.getParamValue('title',url) || decodeURIComponent(uri.path.slice(6))); // Try to get the title parameter of URL; if not available, remove '/wiki/' and use that if ($.inArray(mwtitle.getNamespaceId, ADMINHIGHLIGHT_NAMESPACES)>=0) { var user = mwtitle.getMain.replace(/_/g," "); if (mwtitle.getNamespaceId === -1) user = user.replace('Contributions/',''); // For special page "Contributions/ " if (mwtitle.getNamespaceId === -1) user = user.replace('Contribs/',''); // The Contribs abbreviation too var usergroups = users[user]; if (usergroups == null) return; var usergroupNames = []; if (usergroups.includes(groups.steward)) { link.addClass(link.attr('class') + ' userhighlighter_steward'); usergroupNames.push("steward"); }                       if(usergroups.includes(groups.arbcom)) { link.addClass(link.attr('class') + ' userhighlighter_arbcom'); usergroupNames.push("Arbitration Committee member"); }                       if(usergroups.includes(groups.bureaucrat)) { link.addClass(link.attr('class') + ' userhighlighter_bureaucrat'); usergroupNames.push("bureaucrat"); }                       if(usergroups.includes(groups.interfaceadmin)) { link.addClass(link.attr('class') + ' userhighlighter_interface-admin'); usergroupNames.push("interface administrator"); }                       if(usergroups.includes(groups.suppress)) { link.addClass(link.attr('class') + ' userhighlighter_suppress'); usergroupNames.push("oversighter"); }                       if(usergroups.includes(groups.checkuser)) { link.addClass(link.attr('class') + ' userhighlighter_checkuser'); usergroupNames.push("checkuser"); }                       if (usergroups.includes(groups.sysop)) { link.addClass(link.attr('class') + ' userhighlighter_sysop'); usergroupNames.push("administrator"); }                       if(usergroups.includes(groups.templateeditor)) { link.addClass(link.attr('class') + " userhighlighter_temop"); usergroupNames.push("template editor"); }                       if(usergroups.includes(groups.extendedmover)) { link.addClass(link.attr('class') + " userhighlighter_pgmvr"); usergroupNames.push("page mover"); }                       if(usergroups.includes(groups.filemover)) { link.addClass(link.attr('class') + " userhighlighter_flmvr"); usergroupNames.push("file mover"); }                       if(usergroups.includes(groups.patroller)) { link.addClass(link.attr('class') + " userhighlighter_ptusr"); usergroupNames.push("patroller"); }                       if(usergroups.includes(groups.rollbacker)) { link.addClass(link.attr('class') + " userhighlighter_rbckr"); usergroupNames.push("rollbacker"); }                       if(usergroups.includes(groups.reviewer)) { link.addClass(link.attr('class') + " userhighlighter_pcusr"); usergroupNames.push("pending changes reviewer"); }                       if(usergroups.includes(groups.extendedconfirmed)) { link.addClass(link.attr('class') + " userhighlighter_excon"); usergroupNames.push("extended confirmed"); }                       if (usergroupNames.length > 0) { var merged = usergroupNames.join(", "); var link_title = link.attr("title"); link.attr(                       		"title",                        		(link_title != null ? link_title + "\n" : "")                        			+ merged[0].toUpperCase + merged.substring(1)                			); }                   }                }            } catch (e) { // Sometimes we will run into unparsable links, so just log these and move on               console.error('[UH] Recoverable error', e); }       });    }); }); }(jQuery, mediaWiki)); //