MediaWiki:Common.js
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/** <nowiki> <---- Please leave this alone
* This Javascript file is automatically loaded for all users on
* this wiki. Please make sure you have done sufficient testing
* in your own user Javascript namespace (Special:MyPage/skin.js/
* Special:MyPage/common.js).
*
* Please try to keep additions to this file at a minimum, and if
* possible, add those code snippets as a gadget. Gadgets are
* optimized to use ResourceLoader modules, and can be enabled by
* default for all users.
*
* Please also remember to add sufficient code comments to explain
* what you are doing for others. Also, use tabs and not spaces,
* and follow the coding conventions of MediaWiki as well.
*/
/* DOM abbreviation function */
function newNode(tagname) {
var node = document.createElement( tagname );
for ( var i=1; i<arguments.length; i++ ) {
if ( typeof arguments[i] == 'string' ) {
// Text
node.appendChild( document.createTextNode( arguments[i] ) );
} else if ( typeof arguments[i] == 'object' ) {
if ( arguments[i].nodeName ) {
// If it is a DOM Node
node.appendChild( arguments[i] );
} else {
// Attributes (hopefully)
for ( var j in arguments[i] ) {
if ( j == 'class' ) {
// Classname different because...
node.className = arguments[i][j];
} else if ( j == 'style' ) {
// Style is special
node.style.cssText = arguments[i][j];
} else if ( typeof arguments[i][j] == 'function' ) {
// Basic event handlers
try {
// W3C
node.addEventListener(j,arguments[i][j],false);
} catch (e) {
try {
// MSIE
node.attachEvent( 'on'+j, arguments[i][j], "Language" );
} catch (e) {
// Legacy
node['on'+j]=arguments[i][j];
}
}
} else {
// Normal attributes
node.setAttribute( j,arguments[i][j] );
}
}
}
}
}
return node;
}
/*** END of DOM abbreviation function ***/
/* getCookies, setCookies */
// Note: This is deprecated and replaced by jQuery.cookie,
// but it is here for backward compatibility
/* @deprecated: Use $.cookie instead */
function setCookie(cookieName, cookieValue) {
if (window.console) {
console.warn("deprecated function setCookie called; use jQuery.cookie instead");
}
var today = new Date();
var expire = new Date();
var nDays = 30;
expire.setTime( today.getTime() + (3600000 * 24 * nDays) );
document.cookie = cookieName + "=" + escape(cookieValue) + ";path=/" + ";expires="+expire.toGMTString();
// We need to delete the more specific paths for the next while. Safe to remove this by July 2011, if not before.
document.cookie = cookieName + "=" + ";path=/w" + ";expires=Thu, 01-Jan-1970 00:00:01 GMT";
document.cookie = cookieName + "=" + ";path=/wiki" + ";expires=Thu, 01-Jan-1970 00:00:01 GMT";
}
function getCookie(cookieName) {
if (window.console) {
console.warn("deprecated function getCookie called; use jQuery.cookie instead");
}
var start = document.cookie.indexOf( cookieName + "=" );
if ( start == -1 ) return "";
var len = start + cookieName.length + 1;
if ( ( !start ) && ( cookieName != document.cookie.substring( 0, cookieName.length ) ) ) {
return "";
}
var end = document.cookie.indexOf( ";", len );
if ( end == -1 ) end = document.cookie.length;
return unescape( document.cookie.substring( len, end ) );
}
function deleteCookie(cookieName) {
if ( getCookie(cookieName) ) {
document.cookie = cookieName + "=" + ";path=/" + ";expires=Thu, 01-Jan-1970 00:00:01 GMT";
}
}
/* Dynamic Navigation Bars */
var NavigationBarHide = 'hide ▲';
var NavigationBarShow = 'show ▼';
// set up max count of Navigation Bars on page,
// if there are more, all will be hidden
// NavigationBarShowDefault = 0; // all bars will be hidden
// NavigationBarShowDefault = 1; // on pages with more than 1 bar all bars will be hidden
NavigationBarShowDefault = 0;
/* NavigationBar::toggleNavigationBar */
// shows and hides content and picture (if available) of navigation bars
// Parameters:
// indexNavigationBar: the index of navigation bar to be toggled
function toggleNavigationBar(indexNavigationBar) {
var NavToggle = document.getElementById( "NavToggle" + indexNavigationBar );
var NavFrame = document.getElementById( "NavFrame" + indexNavigationBar );
if ( !NavFrame || !NavToggle ) {
return false;
}
// if shown now
if ( NavToggle.isHidden == false ) {
for ( var NavChild = NavFrame.firstChild; NavChild; NavChild = NavChild.nextSibling ) {
if ( NavChild.className == 'NavPic' ) {
NavChild.style.display = 'none';
} else if ( NavChild.className == 'NavContent' ) {
NavChild.style.display = 'none';
}
}
NavToggle.childNodes[1].firstChild.nodeValue = NavigationBarShow;
NavToggle.isHidden = true;
// if hidden now
} else if ( NavToggle.isHidden == true ) {
for ( var NavChild = NavFrame.firstChild; NavChild; NavChild = NavChild.nextSibling ) {
if ( NavChild.className == 'NavPic' ) {
NavChild.style.display = 'block';
} else if ( NavChild.className == 'NavContent' ) {
NavChild.style.display = 'block';
}
}
NavToggle.childNodes[1].firstChild.nodeValue = NavigationBarHide;
NavToggle.isHidden = false;
}
}
/* NavigationBar::createNavigationBarToggleButton */
var wgNavBarArray = [];
// adds show/hide-button to navigation bars
function createNavigationBarToggleButton() {
// Are we previewing an translation section?
var preview = document.getElementById( 'wikiPreview' );
if ( preview != null ) {
var p = preview.getElementsByTagName('p');
if ( p != null && p.length >= 2 && p[1].firstChild.id == 'Translations' ) {
NavigationBarShowDefault = 999;
}
}
var indexNavigationBar = 0;
// iterate over all < div >-elements
for ( var i=0; NavFrame = document.getElementsByTagName("div")[i]; i++ ) {
// if found a navigation bar
// (Note that a navigation bar is a div whose *sole* class is
// "NavFrame". That might not be the best approach, but currently
// {{trans-see}} exploits it, so be cautious in changing that.)
if ( NavFrame.className == "NavFrame" ) {
indexNavigationBar++;
var NavToggle = document.createElement("span");
NavToggle.className = 'NavToggle';
NavToggle.setAttribute( 'id', 'NavToggle' + indexNavigationBar );
NavToggle.appendChild( document.createTextNode('[') );
NavToggle.appendChild( document.createElement("a") );
var NavToggleText = document.createTextNode( NavigationBarHide );
// These need an href, but must do nothing
NavToggle.childNodes[1].setAttribute( 'href', 'javascript:(function(){})()' );
NavToggle.childNodes[1].appendChild( NavToggleText );
NavToggle.appendChild(document.createTextNode(']'));
NavToggle.isHidden = false;
wgNavBarArray[indexNavigationBar - 1] = NavToggle;
// Find the NavHead and attach the toggle link (Must be this complicated because Moz's firstChild handling is borked)
for ( var j=0; j < NavFrame.childNodes.length; j++ ) {
if ( NavFrame.childNodes[j].className == "NavHead" ) {
var NavHead = NavFrame.childNodes[j];
for ( var k=0; k < NavHead.childNodes.length; k++ ) {
if (NavHead.childNodes[k].nodeType == 1) {
NavHead.childNodes[k].onclick = function (e) {
if (e) {
e.stopPropagation ()
} else {
window.event.cancelBubble = true;
}
}
}
}
NavHead.style.cursor = "pointer";
NavHead.onclick = ( function (i) { return function () { toggleNavigationBar(i); } } )( indexNavigationBar )
if ( NavHead.childNodes[0] ) {
NavHead.insertBefore(NavToggle,NavHead.childNodes[0]);
} else {
NavHead.appendChild(NavToggle);
}
}
}
NavFrame.setAttribute('id', 'NavFrame' + indexNavigationBar);
}
}
// if more Navigation Bars found than Default: hide all
if ( NavigationBarShowDefault < indexNavigationBar ) {
for ( var i=1; i<=indexNavigationBar; i++ ) {
toggleNavigationBar(i);
}
}
}
$( createNavigationBarToggleButton );
/*** END of Dynamic Navigation Bars ***/
/**
* jQuery UI on-demand loader
* Loads jQuery UI modules on demand and allows users making use of
* (some) of the awesome jQuery UI widgets (such as in Template:Clickable button)
*
* Note: This code is from Commons' MediaWiki:Common.js
*/
mw.hook( 'wikipage.content' ).add( function($c) {
var $accordion = $( '.accordion', $c ),
$button = $( '.ui-button', $c );
if ( $accordion.length ) {
mw.loader.using( 'jquery.ui', function () {
$accordion.accordion( { autoHeight: false } );
} );
}
if ( $button.length ) {
mw.loader.load( 'jquery.ui' );
}
} );
/*** END of jQuery UI on-demand loader ***/
/*******************************************************************************
* Visibility Toggling *
* *
* Often, you want certain content to be hidden by default, with a link to show *
* or re-hide it (i.e., to toggle its visibility). This bit of code provides *
* infrastructure to help you do that in a consistent way. The idea is that *
* there are certain "categories" of hidden content: "synonyms", for example. *
* So, in addition to letting users toggle the visibility of a *specific* bit *
* of content, we also want them to be able to toggle the visibility of an *
* entire *category* of content. And we want this to be "sticky": if the user *
* desides to show all synonyms, then that should be stored in a cookie, so *
* that synonyms *stay* shown. This bit of code handles all that. *
* *
* Still, you have to handle a lot yourself: *
* *
* - You have to write the code that finds all the hideable bits of content. *
* - For each hideable bit of content, you have to create a show/hide link. *
* (But this part is pretty easy. The hardest part is putting the link in *
* the right place.) *
* - For a given hideable bit of content, you have to write a function to hide *
* it and a function to show it. (These functions should also set the text *
* of the show/hide link.) *
* *
* Once you've done that, you just find all the hideable bits of content; for *
* each one, you create a show/hide link, and you call *
* *
* VisibilityToggles.register(category, show_function, hide_function) *
* *
* and set the show/hide link's "onClick" attribute to whatever that returns. *
*******************************************************************************/
var VisibilityToggles = {
// toggles[category] = [[show, hide],...]; statuses[category] = [true, false,...]; buttons = <li>
toggles: {}, statuses: {}, buttons: null,
// Add a new toggle, adds a Show/Hide category button in the toolbar,
// and will call show_function and hide_function once on register, and every alternate click.
register: function (category, show_function, hide_function) {
var id = 0;
if (!this.toggles[category]) {
this.toggles[category] = [];
this.statuses[category] = [];
} else {
id = this.toggles[category].length;
}
this.toggles[category].push([show_function, hide_function]);
this.statuses[category].push(this.currentStatus(category));
this.addGlobalToggle(category);
(this.statuses[category][id] ? show_function : hide_function)();
return function () {
var statuses = VisibilityToggles.statuses[category];
statuses[id] = !statuses[id]
VisibilityToggles.checkGlobalToggle(category);
return (statuses[id] ? show_function : hide_function)();
}
},
// Add a new global toggle to the side bar
addGlobalToggle: function(category) {
if (document.getElementById('p-visibility-'+category))
return;
if (!this.buttons) {
this.buttons = newNode('ul');
var collapsed = $.cookie("vector-nav-p-visibility") == "false", toolbox = newNode('div', {'class': "portal portlet "+(collapsed?"collapsed":"expanded"), 'id': 'p-visibility'},
newNode('h3', 'Visibility'),
newNode('div', {'class': "pBody body"}, collapsed?undefined:{'style':'display:block;'}, this.buttons)
);
var sidebar = document.getElementById('mw-panel') || document.getElementById('column-one');
var insert = null;
if (insert = (document.getElementById('p-lang') || document.getElementById('p-feedback')))
sidebar.insertBefore(toolbox, insert);
else
sidebar.appendChild(toolbox);
}
var status = this.currentStatus(category);
var newToggle = newNode('li', newNode('a', {
id: 'p-visibility-' + category,
style: 'cursor: pointer',
href: '#visibility-' + category,
click: function(e)
{
VisibilityToggles.toggleGlobal(category);
if (e && e.preventDefault)
e.preventDefault();
else
window.event.returnValue = false;
return false;
}},
(status ? 'Hide ' : 'Show ') + category));
for (var i=0; i < this.buttons.childNodes.length; i++) {
if (this.buttons.childNodes[i].id < newToggle.id) {
this.buttons.insertBefore(newToggle, this.buttons.childNodes[i]);
return;
}
}
this.buttons.appendChild(newToggle);
},
// Update the toggle-all buttons when all things are toggled one way
checkGlobalToggle: function(category) {
var statuses = this.statuses[category];
var status = statuses[0];
for (var i = 1; i < statuses.length; i++) {
if (status != statuses[i])
return;
}
document.getElementById('p-visibility-' + category).innerHTML = (status ? 'Hide ' : 'Show ') + category;
},
// Toggle all un-toggled elements when the global button is clicked
toggleGlobal: function(category) {
var status = document.getElementById('p-visibility-' + category).innerHTML.indexOf('Show ') == 0;
for (var i = 0; i < this.toggles[category].length; i++ ) {
if (this.statuses[category][i] != status) {
this.toggles[category][i][status ? 0 : 1]();
this.statuses[category][i] = status;
}
}
document.getElementById('p-visibility-' + category).innerHTML = (status ? 'Hide ' : 'Show ') + category;
var current = getCookie('Visibility');
if (!current)
current = ";";
current = current.replace(';' + category + ';', ';');
if (status)
current = current + category + ";";
setCookie('Visibility', current);
},
currentStatus: function(category) {
if (window.location.hash.toLowerCase().split('_')[0] == '#' + category.toLowerCase())
return true;
if (window.location.href.search(/[?](.*&)?hidecats=/) > 0)
{
var hidecats = window.location.href;
hidecats = hidecats.replace(/^[^?]+[?]((?!hidecats=)[^&]*&)*hidecats=/, '');
hidecats = hidecats.replace(/&.*/, '');
hidecats = hidecats.split(',');
for (var i = 0; i < hidecats.length; ++i)
if (hidecats[i] == category || hidecats[i] == 'all')
return false;
else if (hidecats[i] == '!' + category || hidecats[i] == 'none')
return true;
}
if (getCookie('WiktionaryPreferencesShowNav') == 'true')
return true;
if (getCookie('Visibility').indexOf(';' + category + ';') >= 0)
return true;
// TODO check category-specific cookies
return false;
}
};
/*******************************************************************************
* Hidden 'Onyms *
* *
* This bit of code allows 'onyms (synonyms or antonyms or whatnot) to be *
* hidden (collapsed) by default. For each hidden group of 'onyms, there will *
* be a link to show it (which, once clicked, becomes a link to re-hide it). *
* *
* This code makes use of the "Visibility Toggling" infrastructure (see above), *
* so it creates toolbar-links and cookies and whatnot as appropriate. It uses *
* separate categories for different types of 'onyms; for example, all synonyms *
* can be shown or hidden at once. *
* *
* To use it, create a template on the model of {{synonyms}} (q.v.). The *
* category is controlled by the class-name of the <ul> element (for example, *
* class="onyms-collapse onyms-collapse-synonyms" assigns the list to the *
* "synonyms" category in the toolbar), and the text of the show/hide link is *
* controlled by the bold text at the head of the first <li> element. *
*******************************************************************************/
function setupHiddenOnyms(onyms)
{
var li = onyms.parentNode;
var endOfLine = li.firstChild;
while(true)
{
if(endOfLine == null) // should never happen
break;
if(endOfLine.nodeName.search(/^(dl|ul|ol|menu)$/i) > -1)
break;
endOfLine = endOfLine.nextSibling;
}
var HQToggle = newNode('a', {href: 'javascript:(function(){})()'}, '');
var toggleLink = newNode('span', {'class': 'HQToggle', 'style': 'font-size:0.65em'}, ' [', HQToggle, ']');
if(endOfLine == null) // should never happen
li.appendChild(toggleLink);
else
li.insertBefore(toggleLink, endOfLine);
var category = onyms.className.match(/(^| )onyms-collapse-([^ ]+)/)[2];
var text = onyms.getElementsByTagName('b')[0].innerHTML.replace(/:/g, '').toLowerCase();
HQToggle.onclick =
(
VisibilityToggles.register
(
category,
function show()
{
HQToggle.innerHTML = text + ' ▲';
onyms.style.display = '';
},
function hide()
{
HQToggle.innerHTML = text + ' ▼';
onyms.style.display = 'none';
}
)
);
}
function initHiddenOnyms() {
var ols = document.getElementsByTagName('ol');
for(var i = 0; i < ols.length; ++i) {
for(var li = ols[i].firstChild; li !== null; li = li.nextSibling) {
if(li.nodeName.toUpperCase() != 'LI') {
continue;
}
var uls = li.getElementsByTagName('ul');
for(var j = 0; j < uls.length; ++j) {
if(uls[j].parentNode == li && $(uls[j]).hasClass('onyms-collapse')) {
setupHiddenOnyms(uls[j]);
}
}
}
}
}
$(document).ready(initHiddenOnyms);
/*** NOTHING BELOW THIS LINE </nowiki> ***/