MediaWiki:Common.js: Difference between revisions
(restoring XEB; it's back in working order!) |
(added JS support for dynamic locale-based date format) |
||
(44 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
/* JavaScript brought over from Wikipedia appears first. JS code written for our wiki comes second, under the heading CUSTOM ONI WIKI ADDITIONS. */ | |||
/* WIKIPEDIA CARRY-OVERS */ | |||
/* The original code on Wikipedia's Common.js page should be checked once in a while for updates to, or removal of, this code */ | |||
/** extract a URL parameter from the current URL ********** | /** extract a URL parameter from the current URL ********** | ||
* From [[en:User:Lupin/autoedit.js]] | * From [[en:User:Lupin/autoedit.js]] | ||
Line 14: | Line 19: | ||
return null; | return null; | ||
} | } | ||
/** &withJS= URL parameter ******* | /** &withJS= URL parameter ******* | ||
* Allow to try custom scripts from MediaWiki space | * Allow to try custom scripts from MediaWiki space | ||
Line 22: | Line 27: | ||
if (extraJS && extraJS.match("^MediaWiki:[^&<>=%]*\.js$")) | if (extraJS && extraJS.match("^MediaWiki:[^&<>=%]*\.js$")) | ||
{ | { | ||
mw.loader.load('/w/index.php?title=' + extraJS + '&action=raw&ctype=text/javascript'); | |||
} | } | ||
// TODO: Get this working again | |||
/* | /* If we're on the Search page, load the script that augments that page */ | ||
if ( | if (mw.config.get('wgPageName') === 'Special:Search') | ||
{ | { | ||
mw.loader.load('/w/index.php?title=MediaWiki:Common.js/search.js&action=raw&ctype=text/javascript'); | |||
} | } | ||
/** | |||
* Collapsible tables; reimplemented with mw-collapsible | |||
* Styling is also in place to avoid FOUC | |||
* | |||
* Allows tables to be collapsed, showing only the header. See [[Help:Collapsing]]. | |||
* @version 3.0.0 (2018-05-20) | |||
* @source https://www.mediawiki.org/wiki/MediaWiki:Gadget-collapsibleTables.js | |||
* @author [[User:R. Koot]] | |||
* @author [[User:Krinkle]] | |||
* @author [[User:TheDJ]] | |||
* @deprecated Since MediaWiki 1.20: Use class="mw-collapsible" instead which | |||
* is supported in MediaWiki core. Shimmable since MediaWiki 1.32 | |||
* | |||
* @param {jQuery} $content | |||
*/ | |||
function makeCollapsibleMwCollapsible( $content ) { | |||
var $tables = $content | |||
.find( 'table.collapsible:not(.mw-collapsible)' ) | |||
.addClass( 'mw-collapsible' ); | |||
$.each( $tables, function ( index, table ) { | |||
// mw.log.warn( 'This page is using the deprecated class collapsible. Please replace it with mw-collapsible.'); | |||
{ | if ( $( table ).hasClass( 'collapsed' ) ) { | ||
$( table ).addClass( 'mw-collapsed' ); | |||
// mw.log.warn( 'This page is using the deprecated class collapsed. Please replace it with mw-collapsed.'); | |||
} | |||
} ); | |||
if ( $tables.length > 0 ) { | |||
mw.loader.using( 'jquery.makeCollapsible' ).then( function () { | |||
$tables.makeCollapsible(); | |||
} ); | |||
} | |||
} | |||
mw.hook( 'wikipage.content' ).add( makeCollapsibleMwCollapsible ); | |||
/** | |||
* Add support to mw-collapsible for autocollapse, innercollapse and outercollapse | |||
* | |||
* Maintainers: TheDJ | |||
*/ | |||
function mwCollapsibleSetup( $collapsibleContent ) { | |||
var $element, | |||
$toggle, | |||
autoCollapseThreshold = 2; | |||
$.each( $collapsibleContent, function ( index, element ) { | |||
$element = $( element ); | |||
if ( $element.hasClass( 'collapsible' ) ) { | |||
$element.find( 'tr:first > th:first' ).prepend( $element.find( 'tr:first > * > .mw-collapsible-toggle' ) ); | |||
} | |||
if ( $collapsibleContent.length >= autoCollapseThreshold && $element.hasClass( 'autocollapse' ) ) { | |||
$element.data( 'mw-collapsible' ).collapse(); | |||
} else if ( $element.hasClass( 'innercollapse' ) ) { | |||
if ( $element.parents( '.outercollapse' ).length > 0 ) { | |||
$element.data( 'mw-collapsible' ).collapse(); | |||
} | |||
} | |||
// because of colored backgrounds, style the link in the text color | |||
// to ensure accessible contrast | |||
$toggle = $element.find( '.mw-collapsible-toggle' ); | |||
if ( $toggle.length ) { | |||
// Make the toggle inherit text color (Updated for T333357 2023-04-29) | |||
if ( $toggle.parent()[ 0 ].style.color ) { | |||
$toggle.css( 'color', 'inherit' ); | |||
$toggle.find( '.mw-collapsible-text' ).css( 'color', 'inherit' ); | |||
} | |||
} | |||
} ); | |||
} | } | ||
mw.hook( 'wikipage.collapsibleContent' ).add( mwCollapsibleSetup ); | |||
/* | /* CUSTOM ONI WIKI ADDITIONS */ | ||
/* Focus us on the Search box if we're on the Main Page, so we can immediately start typing */ | |||
if (mw.config.get('wgIsMainPage')) | |||
{ | { | ||
$(function() | |||
{ | { | ||
$('#searchInput').focus(); | |||
}; | }); | ||
} | } | ||
/** Hover tables ********************************************************* | /** Hover tables ********************************************************* | ||
Line 130: | Line 144: | ||
} | } | ||
} | } | ||
function initHoverTables() | function initHoverTables() | ||
{ | { | ||
Line 144: | Line 157: | ||
for (var j = 0; j < Cells.length; j++) | for (var j = 0; j < Cells.length; j++) | ||
{ | { | ||
if ( | if ($(Cells[j]).hasClass("hovercell")) | ||
{ | { | ||
$(Cells[j]).mouseover(new Function("evt", "showDescrip(this.id, true);")); | |||
$(Cells[j]).mouseout(new Function("evt", "showDescrip(this.id, false);")); | |||
} | } | ||
} | } | ||
} | } | ||
} | } | ||
$(initHoverTables); | |||
/** Hover GIFs ********************************************************* | /** Hover GIFs ********************************************************* | ||
Line 176: | Line 188: | ||
gif_img.src = gif_src.replace('.gif', '.jpg'); | gif_img.src = gif_src.replace('.gif', '.jpg'); | ||
} | } | ||
function initHoverGIFs() | function initHoverGIFs() | ||
{ | { | ||
Line 186: | Line 197: | ||
for (var i = 0; i < gifs.length; i++) | for (var i = 0; i < gifs.length; i++) | ||
{ | { | ||
$(gifs[i]).mouseover(swapImage(this.id, true)); | |||
$(gifs[i]).mouseout(swapImage(this.id, false)); | |||
} | } | ||
} | } | ||
$(initHoverGIFs); | |||
/***** Auto-sorting tables ******** | |||
* Auto-sorts sortable tables by a given column (why is this not built-in?!) | |||
* You must opt into this feature by placing "autosort" in the list of | |||
/ | * the table's classes along with "sortable" | ||
* | * | ||
* Maintainers: [[User:Iritscen]] | |||
* Maintainers: [[User: | |||
*/ | */ | ||
// For some reason, the arrow buttons and headerSort classes are not attached to sortable tables immediately upon the "load" event, and we need those, so we wait a bit before trying to sort | |||
function sortTimer() | |||
function | |||
{ | { | ||
setTimeout(function() | |||
{ | { | ||
sortSortableTables(); | |||
}, (1 * 1000)); | |||
} | |||
} | } | ||
function sortSortableTables() | |||
function | |||
{ | { | ||
console.log("Sorting the autosort tables."); | |||
// Iterate over all <div> elements | |||
var | var divs = document.getElementsByTagName("div"); | ||
if (!divs) return; | |||
for (var i = 0; i < | for (var i = 0; i < divs.length; i++) | ||
{ | { | ||
var theDiv = divs[i]; | |||
var tables = theDiv.getElementsByTagName("table"); | |||
if (!tables) continue; | |||
for (var j = 0; j < tables.length; j++) | |||
{ | { | ||
/ | var theTable = tables[j]; | ||
// If we found a sortable table that is asking for autosort... | |||
if ($(theTable).hasClass("sortable") && $(theTable).hasClass("autosort")) | |||
{ | |||
var sortColumnNum = 1, curColumnNum = 0; | |||
// First check if there is any by-column-x class at all, then take the time to figure out the value of x | |||
var patt = /by-column/; | |||
if (patt.test(theTable.className)) | |||
{ | |||
for (var col = 1; col < 10; col++) | |||
{ | |||
var colOption = "by-column-" + col; | |||
if ($(theTable).hasClass(colOption)) | |||
{ | |||
//console.log("Table " + j + " wants to sort by column " + col); | |||
sortColumnNum = col; | |||
// Now look for sort button and click it | |||
var allTHs = theTable.getElementsByTagName("th"); | |||
if (!allTHs) | |||
{ | |||
console.log("Failed to get 'th' elements!"); | |||
continue; | |||
} | |||
for (var k = 0; k < allTHs.length; k++) | |||
{ | |||
//console.log("Class names for 'th' # " + k + " are " + allTHs[k].className); | |||
if ($(allTHs[k]).hasClass("headerSort")) | |||
{ | |||
curColumnNum++; | |||
if (curColumnNum == sortColumnNum) | |||
{ | |||
console.log("Clicking sort button (ID " + k + ") for column " + sortColumnNum); | |||
$(allTHs[k]).trigger("click"); // use jQuery's trigger() to send click event to this arrow | |||
return; | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | } | ||
} | } | ||
Line 295: | Line 277: | ||
} | } | ||
} | } | ||
$(sortTimer); | |||
/***** Auto-formatting dates ******** | |||
* Re-formats a date placed by [[Template:LocaleDate]] to match the | |||
* month/day order of the user's locale. | |||
/ | |||
* | * | ||
* | * Maintainers: [[User:Iritscen]] | ||
*/ | */ | ||
function getLang() | |||
function | |||
{ | { | ||
if (navigator.languages !== undefined) | |||
return navigator.languages[0]; | |||
return navigator.language; | |||
} | } | ||
/* Because CSS variables didn't seem to work for changing the .date-[day/month]::before rules, | |||
// | this function manually trawls the stylesheets until it finds those rules and changes them */ | ||
function | function changeDateSeparator(component, separator) | ||
{ | { | ||
const styleSheets = document.styleSheets; | |||
var ruleIndex; | |||
for (var | for (var i = 0; i < styleSheets.length; i++) | ||
{ | |||
const rules = styleSheets[i].cssRules || styleSheets[i].rules; | |||
for (var j = 0; j < rules.length; j++) | |||
{ | { | ||
if (rules[j].selectorText === '.mw-parser-output .date-'+component+'::before') | |||
{ | |||
ruleIndex = j; | |||
break; | |||
} | |||
} | } | ||
if (ruleIndex !== undefined) | |||
{ | { | ||
// Update the content of the existing rule | |||
styleSheets[i].deleteRule(ruleIndex); | |||
styleSheets[i].insertRule('.date-'+component+'::before{content:"'+separator+'";}', ruleIndex); | |||
break; | |||
} | } | ||
} | |||
} | } | ||
function localizeDates() | |||
function | |||
{ | { | ||
const lang = getLang(); | |||
const root = document.querySelector(':root'); | |||
if (root == undefined || lang == undefined) | |||
return; | |||
if (lang != "en-US") // Template:LocaleDate prints dates in U.S.-style by default | |||
{ | |||
root.style.setProperty('--month-order', '2'); | |||
root.style.setProperty('--day-order', '1'); | |||
changeDateSeparator('month', '/'); | |||
changeDateSeparator('day', ''); | |||
} | |||
} | } | ||
$(localizeDates); | |||
Latest revision as of 22:03, 5 May 2025
/* JavaScript brought over from Wikipedia appears first. JS code written for our wiki comes second, under the heading CUSTOM ONI WIKI ADDITIONS. */ /* WIKIPEDIA CARRY-OVERS */ /* The original code on Wikipedia's Common.js page should be checked once in a while for updates to, or removal of, this code */ /** extract a URL parameter from the current URL ********** * From [[en:User:Lupin/autoedit.js]] * * paramName : the name of the parameter to extract */ function getURLParamValue(paramName, url) { if (typeof (url) == 'undefined' || url === null) url = document.location.href; var cmdRe = RegExp('[&?]' + paramName + '=([^&#]*)'); // Stop at hash var m = cmdRe.exec(url); if (m && m.length > 1) return decodeURIComponent(m[1]); return null; } /** &withJS= URL parameter ******* * Allow to try custom scripts from MediaWiki space * without editing [[Special:Mypage/monobook.js]] */ var extraJS = getURLParamValue("withJS"); if (extraJS && extraJS.match("^MediaWiki:[^&<>=%]*\.js$")) { mw.loader.load('/w/index.php?title=' + extraJS + '&action=raw&ctype=text/javascript'); } // TODO: Get this working again /* If we're on the Search page, load the script that augments that page */ if (mw.config.get('wgPageName') === 'Special:Search') { mw.loader.load('/w/index.php?title=MediaWiki:Common.js/search.js&action=raw&ctype=text/javascript'); } /** * Collapsible tables; reimplemented with mw-collapsible * Styling is also in place to avoid FOUC * * Allows tables to be collapsed, showing only the header. See [[Help:Collapsing]]. * @version 3.0.0 (2018-05-20) * @source https://www.mediawiki.org/wiki/MediaWiki:Gadget-collapsibleTables.js * @author [[User:R. Koot]] * @author [[User:Krinkle]] * @author [[User:TheDJ]] * @deprecated Since MediaWiki 1.20: Use class="mw-collapsible" instead which * is supported in MediaWiki core. Shimmable since MediaWiki 1.32 * * @param {jQuery} $content */ function makeCollapsibleMwCollapsible( $content ) { var $tables = $content .find( 'table.collapsible:not(.mw-collapsible)' ) .addClass( 'mw-collapsible' ); $.each( $tables, function ( index, table ) { // mw.log.warn( 'This page is using the deprecated class collapsible. Please replace it with mw-collapsible.'); if ( $( table ).hasClass( 'collapsed' ) ) { $( table ).addClass( 'mw-collapsed' ); // mw.log.warn( 'This page is using the deprecated class collapsed. Please replace it with mw-collapsed.'); } } ); if ( $tables.length > 0 ) { mw.loader.using( 'jquery.makeCollapsible' ).then( function () { $tables.makeCollapsible(); } ); } } mw.hook( 'wikipage.content' ).add( makeCollapsibleMwCollapsible ); /** * Add support to mw-collapsible for autocollapse, innercollapse and outercollapse * * Maintainers: TheDJ */ function mwCollapsibleSetup( $collapsibleContent ) { var $element, $toggle, autoCollapseThreshold = 2; $.each( $collapsibleContent, function ( index, element ) { $element = $( element ); if ( $element.hasClass( 'collapsible' ) ) { $element.find( 'tr:first > th:first' ).prepend( $element.find( 'tr:first > * > .mw-collapsible-toggle' ) ); } if ( $collapsibleContent.length >= autoCollapseThreshold && $element.hasClass( 'autocollapse' ) ) { $element.data( 'mw-collapsible' ).collapse(); } else if ( $element.hasClass( 'innercollapse' ) ) { if ( $element.parents( '.outercollapse' ).length > 0 ) { $element.data( 'mw-collapsible' ).collapse(); } } // because of colored backgrounds, style the link in the text color // to ensure accessible contrast $toggle = $element.find( '.mw-collapsible-toggle' ); if ( $toggle.length ) { // Make the toggle inherit text color (Updated for T333357 2023-04-29) if ( $toggle.parent()[ 0 ].style.color ) { $toggle.css( 'color', 'inherit' ); $toggle.find( '.mw-collapsible-text' ).css( 'color', 'inherit' ); } } } ); } mw.hook( 'wikipage.collapsibleContent' ).add( mwCollapsibleSetup ); /* CUSTOM ONI WIKI ADDITIONS */ /* Focus us on the Search box if we're on the Main Page, so we can immediately start typing */ if (mw.config.get('wgIsMainPage')) { $(function() { $('#searchInput').focus(); }); } /** Hover tables ********************************************************* * * Description: Allows tables to use mouse-over to provide explanation of * the pointed-to content. * Maintainers: [[User:Iritscen]] */ function showDescrip(typeID, show_or_not) { var DescripPanel = document.getElementsByClassName("hovertable_descrip")[0]; /* only get 1st one on page to keep things simple */ var Descrips = DescripPanel.getElementsByTagName("span"); if (!DescripPanel || !Descrips) return false; for (var i = 0; i < Descrips.length; i++) { if (Descrips[i].id == typeID) { if (show_or_not) Descrips[i].style.display = "block"; else Descrips[i].style.display = "none"; } } } function initHoverTables() { var Tables = document.getElementsByClassName("hovertable"); if (!Tables) return false; for (var i = 0; i < Tables.length; i++) { var Cells = Tables[i].getElementsByTagName("td"); if (!Cells) continue; for (var j = 0; j < Cells.length; j++) { if ($(Cells[j]).hasClass("hovercell")) { $(Cells[j]).mouseover(new Function("evt", "showDescrip(this.id, true);")); $(Cells[j]).mouseout(new Function("evt", "showDescrip(this.id, false);")); } } } } $(initHoverTables); /** Hover GIFs ********************************************************* * * Description: Allows GIFs to only animate upon mouse-over. * * Maintainers: [[User:Iritscen]] */ function swapImage(gifID, show_gif) { var gif_span = document.getElementById(gifID); var gif_image = gif_span.getElementsByClassName("image")[0]; var gif_img = gif_image.getElementsByTagName("img")[0]; var gif_src = gif_img.src; if (!gif_img) return false; if (show_gif) gif_img.src = gif_src.replace('.jpg', '.gif'); else gif_img.src = gif_src.replace('.gif', '.jpg'); } function initHoverGIFs() { var gifs = document.getElementsByClassName("hovergif"); if (!gifs) return false; for (var i = 0; i < gifs.length; i++) { $(gifs[i]).mouseover(swapImage(this.id, true)); $(gifs[i]).mouseout(swapImage(this.id, false)); } } $(initHoverGIFs); /***** Auto-sorting tables ******** * Auto-sorts sortable tables by a given column (why is this not built-in?!) * You must opt into this feature by placing "autosort" in the list of * the table's classes along with "sortable" * * Maintainers: [[User:Iritscen]] */ // For some reason, the arrow buttons and headerSort classes are not attached to sortable tables immediately upon the "load" event, and we need those, so we wait a bit before trying to sort function sortTimer() { setTimeout(function() { sortSortableTables(); }, (1 * 1000)); } function sortSortableTables() { console.log("Sorting the autosort tables."); // Iterate over all <div> elements var divs = document.getElementsByTagName("div"); if (!divs) return; for (var i = 0; i < divs.length; i++) { var theDiv = divs[i]; var tables = theDiv.getElementsByTagName("table"); if (!tables) continue; for (var j = 0; j < tables.length; j++) { var theTable = tables[j]; // If we found a sortable table that is asking for autosort... if ($(theTable).hasClass("sortable") && $(theTable).hasClass("autosort")) { var sortColumnNum = 1, curColumnNum = 0; // First check if there is any by-column-x class at all, then take the time to figure out the value of x var patt = /by-column/; if (patt.test(theTable.className)) { for (var col = 1; col < 10; col++) { var colOption = "by-column-" + col; if ($(theTable).hasClass(colOption)) { //console.log("Table " + j + " wants to sort by column " + col); sortColumnNum = col; // Now look for sort button and click it var allTHs = theTable.getElementsByTagName("th"); if (!allTHs) { console.log("Failed to get 'th' elements!"); continue; } for (var k = 0; k < allTHs.length; k++) { //console.log("Class names for 'th' # " + k + " are " + allTHs[k].className); if ($(allTHs[k]).hasClass("headerSort")) { curColumnNum++; if (curColumnNum == sortColumnNum) { console.log("Clicking sort button (ID " + k + ") for column " + sortColumnNum); $(allTHs[k]).trigger("click"); // use jQuery's trigger() to send click event to this arrow return; } } } } } } } } } } $(sortTimer); /***** Auto-formatting dates ******** * Re-formats a date placed by [[Template:LocaleDate]] to match the * month/day order of the user's locale. * * Maintainers: [[User:Iritscen]] */ function getLang() { if (navigator.languages !== undefined) return navigator.languages[0]; return navigator.language; } /* Because CSS variables didn't seem to work for changing the .date-[day/month]::before rules, this function manually trawls the stylesheets until it finds those rules and changes them */ function changeDateSeparator(component, separator) { const styleSheets = document.styleSheets; var ruleIndex; for (var i = 0; i < styleSheets.length; i++) { const rules = styleSheets[i].cssRules || styleSheets[i].rules; for (var j = 0; j < rules.length; j++) { if (rules[j].selectorText === '.mw-parser-output .date-'+component+'::before') { ruleIndex = j; break; } } if (ruleIndex !== undefined) { // Update the content of the existing rule styleSheets[i].deleteRule(ruleIndex); styleSheets[i].insertRule('.date-'+component+'::before{content:"'+separator+'";}', ruleIndex); break; } } } function localizeDates() { const lang = getLang(); const root = document.querySelector(':root'); if (root == undefined || lang == undefined) return; if (lang != "en-US") // Template:LocaleDate prints dates in U.S.-style by default { root.style.setProperty('--month-order', '2'); root.style.setProperty('--day-order', '1'); changeDateSeparator('month', '/'); changeDateSeparator('day', ''); } } $(localizeDates);