MediaWiki:Common.js: Difference between revisions
(replacing obsolete hasClass() with $(element).hasClass()) |
(added JS support for dynamic locale-based date format) |
||
| (35 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 25: | Line 30: | ||
} | } | ||
// 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 */ | ||
if ( | |||
/* 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 108: | Line 144: | ||
} | } | ||
} | } | ||
function initHoverTables() | function initHoverTables() | ||
{ | { | ||
| Line 130: | Line 165: | ||
} | } | ||
} | } | ||
$(initHoverTables); | $(initHoverTables); | ||
| Line 154: | Line 188: | ||
gif_img.src = gif_src.replace('.gif', '.jpg'); | gif_img.src = gif_src.replace('.gif', '.jpg'); | ||
} | } | ||
function initHoverGIFs() | function initHoverGIFs() | ||
{ | { | ||
| Line 168: | Line 201: | ||
} | } | ||
} | } | ||
$(initHoverGIFs); | $(initHoverGIFs); | ||
/***** Auto-sorting tables ******** | |||
/** | |||
* Auto-sorts sortable tables by a given column (why is this not built-in?!) | * 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 | * You must opt into this feature by placing "autosort" in the list of | ||
| Line 517: | Line 210: | ||
* Maintainers: [[User:Iritscen]] | * 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() | function sortSortableTables() | ||
{ | { | ||
console.log("Sorting the autosort tables."); | |||
// Iterate over all <div> elements | // Iterate over all <div> elements | ||
var divs = document.getElementsByTagName("div"); | var divs = document.getElementsByTagName("div"); | ||
| Line 537: | Line 238: | ||
// First check if there is any by-column-x class at all, then take the time to figure out the value of x | // First check if there is any by-column-x class at all, then take the time to figure out the value of x | ||
if ( | var patt = /by-column/; | ||
if (patt.test(theTable.className)) | |||
{ | { | ||
for (var col = 1; col < 10; col++) | for (var col = 1; col < 10; col++) | ||
| Line 544: | Line 246: | ||
if ($(theTable).hasClass(colOption)) | if ($(theTable).hasClass(colOption)) | ||
{ | { | ||
//console.log("Table " + j + " wants to sort by column " + col); | |||
sortColumnNum = 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 569: | 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() | |||
{ | |||
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); | |||
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);