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 2023-08-20 10:36).'); // 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 document.querySelector("link[href='/load.php?lang=en&modules=site.styles&only=styles&skin=minerva']").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); } }