Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
//<nowiki>
/**
* 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 <https://en.wikipedia.org/wiki/User_talk:SilverLocust>."
};
// 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 <a> 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/<username>"
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.autopatrolled)) {
link.addClass(link.attr('class') + ' userhighlighter_ap');
usergroupNames.push("autopatrolled");
}
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));
// </nowiki>