MediaWiki:Common.js

/* Any JavaScript here will be loaded for all users on every page load. */

// ================================================================================ // Pikan -- the analyzer on the edit page // ================================================================================ $.getScript('//www.pikminwiki.com/index.php?title=User:Espyo/pikan-core.js' + '&action=raw&ctype=text/javascript', function {  mw.loader.load('//www.splatoonwiki.org/w/index.php?title=User:Espyo/pikan-inkipedia.js' + '&action=raw&ctype=text/javascript'); });

// ================================================================================ // Username replace function for Template:USERNAME // ================================================================================ // Inserts user name into. // Disable by setting disableUsernameReplace = true. jQuery(function($) { if (typeof(disableUsernameReplace) != 'undefined' && disableUsernameReplace)    return;  var username = mw.config.get('wgUserName');  if (username == null)    return;

$('.insertusername').text(username); });

// ================================================================================ // Editcount replace function for Template:EDITCOUNT // ================================================================================ // Inserts edit count into jQuery(function($) { var userEditCount = mw.config.get('wgUserEditCount');  if (userEditCount == null)    return;

$('.inserteditcount').text(userEditCount); });

// ================================================================================ // Registration date replace function for Template:REGISTRATIONDATE // ================================================================================ // Inserts registration date into jQuery(function($) { var userRegistrationDate = mw.config.get('wgUserRegistration');  if (userRegistrationDate == null)    return;  var d = new Date(0); // Sets the date to the epoch  d.setUTCMilliseconds(userRegistrationDate);

$('.insertregistrationdate').text(d.toLocaleString); });

// ================================================================================ // Injection into Upload Wizard for custom licensing and categories // ================================================================================ //https://commons.wikimedia.org/wiki/Commons:Upload_Wizard/Editing#License_options //https://www.mediawiki.org/wiki/Extension:UploadWizard/CustomWikiTextLicenseFeature

if ("UploadWizard" === mw.config.get('wgCanonicalSpecialPageName')) { mw.loader.using('ext.uploadWizard', function {    try {      var autoAdd = mw.config.get('UploadWizardConfig').autoAdd;      // Add a custom category to all my upload wizard uploads      // autoAdd.categories.push("Images");

// Add a custom license tag to all my files uploaded using the sorcerer autoAdd.wikitext += ""; } catch (ex) { mw.log("Injecting custom-category into UploadWizard failed.", ex); } }); }

/////////////////////////////////////////////////////////////////////////////// //                              Gobbler Class                               // ///////////////////////////////////////////////////////////////////////////////

// Folds all infoboxes into a single, tabbed element

// Object constructor var Gobbler = function {

// Check for a gobbler object in the document this.element = document.getElementById("gobbler"); if (!this.element) return;

// Gobble all of the infoboxes on the page var elements = document.getElementsByClassName("tagInfobox"); var tabs    = []; for (var x = 0; x < elements.length; x++) tabs.push(this.gobble(elements[x])); this.onTab(tabs[tabs.length - 1]);

// Add the infoboxes and tab buttons to the gobbler element var tabStrip = document.createElement("div"); tabStrip.style.textAlign = "left"; if (tabs.length > 1) this.element.appendChild(tabStrip); for (var x = 0; x < tabs.length; x++) { tabStrip.appendChild(tabs[x].button); this.element.appendChild(tabs[x].element); }

};

// Process an infobox Gobbler.prototype.gobble = function(element) {

// Retrieve the infobox's hidden tab button element var button = element.getElementsByClassName("tagTab")[0];

// Create a tab object using the contents of the element var tab = { button:       button, colorInactive: button.style.backgroundColor, colorActive:  button.style.outlineColor, element:      element, cssText:      element.style.cssText };

// Configure element properties var that = this; element.style.cssText = "width: 100%; height: 100%; display: none;"; button.style.display = "inline"; button.onmousedown   = function(e) { if (e.button == 0) that.onTab(tab); };   return tab; };

// Event handler for when a tab is selected/clicked Gobbler.prototype.onTab = function(tab) {

// Deselect the previous infobox if (this.current) { this.current.button.style.fontWeight     = "normal"; this.current.button.style.backgroundColor = this.current.colorInactive; this.current.element.style.display       = "none"; }

// Select the new infobox this.element.style.cssText      = tab.cssText; tab.button.style.fontWeight     = "bold"; tab.element.style.display       = "inline"; tab.button.style.backgroundColor = tab.colorActive; this.current                    = tab; };

new Gobbler;

/////////////////////////////////////////////////////////////////////////////// //                       Schedule Utility Functions                         // ///////////////////////////////////////////////////////////////////////////////

// Advances a timestamp to the next multiple of 2 hours function advanceDateTime(time) { var ret = new Date(time.getTime); ret.setMinutes(0); ret.setTime(ret.getTime + 3600000 * ( ret.getUTCHours & 1 ? 1 : 2   ));    return ret; }

// Formats a timestamp as a string in local time function formatDateTime(time) { var ret = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ][time.getMonth] + " " + time.getDate + " " + zeroPad(time.getHours, 2) + ":" + zeroPad(time.getMinutes, 2) ;   return ret; }

// Parses a UTC date string in the format "MMM dd hh:mm" function parseDateTime(text, year) { text = text.split(/[\s:]+/); return new Date(Date.UTC( year, { jan: 0, feb: 1, mar: 2, apr: 3, may: 4, jun:  5, jul: 6, aug: 7, sep: 8, oct: 9, nov: 10, dec: 11 }[text[0].toLowerCase], parseInt(text[1]), // Day parseInt(text[2]), // Hours parseInt(text[3]), // Minutes 0, 0 // Seconds, milliseconds )); }

// Parses a last-fetched string into a Date object function parseFetched(now, text) { var ret = parseDateTime(text, now.getUTCFullYear); if (now < ret) // Accounts for year boundary ret.setUTCFullYear(ret.getUTCFullYear - 1); return ret; }

// Parses a schedule string into a Date object function parseSchedule(fetched, text) { var ret = parseDateTime(text, fetched.getUTCFullYear); if (ret.getTime < fetched.getTime - 8640000000) ret.setUTCFullYear(ret.getUTCFullYear + 1); return ret; }

// Calculates the time remaining until a given timestamp, as a string function timeUntil(now, target) { target     = target.getTime - now.getTime; target     = Math.floor(target % 7200000 / 1000); var seconds = zeroPad(target % 60, 2); var minutes = zeroPad(Math.floor(target / 60) % 60, 2); var hours  = Math.floor(target / 3600); return hours + ":" + minutes + ":" + seconds; }

// Pad a number with leading zeroes function zeroPad(number, digits) { number = "" + number; while (number.length < digits) number = "0" + number; return number; }

/////////////////////////////////////////////////////////////////////////////// //                          BattleSchedule Class                            // ///////////////////////////////////////////////////////////////////////////////

// Maintains auto-updating Ink Battle schedule elements

// Object constructor var BattleSchedule = function {

// Initialize instance fields this.lblNow    = document.getElementById("battle1"); this.lblNext   = document.getElementById("battle2"); this.lblFetched = document.getElementById("battleFetched"); this.prev      = false;

// Error checking if (!this.lblFetched) return; // No schedule data

// Get the current and last-fetched timestamps var now    = new Date; var fetched = parseFetched(now, this.lblFetched.innerHTML);

// Determine the timestamp of the following two rotations this.next = advanceDateTime(fetched); this.later = advanceDateTime(this.next);

// Update initial display this.onTick(now); this.lblFetched.innerHTML = formatDateTime(fetched);

// Schedule periodic updates var that = this; this.timer = setInterval(function { that.onTick(new Date); }, 1000); };

// Periodic update handler BattleSchedule.prototype.onTick = function(now) {

// Determine when the "Now" row enters the past if (now >= this.next && !this.prevNow) { this.prev            = true; this.lblNow.innerHTML = "Previous"; }

// Determine when the "Next" row enters the past if (now >= this.later && !this.prevNext) { this.lblNext.innerHTML = "Previous"; clearInterval(this.timer); return; }

// Display the time until the next rotation this.lblNext.innerHTML = (this.prev ? "Now, for another " : "Next, in ") + timeUntil(now, this.prev ? this.later : this.next) ; };

new BattleSchedule;

/////////////////////////////////////////////////////////////////////////////// //                          SalmonSchedule Class                            // ///////////////////////////////////////////////////////////////////////////////

// Maintains auto-updating Salmon Run schedule elements

// Object constructor var SalmonSchedule = function {

// Get the current and last-fetched timestamps var lblFetched = document.getElementById("salmonFetched"); if (!lblFetched) return; // No schedule var now       = new Date; var fetched   = parseFetched(now, lblFetched.innerHTML);

// Initialize instance fields this.slots = [ this.parse(document.getElementById("salmon1"), fetched), this.parse(document.getElementById("salmon2"), fetched), ];

// Update initial display this.onTick(now); lblFetched.innerHTML = formatDateTime(fetched);

// Schedule periodic updates var that = this; this.timer = setInterval(function { that.onTick(new Date); }, 1000); };

// Periodic update handler SalmonSchedule.prototype.onTick = function(now) {

// Cycle through slots for (var x = 0; x < this.slots.length; x++) { var slot = this.slots[x]; if (slot.prev) continue; // Skip this slot

// Determine when this slot should stop updating slot.prev = now >= slot.end;

// Update the element slot.element.innerHTML = now >= slot.end ? "Previous" : now >= slot.start ? "Now - " + formatDateTime(slot.end) : formatDateTime(slot.start) + " - " + formatDateTime(slot.end) ;   }

// De-schedule the timer if (this.slots[this.slots.length - 1].prev) clearInterval(this.timer); };

// Parse a single Salmon Run schedule slot SalmonSchedule.prototype.parse = function(element, fetched) { var text = element.innerHTML; return { element: element, start:  parseSchedule(fetched, text.substring( 0, 12)), end:    parseSchedule(fetched, text.substring(15, 27)), prev:   false }; }

new SalmonSchedule;

/////////////////////////////////////////////////////////////////////////////// //                           ShopSchedule Class                             // ///////////////////////////////////////////////////////////////////////////////

// Maintains auto-updating SplatNet 2 Shop schedule elements

// Object constructor var ShopSchedule = function { var lblFetched = document.getElementById("shopFetched"); if (!lblFetched) return; // No schedule

// Get the current and last-fetched timestamps var now    = new Date; var fetched = parseFetched(now, lblFetched.innerHTML);

// Update initial display lblFetched.innerHTML = formatDateTime(fetched); };

new ShopSchedule;

/////////////////////////////////////////////////////////////////////////////// //                         SplatfestSchedule Class                          // ///////////////////////////////////////////////////////////////////////////////

// Maintains auto-updating Splatfest schedule elements

// Object constructor var SplatfestSchedule = function {

// Initialize instance fields var now = new Date; this.slots = [ this.parse(document.getElementById("splatfest1"), now), this.parse(document.getElementById("splatfest2"), now), this.parse(document.getElementById("splatfest3"), now) ];

// Update initial display this.onTick(now);

// Schedule periodic updates var that = this; this.timer = setInterval(function { that.onTick(new Date); }, 1000); };

// Periodic update handler SplatfestSchedule.prototype.onTick = function(now) {

// Cycle through slots for (var x = 0; x < this.slots.length; x++) { var slot = this.slots[x]; if (slot.prev) continue; // Skip this slot

// Determine when this slot should stop updating slot.prev = now >= slot.end;

// Update the element slot.element.innerHTML = now >= slot.end ? "Concluded" : now >= slot.start ? "Now - " + formatDateTime(slot.end) : formatDateTime(slot.start) ;   }

// De-schedule the timer if (this.slots[this.slots.length - 1].prev) clearInterval(this.timer); };

// Parse a single Splatfest schdule slot SplatfestSchedule.prototype.parse = function(element, now) {

// Error checking if (!element) return { prev: true };

// Determine the current time and start and end timestamps var start = parseDateTime(element.innerHTML, now.getUTCFullYear); return { element: element, start:  start, end:    new Date(start.getTime + 86400000), prev:   false }; };

new SplatfestSchedule;

// ================================================================================ // Prevent audio from loading until clicked // ================================================================================ // Made by FunPL with slight help from google

// Utility functions

function resolveID(id){ var replaced = id.replaceAll(".26","&"); replaced = replaced.replaceAll(".21","!"); replaced = replaced.replaceAll(".28","(");   replaced = replaced.replaceAll(".29",")"); replaced = replaced.replaceAll(".CF.89","ω"); replaced = replaced.replaceAll(".7E","~"); replaced = replaced.replaceAll(".27","'"); replaced = replaced.replaceAll(".40","@"); replaced = replaced.replaceAll(".23","#"); replaced = replaced.replaceAll(".24","$"); replaced = replaced.replaceAll(".25","%"); return replaced; } String.prototype.replaceAll = function(search, replacement) { var target = this; return target.split(search).join(replacement); };

var firstParse = true;

function parseApi(parsetext, fn) { if (firstParse) { setTimeout(parseApi, 1000, parsetext, fn) setTimeout(function {           firstParse = false        }, 900) return }   new mw.Api.get({        action: 'parse',        text: parsetext,        title: mw.config.values.wgPageName,    }).done(function(data) {        const wikitext = data.parse.text['*'];        if (wikitext) {            fn(wikitext);        } else {            fn("")        }    }); }

function parseApiAdd(parsetext, add, fn) { if (firstParse) { setTimeout(parseApiAdd, 1000, parsetext, add, fn) setTimeout(function {           firstParse = false        }, 900) return }   new mw.Api.get({        action: 'parse',        text: parsetext,        title: mw.config.values.wgPageName,    }).done(function(data) {        const wikitext = data.parse.text['*'];        if (wikitext) {            fn(wikitext, add);        } else {            fn("", null)        }    }); }

// Audio players

// Initialize all load buttons

// Load/Unload var unloadedtext = "Load "; var loadels = document.getElementsByClassName("load-media"); var loadmediadata = {}; [].forEach.call(loadels, function(el) {   var nr = 0;    if(el.children[nr] == null){        el.innerHTML = "Error! Please report this"    }    if(el.children[nr].nodeName != "P"){        nr = 1;        if(el.children[nr] == null){            el.innerHTML = "Error! Please report this"        }    }    el.id = el.id + '|' + el.children[nr].children[1].innerHTML;    var loadname = el.children[nr].children[0].innerHTML;    if(loadname == ""){        loadname = "media"    }    loadmediadata[el.id] = loadname;    el.innerHTML = unloadedtext + el.id + unloadedtext2 + loadname + " ";    el.classList.add("load-media-group-"+loadname) });

// Load/Unload all var loadalltext = "Load all "; var unloadalltext = "Unload all "; var allels = document.getElementsByClassName("loadall-media"); var allels2 = document.getElementsByClassName("unloadall-media"); [].forEach.call(allels, function(el) {   var group = el.parentElement.classList[0].split("-")[3]    if(group == ""){        group = "media";    }    el.innerHTML = loadalltext + group + " files "; }); [].forEach.call(allels2, function(el) {   var group = el.parentElement.classList[0].split("-")[3]    if(group == ""){        group = "media";    }    el.innerHTML = unloadalltext + group + " files "; });

function loadmedia(text) { try { var loadname = loadmediadata[text.id]; if (text.innerHTML == unloadedtext + text.id + unloadedtext2 + loadname + " ") { text.innerHTML = "Wait "; var id = resolveID(text.id); console.log(text.id); console.log(id); parseApi("" + id + "", function(wikitext) {               text.innerHTML = "Unload "+loadname+" " + wikitext;            }); } else { text.innerHTML = unloadedtext + text.id + unloadedtext2 + loadname + " "; }   } catch (error) { text.innerHTML = "Error "; console.error(error); } } function loadallmedia(allel) { try { if(allel.parentElement.parentElement.classList[0] != "loadall-media-group-"){ var group = allel.parentElement.parentElement.classList[0].split("-")[3]; var thisloadels = document.getElementsByClassName("load-media-group-"+group) }       else{ var thisloadels = loadels; }       [].forEach.call(thisloadels, function(el) {            loadforce(el)        }); } catch (error) { console.error(error); } }

function unloadallmedia(allel) { try { if(allel.parentElement.parentElement.classList[0] != "loadall-media-group-"){ var group = allel.parentElement.parentElement.classList[0].split("-")[3]; var thisloadels = document.getElementsByClassName("load-media-group-"+group) }       else{ var thisloadels = loadels; }       [].forEach.call(thisloadels, function(el) {            unloadforce(el)        }); } catch (error) { console.error(error); } }

function loadforce(text) { try { var loadname = loadmediadata[text.id]; if (text.innerHTML == unloadedtext + text.id + unloadedtext2 + loadname + " ") { text.innerHTML = "Wait "; var id = resolveID(text.id); console.log(text.id); console.log(id); parseApi("" + id + "", function(wikitext) {               text.innerHTML = "Unload "+loadname+" " + wikitext;            }); }   } catch (error) { text.innerHTML = "Error "; console.error(error); } }

function unloadforce(text) { try { var loadname = loadmediadata[text.id]; if (text.innerHTML != unloadedtext + text.id + unloadedtext2 + loadname + " ") { text.innerHTML = unloadedtext + text.id + unloadedtext2 + loadname + " "; }   } catch (error) { text.innerHTML = "Error "; console.error(error); } }

// ================================================================================ // Interactive Music Code // ================================================================================

// Set an onclick handler for opening the filters tab

$(".i-menu-filter").click(function{	if($(this).next.css("display") == "none"){		$(this).next.css("display", "block")		$(this).css("background", "#aaaaaa")	}	else{		$(this).next.css("display", "none")		$(this).css("background", "")	} })

// Set an onclick handler for checking boxes

$(".i-checkbox").click(function{	if($(this).children.css("opacity") == "0"){		$(this).children.css("opacity", "1")		if($(this).hasClass("i-category")){			icheckboxes;		}	}	else{		$(this).children.css("opacity", "0")		if($(this).hasClass("i-category")){			icheckboxes;		}	} })

// Handle refreshing the music list based on selected filters

function icheckboxes{ var allclass = []; var classStatus = {}; $(".i-checkbox.i-category").each(function{       var classes = $(this)[0].classList;        for(var i = 0, len = classes.length;i<len;i++){            var curr = classes[i];            if(curr.startsWith("i-category-")){                if(allclass.indexOf(curr) == -1){                    allclass[allclass.length] = curr;                    classStatus[curr] = $(this).children.css("opacity");                }            }        }    }) var select = "." + allclass.join(", ."); $(select).each(function{       if($(this).hasClass("i-checkbox")){            return;        }        var classes = $(this)[0].classList;        var status = 1;        for(var i = 0, len = classes.length;i<len;i++){            var curr = classes[i];            if(curr.startsWith("i-category-")){                if(classStatus[curr] == 0){                    status = 0;                }            }        }        if(status == 1){            status = ""        }        else{            status = "none"        }        $(this).css("display", status)    }) }

// ================================================================================ // Page specific JS/CSS // ================================================================================

//Define vars var mwSkin = mw.config.get("skin") var pageTypes = {}; pageTypes.globalJS = 'MediaWiki:Common.js/' + mw.config.get("wgPageName"); pageTypes.globalCSS = 'MediaWiki:Common.css/' + mw.config.get("wgPageName"); pageTypes.skinJS = 'MediaWiki:' + mwSkin + '.js/' + mw.config.get("wgPageName"); pageTypes.skinCSS = 'MediaWiki:' + mwSkin + '.css/' + mw.config.get("wgPageName"); pageTypes.userJS = 'User:' + mw.config.get("wgUserName") + '/common.js/' + mw.config.get("wgPageName"); pageTypes.userCSS = 'User:' + mw.config.get("wgUserName") + '/common.css/' + mw.config.get("wgPageName"); pageTypes.userSkinJS = 'User:' + mw.config.get("wgUserName") + '/' + mwSkin + '.js/' + mw.config.get("wgPageName"); pageTypes.userSkinCSS = 'User:' + mw.config.get("wgUserName") + '/' + mwSkin + '.css/' + mw.config.get("wgPageName");

function checkPage(type, exist) { if (exist == null) { new mw.Api.get({           action: 'parse',            page: type,            prop: ""        }).done(function (data) {            checkPage(type, true)        }).fail(function (data) {            checkPage(type, false)        }); }   else if (exist) { if (getTypeByValue(type).endsWith("JS")) { mw.loader.load('/w/index.php?title=' + pageEnc(type) + '&action=raw&ctype=text/javascript'); }       else { mw.loader.load('/w/index.php?title=' + pageEnc(type) + '&action=raw&ctype=text/css', 'text/css'); }       checkNextPage(type) }   else if (!exist) { checkNextPage(type) } }

function checkNextPage(type) { switch (type) { case pageTypes.globalJS: checkPage(pageTypes.skinJS); break; case pageTypes.globalCSS: checkPage(pageTypes.globalJS); break; case pageTypes.skinJS: checkPage(pageTypes.skinCSS); break; case pageTypes.skinCSS: if (mw.config.get("wgUserName") != null) { checkPage(pageTypes.userJS); }           break; case pageTypes.userJS: if (mw.config.get("wgUserName") != null) { checkPage(pageTypes.userCSS); }           break; case pageTypes.userCSS: if (mw.config.get("wgUserName") != null) { checkPage(pageTypes.userSkinJS); }           break; case pageTypes.userSkinJS: if (mw.config.get("wgUserName") != null) { checkPage(pageTypes.userSkinCSS); }           break; } }

function pageEnc(string) { return encodeURIComponent(string); }

function getTypeByValue(value) { return Object.keys(pageTypes).find(function (key) { return pageTypes[key] === value }); }

//Check page specific files onApiReady(function {    checkPage(pageTypes.globalCSS); });

function onApiReady(callback) { if (mw.Api != null) { setTimeout(callback, 0); } else { setTimeout(onApiReady.bind(null, callback), 0); } }

// ================================================================================ // DarkMode // ================================================================================ if($("[href='/w/load.php?debug=false&lang=en&modules=ext.gadget.darkmode%2Ctarget&only=styles&skin=vector']")[0] != null){ $(".lightmode").remove } else{ $(".darkmode").remove } $(".poll").append($("#poll"))