MediaWiki:Common.js
From Guild Wars 2 Wiki
Jump to navigationJump to search
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)
- Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Press Ctrl-F5.
/* MEDIAWIKI:COMMON.JS */
console.log('[[MediaWiki:Common.js]] has loaded (revision 2025-03-29 16:44).');
// Wrapper to address Extension:MobileFrontend deficiencies
// Note that the retrieved skin will be lowercase but this will be OK for requests
var sk = mw.config.get('skin');
if ('globalVariableFromMobileJS' in window) {
console.log('Common: mw.util function - scenario 1.');
// Address case for global variable has been set by [[MediaWiki:Mobile.js]] directing to this script
// Add [[MediaWiki:Common.css]] stylesheet
mw.loader.load( '/index.php?title=MediaWiki:Common.css&action=raw&ctype=text/css', 'text/css' );
// Add skin specific stylesheet
mw.loader.load( '/index.php?title=MediaWiki:' + sk + '.css&action=raw&ctype=text/css', 'text/css' );
loadCommonJS();
} else if (typeof mw.config.get('wgMFAmc') === 'object' && sk !== 'minerva') {
console.log('Common: mw.util function - scenario 2.');
// Address case for desktop and non-minerva skin
loadCommonJS();
} else if (typeof mw.config.get('wgMFAmc') === 'object' && sk === 'minerva') {
console.log('Common: mw.util function - scenario 3.');
// Address case for desktop and minerva
// Remove common.css stylesheet - note this depends on user preference
var l1 = document.querySelector("link[href='/load.php?lang=en&modules=site.styles&only=styles&skin=minerva']"); if (l1) { l1.remove(); }
var l2 = document.querySelector("link[href='/load.php?lang=es&modules=site.styles&only=styles&skin=minerva']"); if (l2) { l2.remove(); }
var l3 = document.querySelector("link[href='/load.php?lang=fr&modules=site.styles&only=styles&skin=minerva']"); if (l3) { l3.remove(); }
var l4 = document.querySelector("link[href='/load.php?lang=de&modules=site.styles&only=styles&skin=minerva']"); if (l4) { l4.remove(); }
// skip loading rest of JS
} else {
console.log('Common: mw.util function - scenario 4.');
// Address remaining desktop platform options with mobile display
// skip loading rest of JS
}
function loadCommonJS() {
console.log('Common: loadCommonJS function loaded.');
// rest of common.js goes here
if (document.readyState === 'loading') {
// Loading hasn't finished yet (status loading)
console.log('Common: loadCommonJS function - dom content at loading state, adding event listener to trigger commonJS function.', document.readyState);
document.addEventListener('DOMContentLoaded', commonJS);
} else {
// 'DOMContentLoaded' has already fired
console.log('Common: loadCommonJS function - dom content either at complete or interactive state, proceeding to commonJS function.');
commonJS();
}
function commonJS() {
console.log('Common: commonJS function executed.');
// Semantic Mediawiki Gallery overlay bug fix
$.support.opacity = true;
/**
* Additional scripts specified here and on other MediaWiki pages
* collapsible tables from [[MediaWiki:CollapsibleTables.js]]
* ingame chatlink searches from [[MediaWiki:ChatLinkSearch.js]]
*/
// Scripts to use when viewing articles
if (mw.config.get('wgIsArticle') || window.location.href.indexOf('action=submit') > -1 || mw.config.get('wgNamespaceNumber') == -1) {
// Article namespace
if (mw.config.get('wgNamespaceNumber') === 0 ) {
addArticleFeedback(document);
demarcateDialogue();
gameUpdateIcons();
}
// Only if the page contains collapsible tables
if ( $('.collapsible, .expandable').length > 0 ) {
mw.loader.load( '/index.php?title=MediaWiki:CollapsibleTables.js&action=raw&ctype=text/javascript' );
}
tradingPostPrices()
autoConvertUTC();
}
// Script to use in the Special namespace only
if ( mw.config.get('wgNamespaceNumber') == -1 ) {
uploadEnforcer();
}
// Scripts to use when searching
if (mw.config.get('wgPageName') == 'Special:Search') {
mw.loader.load( '/index.php?title=MediaWiki:ChatLinkSearch.js&action=raw&ctype=text/javascript' );
}
/**
* Feedback button (see [[Help:Leaving article feedback]])
* Adds a button to leave feedback about a mainspace article on the talk page using a preloaded feedback format
*/
function addArticleFeedback (document) {
// Don't bother if its the Main Page as you can't submit feedback onto a locked talk page
if (mw.config.get('wgPageName') === 'Main_Page') {
return;
}
// Construct pretty date format with padded zeros YYYY-MM-DD
var currentDate = new Date();
var day = ('0' + currentDate.getDate().toString()).slice(-2);
var month = ('0' + (currentDate.getMonth() + 1 ).toString()).slice(-2);
var year = currentDate.getFullYear();
var currentDate = year + '%2F' + month + '%2F' + day;
// Construct new tab
feedbacktab = document.createElement('li');
feedbacktab.id = 'special-articlefeedback';
feedbacklink = '/index.php?title=Talk:' + encodeURIComponent(mw.config.get('wgPageName')) + '&action=edit§ion=new&editintro=Template:Feedback_notice&preload=Template:Feedback_preload&preview=yes&preloadtitle=Feedback+' + currentDate;
feedbackhover = 'Leave us feedback on the content of the article so that we can improve it';
feedbacktabtext = 'Leave article feedback';
// Change format depending on whether user is using vector or monobook
if (mw.config.get('skin') == 'vector') {
feedbacktab.innerHTML = '<a href="' + feedbacklink + '" title="' + feedbackhover + '"><span>' + feedbacktabtext + '</span></a>'
} else if (mw.config.get('skin') == 'monobook') {
feedbacktab.innerHTML = '<a href="' + feedbacklink + '" title="' + feedbackhover + '">' + feedbacktabtext + '</a>'
}
// Finally add the feedback button somewhere after the discussion tab
var talk = document.getElementById('ca-talk');
if (talk) {
talk.parentNode.insertBefore(feedbacktab, document.getElementById('ca-talk').nextSibling);
}
}
/** Display a clock. **/
/** Spliced from [[mw:MediaWiki:Gadget-LocalLiveClock.js]] and [[mw:MediaWiki:Gadget-UTCLiveClock.js]] **/
function displayClock() {
console.log('Common: displayClock function executed.');
mw.loader.using( ['mediawiki.util', 'mediawiki.api'] ).then( function () {
console.log('Common: displayClock function - mw.util and mw.api loaded.');
var $target;
function padWithZeroes( num ) {
// Pad a number with zeroes. The number must be an integer where
// 0 <= num < 100.
return num < 10 ? '0' + num.toString() : num.toString();
}
function showTime( $target ) {
var now = new Date();
// Set the Local time.
var hh = now.getHours();
var mm = now.getMinutes();
var ss = now.getSeconds();
var time = padWithZeroes( hh ) + ':' + padWithZeroes( mm ) + ':' + padWithZeroes( ss );
// Set the UTC time.
var uhh = now.getUTCHours();
var umm = now.getUTCMinutes();
var uss = now.getUTCSeconds();
var utime = padWithZeroes( uhh ) + ':' + padWithZeroes( umm ) + ':' + padWithZeroes( uss );
var string;
if (hh == uhh) {
string = time + ' UTC';
} else {
string = time + ' (' + utime + ' UTC)';
}
// Write to page.
$target.text( string );
// Schedule the next time change.
var ms = now.getUTCMilliseconds();
setTimeout( function () {
showTime( $target );
}, 1100 - ms );
}
function liveClock() {
// CSS styles are set in [[Mediawiki:Common.css]] - search for utcdate.
// Add the portlet link.
var node = mw.util.addPortletLink(
'p-personal',
mw.util.getUrl( null, { action: 'purge' } ),
'',
'utcdate',
null,
null,
'#pt-userpage, #pt-anonuserpage'
);
if ( !node ) {
return;
}
// Purge the page when the clock is clicked.
// If logged in, avoid the purge confirmation screen.
if ( $('#pt-userpage').length > 0 ) {
$( node ).on( 'click', function ( e ) {
new mw.Api().post( { action: 'purge', titles: mw.config.get( 'wgPageName' ) } ).then( function () {
location.reload();
}, function () {
mw.notify( 'Purge failed', { type: 'error' } );
} );
e.preventDefault();
} );
}
// Show the clock.
showTime( $( node ).find( 'a:first' ) );
}
$( liveClock );
} );
}
displayClock();
/**
* Trading post prices. (see [[Template:Tp]])
* Converts placeholder content of elements with class "gw2-tpprice" to trading post prices, using "data-info" attribute values (sell or buy) if available.
*/
function tradingPostPrices () {
console.log('Common: tradingPostPrices function loaded.');
if ( $('.gw2-tpprice').length === 0 ) {
return;
}
function pad (s) {
return (s < 10 ? '0' : '') + s;
}
function getCoin (coin, asHtml) {
if (coin === null || isNaN(coin)) {
return asHtml ? '<span class="numbers">?</span> <img src="/images/e/eb/Copper_coin.png" alt="Copper" />' : '?';
}
coin = Math.ceil(coin);
var copper = coin % 100;
var text = copper + 'c';
var html = '<span class="numbers">' + (coin >= 100 ? pad(copper) : copper ) + '</span> <img src="/images/e/eb/Copper_coin.png" alt="Copper" />';
if (coin >= 100) {
var silver = (coin / 100 >> 0) % 100;
html = '<span class="numbers">' + (coin >= 10000 ? pad(silver) : silver ) + '</span> <img src="/images/3/3c/Silver_coin.png" alt="Silver" /> ' + html;
text = silver + 's ' + text;
}
if (coin >= 10000) {
var gold = coin / 10000 >> 0;
html = '<span class="numbers">' + gold + '</span> <img src="/images/d/d1/Gold_coin.png" alt="Gold" /> ' + html;
text = gold + 'g ' + text;
}
return asHtml ? html : text;
}
// Helper function to find elements in A which are not in B. Usage: a.diff(b)
Array.prototype.diff = function(a) {
return this.filter(function(el) {
return a.indexOf(el) < 0;
});
};
var elements = {}, ids = [];
$('.gw2-tpprice').each(function () {
var id = +this.getAttribute('data-id');
var qty = 1;
if (this.hasAttribute('data-quantity')) {
qty = +this.getAttribute('data-quantity');
}
if (!id || parseInt(id) != id) {
return;
}
id = parseInt(id);
if (!elements[id]) {
elements[id] = [];
ids.push(id);
}
elements[id].push({
elem: this,
info: this.getAttribute('data-info'),
qty: qty
});
});
var promises = [];
for (var i=0; i < ids.length; i+=200) {
var current_ids = ids.slice(i,i+200);
promises.push({
request: $.getJSON('https://api.guildwars2.com/v2/commerce/prices?ids='+current_ids.join(',')+'&wiki=1&lang=en'),
requested_ids: current_ids,
observed_ids: [],
missing_ids: []
});
}
$.each(promises, function(p_index, p){
$.when(p.request)
.done(function(data){
$.each(data, function (index, item) {
// Partial success can mean that some valid ids were returned, but some were ignored, hence loop through original requests.
p.observed_ids.push(item.id);
var buyText = 'Highest individual buy order: ' + getCoin(item.buys.unit_price);
var sellText = 'Lowest individual sell offer: ' + getCoin(item.sells.unit_price);
$.each(elements[item.id], function () {
if (this.info == 'buy') {
this.elem.innerHTML = getCoin(item.buys.unit_price * this.qty, true);
this.elem.title = buyText + ' (' + item.buys.quantity + ' ordered)';
this.elem.setAttribute('data-sort-value', item.buys.unit_price * this.qty);
}
else if (this.info == 'sell') {
this.elem.innerHTML = getCoin(item.sells.unit_price * this.qty, true);
this.elem.title = sellText + ' (' + item.sells.quantity + ' listed)';
this.elem.setAttribute('data-sort-value', item.sells.unit_price * this.qty);
}
else {
this.elem.innerHTML = getCoin(item.sells.unit_price * this.qty, true);
this.elem.title = sellText + ' / ' + buyText;
this.elem.setAttribute('data-sort-value', item.sells.unit_price * this.qty);
}
if ((this.info == 'buy' && !item.buys.quantity) || (this.info != 'buy' && !item.sells.quantity)) {
this.elem.style.opacity = '0.5';
}
});
});
// Check for missing ids which were in the original request
p.missing_ids = p.requested_ids.diff(p.observed_ids);
$.each(p.missing_ids, function(index, id){
$.each(elements[id], function() {
this.elem.innerHTML = getCoin(0, true);
this.elem.title = 'No buy or sell orders found.'
this.elem.style.opacity = '0.5';
});
});
})
.fail( function(d, textStatus, error) {
console.log("Commerce API getJSON failed (request #" + p_index +"), status: " + textStatus + ", error: "+error, JSON.stringify(d.responseText));
$.each(p.requested_ids, function(index, id){
$.each(elements[id], function() {
this.elem.innerHTML = getCoin(null, true);
this.elem.title = 'Unable to retrieve buy or sell orders.'
this.elem.style.opacity = '0.5';
});
});
});
});
}
/**
* Convert UTC time to local time. (see [[Template:UTC time]])
*/
function autoConvertUTC () {
function pad (s) { return (s < 10 ? '0' : '') + s; }
var days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
$('.utc-auto-convert').each(function(i,v){
// Get UTC time using MediaWiki {{#time: U}} epoch format
var utcseconds = v.getAttribute('data-time');
if (utcseconds == 'error') {
return;
}
var d = new Date(0);
d.setUTCSeconds(utcseconds);
var offset = (-1 * d.getTimezoneOffset() / 60);
var offsetstring = '';
if (offset > 0) { offsetstring = '+' + offset; }
if (offset < 0) { offsetstring = offset; }
// Default to showing the time only
var datestring = pad(d.getHours()) + ':' + pad(d.getMinutes()) + ' UTC' + offsetstring;
var titlestring = pad(d.getUTCHours()) + ':' + pad(d.getUTCMinutes()) + ' UTC';
// But check for different formatting in case there is a day of the week given inside the span
if (!v.textContent.match(/^\d/)) {
datestring = days[d.getDay()] + ' ' + datestring;
titlestring = days[d.getUTCDay()] + ' ' + titlestring;
}
// Show result
$(v).html('<span style="cursor:help; border-bottom:1px dotted silver;" title="'+titlestring+'">'+datestring+'</span>');
});
}
/**
* Force users to select a license option when uploading files. Allows other summaries if it contains 'ArenaNet image' or 'User image' license templates.
*/
function uploadEnforcer () {
$('#mw-upload-form').submit(function(event) {
var licenseValue = $('#wpLicense').val();
var uploadDescription = $('#wpUploadDescription').val();
if (licenseValue == '' && !uploadDescription.match(/(Licensing|ArenaNet image|User image)/i)) {
alert('Please select a Licensing option.');
event.preventDefault();
}
});
}
/**
* Additional class formatting "dialogue" for sections titled as dialogue or text on mainspace articles
*/
function demarcateDialogue () {
if (mw.config.get('skin') !== 'minerva') {
$('h2').each(function (i, e) {
var h2Content = this.innerHTML.match(/(dialogue|text)/i);
if (h2Content) {
$(this).nextUntil(this.tagName).wrapAll('<div class="dialogue"></div>');
}
});
}
}
/**
* Increased line spacing for skill and trait icons on the "Game update" / "Upcoming changes and features" pages and subpages
*/
function gameUpdateIcons () {
if (mw.config.get('wgPageName').substring(0, 12) == 'Game_updates' || mw.config.get('wgPageName').substring(0, 29) == 'Upcoming_changes_and_features') {
$('li .skillicon, li .effecticon, li .traiticon').each(function(){
$(this).parent('li').addClass('patchnote');
});
}
}
/**
* Add user agent strings to the HTML element to make browser specific CSS simpler
*/
document.documentElement.setAttribute('data-useragent', navigator.userAgent);
}
}