MediaWiki:Common.js

From Rise of Cultures Wiki
Jump to navigation Jump 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.
// load various utilities from the commons wiki, including l10nFactory and isEditorActive
mw.loader.getScript( 'https://commons.wiki.gg/MediaWiki:Common-base.js?action=raw\u0026ctype=text/javascript' ).then(function(){
////////////////////////////////////////////////////////////////////////////////
/* Now we can use l10nFactory and isEditorActive  /
/* Example for l10nFactory:
var l10n = l10nFactory(mw.config.get('wgUserLanguage'), {
	l10n_key {
		'en': 'en_text',
		'de': 'de_text',
		'zh': 'zh_text',
	},
	l10n_key2 {
		'en': 'en_text2',
		'zh': 'zh_text2',
	}
});
var text = l10n('l10n_key');
*/


////////////////////////////////////////////////////////////////////////////////
/*end of .then() callback*/ });

mw.loader.load("https://commons.wiki.gg/wiki/MediaWiki:DiscordIntegrator.js?action=raw\u0026ctype=text/javascript");


$(document).ready(function() {
    // Menu toggle
    $('<div class="menu-toggle"/>').insertAfter($('#p-logo')).on("click", function(event){
        event.stopPropagation();
        $(this).toggleClass('expanded');
    });

    // Function to update time for a specific element
    function updateTime(timeUTC, elementId) {
        // Step 1: Parse the fixed UTC time
        var utcHour = parseInt(timeUTC.split(':')[0]);
        var utcMinute = parseInt(timeUTC.split(':')[1]);

        // Step 2: Get the user's timezone and offset
        var userTimezone = new Date().toLocaleTimeString('en', { timeZoneName: 'short' }).split(' ')[2];
        // Replace "GMT" with "UTC"
        if (userTimezone.includes('GMT')) {
            userTimezone = userTimezone.replace('GMT', 'UTC');
        }
        if (elementId === 'time-1') {
        userTimezone = userTimezone.replace('UTC', 'DST: UTC');
    	}
        if (elementId === 'time-2') {
        userTimezone = userTimezone.replace('UTC', 'ST: UTC');
    	}
        var now = new Date();
        var userOffset = -1 * now.getTimezoneOffset(); // in minutes

        // Step 3: Calculate the user's local time
        var localHour = utcHour + Math.floor(userOffset / 60); // Add hours
        var localMinute = utcMinute + userOffset % 60; // Add minutes
        // Adjust for overflow and underflow minutes
        if (localMinute < 0) {
            localHour -= 1;
            localMinute += 60;
        }
        if (localMinute >= 60) {
            localHour += 1;
            localMinute -= 60;
        }
	    // Adjust for overflow and underflow hours
	    if (localHour < 0) {
	        localHour += 24;
	    }
	    if (localHour >= 24) {
	        localHour -= 24;
	    }

        // Step 4: Format the time in HH:MM format
        var formattedTime = (localHour < 10 ? '0' : '') + localHour + ':' + (localMinute < 10 ? '0' : '') + localMinute;

        // Step 5: Update the HTML content
        var rewardElement = document.getElementById(elementId);
        if (rewardElement) {
            rewardElement.textContent = formattedTime + ' (' + userTimezone + ')';
        }
    }

    // Call updateTime function for each time
    updateTime('22:00', 'time-1');
    updateTime('23:00', 'time-2');
    updateTime('00:00', 'time-3');
    updateTime('07:00', 'time-4');
    updateTime('12:00', 'time-5');
});


////////////////////////////////////////////////////////////////////////////////
 // modified version of https://dev.fandom.com/wiki/MediaWiki:TZclock.js
/**
 * Implement a timezone-adjusted clock
 * . Looks for elements with class "js-tzclock"
 * . Supports any timezone, not just the user's or UTC
 * . Supports multiple clocks per page
 * . Supports optional daylight saving time
 *
 * Configuration is adapted from tzdata format
 * . Optional comments begin with # (pound/octothorpe)
 * . Spaces in strings must use _ (underscore)
 * .   Underscore is replaced with a space when running
 * . + (plus) is optional for positive time offsets
 * . Basic zone definition must come before any rules
 * . Rules, if there are any, must be in chronological order
 * . NAME is location name displayed in clock (any string)
 * . UTCOFF is offset from UTC ([+|-]hh[:mm])
 * . ZONE is the timezone name (any string)
 * . IN is the month name for a rule (3-letter in English)
 * . ON is the date (numerical date, lastDay, or Day>=date)
 * .   If used, Day is 3-letter in English
 * . AT is the standard time at which the rule takes effect (24-hour)
 * . SAVE is the amount of time added to standard time (hh[:mm])
 * . LETTERS is the zone name when the rule is in effect (any string)
 *
 * Example clock configurations:
 *
 * <div class="js-tzclock"><nowiki>
 * # NAME          UTCOFF  ZONE
 * New_York        -5:00   EST
 * # IN    ON      AT      SAVE    LETTERS
 * Mar     Sun>=8  2:00    1       EDT # 2nd Sunday in March
 * Nov     Sun>=1  2:00    0       EST # 1st Sunday in November
 * </nowiki></div>
 *
 * <div class="js-tzclock"><nowiki>
 * # NAME          UTCOFF  ZONE
 * London          0:00    GMT
 * # IN    ON      AT      SAVE    LETTERS
 * Mar     lastSun 1:00    1       BST # last Sunday in March
 * Oct     lastSun 1:00    0       GMT # last Sunday in October
 * </nowiki></div>
 *
 * <div class="js-tzclock"><nowiki>
 * # NAME          UTCOFF  ZONE
 * Tokyo           9:00    JST # no daylight time in Japan
 * </nowiki></div>
 *
 * <div class="js-tzclock"><nowiki>
 * # NAME          UTCOFF  ZONE
 * Adelaide        +9:30   CST
 * # IN    ON      AT      SAVE    LETTERS
 * Apr     Sun>=1  2:00    0       CST # 1st Sunday in April
 * Oct     Sun>=1  2:00    1       CDT # 1st Sunday in October
 * </nowiki></div>
 *
 * <nowiki> ... </nowiki> may not be essential
 *   for all clock configurations, but it is recommended
 *   to stop MediaWiki from interfering with them
 */
;(function ($) {
    'use strict';

    var msg;
    var
        clock = []; // all clock data

    // calculate day of week using Zeller's algorithm
    // 0 = Saturday, ..., 6 = Friday
    function zeller(d, m, y) {
        var
            Y = y - (m < 3 ? 1 : 0),
            c = Math.floor(Y / 100),
            w;

        m += (m < 3) ? 12 : 0;
        y = Y % 100;
        w = (d + Math.floor(13 * (m + 1) / 5) + y +
            Math.floor(y / 4) + Math.floor(c / 4) - 2 * c) % 7;
        return (w < 0) ? w + 7 : w;
    }

    // convert [+|-]h[:m] to seconds
    function parseHM(s) {
        var
            sign = s.charAt(0) === '-' ? -1 : 1;

        s = (s.replace(/[+\-]/, '') + ':0').split(':');
        return sign * (parseInt(s[0], 10) * 60 + parseInt(s[1], 10)) * 60;
    }

    // parse the daylight saving rule for the given year
    // return epoch seconds (local time)
    function parseRule(year, rule) {
        var
            inMonth = rule[0],
            onDay = rule[1],
            atHour = rule[2],
            week = ['Sat', 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri'],
            m = $.inArray(inMonth, [
                'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
                'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
            ]) + 1,
            last = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][m - 1],
            d;

        if ((m === 2) &&
            (((year % 100 !== 0) && (year % 4 === 0)) || (year % 400 === 0))) {
            // leap year, in the unlikely event daylight saving switches in Feb
            last = 29;
        }
        if (/^\d+$/.test(onDay)) {
            // pure number
            d = parseInt(onDay, 10);
        } else if (onDay.substr(0, 4) === 'last') {
            // last day of week of month
            d = last;
            d -= (zeller(d, m, year) + 7 - $.inArray(onDay.substr(4), week)) % 7;
        } else if (onDay.substr(3, 2) === '>=') {
            // day of week at/after date
            d = parseInt(onDay.substr(5), 10);
            d += ($.inArray(onDay.substr(0, 3), week) + 7 - zeller(d, m, year)) % 7;
        } else {
            // error
            return;
        }
        // ISO format the date and return epoch seconds
        // atHour is local time, so the return is local time, despite the Z
        return Date.parse(
            year.toString() + '-' + ('0' + m.toString()).substr(-2) +
                '-' + ('0' + d.toString()).substr(-2) + 'T00:00:00Z'
        ) / 1000 + atHour;
    }

    // read tz configs from clock elements
    // populate the clock global object
    // data = jQuery collection of clock elements
    // NB: strings inserted into the DOM with $().text()
    //   CANNOT be escaped per OWASP XSS recommendations,
    //   because $().text() uses document.createTextNode,
    //   which does not unescape the entities
    // NB: text nodes are unparsed by the DOM engine
    //   hence there is no XSS or injection risk
    // NB: literal strings that use $().text are
    //   name (0), zone (2), and letters (7 & 12)
    function getConfig(data) {
        var
            i;

        data.each(function (_, e) {
            var
                text = $(e).text()
                    .replace(/#.*?(\n|$)/g, '$1') // remove all comments
                    .replace(/\s+/g, ' ')         // compress whitespace
                    .replace(/^\s|\s$/g, '')      // trim
                    .split(' '),                  // tokenize
                c;

            // process tokens, sanitizing them along the way
            if ((text.length === 3) || (text.length === 13)) {
                // basic zone definition
                clock.push({
                    e: $(e),
                    name: text[0]           // location description
                        .replace(/_/g, ' '),  // translate spaces
                    utcoff: parseHM(text[1] // [+|-]hh:mm UTC offset
                        .replace(/[^+\-\d:]/g, '')
                    ),
                    zone: text[2]           // zone designator
                        .replace(/_/g, ' '),  // translate spaces
                });
                c = clock[clock.length - 1];
                if (text.length === 13) {
                    // daylight time rules
                    c.year = 1; // truthy, but purposefully wrong
                    c.rule1 = [
                        text[3]         // in month (Jan - Dec)
                            .replace(/[^a-zA-Z]/g, ''),
                        text[4]         // on day (number, last, >=)
                            .replace(/[^a-zA-Z>=\d]/g, ''),
                        parseHM(text[5] // at time hh:mm
                            .replace(/[^\d:]/g, '')
                        )
                    ];
                    c.rule2 = [
                        text[8]          // in month (Jan - Dec)
                            .replace(/[^a-zA-Z]/g, ''),
                        text[9]          // on day (number, last, >=)
                            .replace(/[^a-zA-Z>=\d]/g, ''),
                        parseHM(text[10] // at time hh:mm
                            .replace(/[^\d:]/g, '')
                        )
                    ];
                    c.save1 = parseHM(text[6]  // daylight adjust hh:mm
                        .replace(/[^\d:]/g, '')
                    );
                    c.save2 = parseHM(text[11] // daylight adjust hh:mm
                        .replace(/[^\d:]/g, '')
                    );
                    c.letters1 = text[7]   // zone designator
                        .replace(/_/g, ' '); // translate spaces
                    c.letters2 = text[12]  // zone designator
                        .replace(/_/g, ' '); // translate spaces
                }
            } else {
                // error
                $(e).empty().text(msg('error').plain());
            }
        });
    }

    // handle timer event
    function onTick() {
        var
            now = Math.floor(Date.now() / 1000), // epoch in seconds
            time, year, i, c;

        for ( i = 0; i < clock.length; ++i ) {
            c = clock[i];
            time = now + c.utcoff; // local epoch
            year = (new Date(time * 1000)).getUTCFullYear();  // Date() takes msec
            if (c.year) {
                if (year !== c.year) {
                    // Happy New Year (maybe)
                    // (re)parse the rules for the calendar year
                    c.year = year;
                    c.switch1 = parseRule(year, c.rule1);
                    c.switch2 = parseRule(year, c.rule2);
                }
                // apply the rules
                if ((time >= c.switch1) && (time < c.switch2)) {
                    time += c.save1;
                    c.zone = c.letters1;
                } else {
                    time += c.save2;
                    c.zone = c.letters2;
                }
            }
            // mostly RFC-822/RFC-1123 time string
            // See comment about $().text() at getConfig()
            var thetime;
            if (window.TZclockSimpleFormat) {
                thetime = (new Date(time * 1000)).toUTCString()
                    .replace(/Sun, |Mon, |Tue, |Wed, |Thu, |Fri, |Sat, /, '') // remove day name
                    .replace(/\s\d\d\d\d/, ',')                               // remove year
                    .replace(/:\d\d\sGMT/, '');                               // remove seconds and GMT
            } else {
                thetime = (new Date(time * 1000)).toUTCString()
                    .replace(/\d{4}/, '$&,')                // add comma after year
                    .replace(/ 0/g, ' ')                    // no leading zero on date/hour
                    .replace(/UT|GMT/, '(' + c.zone + ')'); // "local" zone designation
            }
            
            $('.js-tzclock-time', c.e).text(thetime);
        }
        // set timeout for next tick
        setTimeout(onTick, 1100 - Date.now() % 1000);
    }

    // main routine
    // look for signature classes, init, and run
    function init($content) {
        var
            data = $content.find('.js-tzclock:not(.loaded)'),
            dom = String.prototype.concat(
                '<div class="js-tzclock-wrap">',
                    '<div class="js-tzclock-lctn"></div>',
                    '<div class="js-tzclock-time"></div>',
                '</div>'
            ),
            // Avoid nesting <div> in <span> - for valid HTML but also
            // to allow embedding the clock inline
            spanDom = String.prototype.concat(
                '<span class="js-tzclock-wrap">',
                    '<span class="js-tzclock-lctn"></span>',
                    '<span class="js-tzclock-time"></span>',
                '</span>'
            ),
            e, i;

        if (data.length) {
            data.addClass('loaded');
            getConfig(data);
            if (clock.length) {
                // init formats with names
                for ( i = 0; i < clock.length; ++i ) {
                    e = clock[i].e.empty().append(
                    	clock[i].e.prop('tagName') === 'SPAN' ? spanDom : dom);
                    // See comment about $().text() at getConfig()
                    $('.js-tzclock-lctn', e).text(clock[i].name);
                }
                // fake the first tick
                setTimeout(onTick);
            }
        }
    }
    
    $(function(){init($('#content'));});

})(jQuery);

 
///////////////////////////////////////////////////////////////////////////////////////////////////////////////

(function(mw, $) {

	/**
	 * Countdown
	 *
	 * @version 2.1
	 *
	 * @author Pecoes <https://c.wikia.com/wiki/User:Pecoes>
	 * @author Asaba <https://dev.wikia.com/wiki/User:Asaba>
	 *
	 * Version 1 authors:
	 * - Splarka <https://c.wikia.com/wiki/User:Splarka>
	 * - Eladkse <https://c.wikia.com/wiki/User:Eladkse>
	 *
	 * documentation and examples at:
	 * <https://dev.wikia.com/wiki/Countdown>
	 */

	/*jshint jquery:true, browser:true, devel:true, camelcase:true, curly:false, undef:true, bitwise:true, eqeqeq:true, forin:true, immed:true, latedef:true, newcap:true, noarg:true, unused:true, regexp:true, strict:true, trailing:false */
	/*global mediaWiki:true*/

	(function (module, mw, $, undefined) {

		'use strict';

		var translations = $.extend(true, {
				// English (English)
				en: {
					and: 'and',
					second: 'second',
					seconds: 'seconds',
					minute: 'minute',
					minutes: 'minutes',
					hour: 'hour',
					hours: 'hours',
					day: 'day',
					days: 'days'
				}
			}, module.translations || {}),
			i18n = translations[
				mw.config.get('wgContentLanguage')
				] || translations.en;

		var countdowns = [];

		var NO_LEADING_ZEROS = 1,
			SHORT_FORMAT = 2,
			NO_ZEROS = 4;

		function output (i, diff) {
			/*jshint bitwise:false*/
			var delta, result, parts = [];
			delta = diff % 60;
			result = ' ' + i18n[delta === 1 ? 'second' : 'seconds'];
			if (countdowns[i].opts & SHORT_FORMAT) result = result.charAt(1);
			parts.unshift(delta + result);
			diff = Math.floor(diff / 60);
			delta = diff % 60;
			result = ' ' + i18n[delta === 1 ? 'minute' : 'minutes'];
			if (countdowns[i].opts & SHORT_FORMAT) result = result.charAt(1);
			parts.unshift(delta + result);
			diff = Math.floor(diff / 60);
			delta = diff % 24;
			result = ' ' + i18n[delta === 1 ? 'hour'   : 'hours'  ];
			if (countdowns[i].opts & SHORT_FORMAT) result = result.charAt(1);
			parts.unshift(delta + result);
			diff = Math.floor(diff / 24);
			result = ' ' + i18n[diff  === 1 ? 'day'    : 'days'   ];
			if (countdowns[i].opts & SHORT_FORMAT) result = result.charAt(1);
			parts.unshift(diff  + result);
			result = parts.pop();
			if (countdowns[i].opts & NO_LEADING_ZEROS) {
				while (parts.length && parts[0][0] === '0') {
					parts.shift();
				}
			}
			if (countdowns[i].opts & NO_ZEROS) {
				parts = parts.filter(function(part) {
					return part[0] !== '0';
				});
			}
			if (parts.length) {
				if (countdowns[i].opts & SHORT_FORMAT) {
					result = parts.join(' ') + ' ' + result;
				} else {
					result = parts.join(', ') + ' ' + i18n.and + ' ' + result;
				}
			}
			countdowns[i].node.text(result);
		}

		function end(i) {
			var c = countdowns[i].node.parent();
			switch (c.attr('data-end')) {
				case 'remove':
					c.remove();
					return true;
				case 'stop':
					output(i, 0);
					return true;
				case 'toggle':
					var toggle = c.attr('data-toggle');
					if (toggle && toggle == 'next') {
						c.next().css('display', 'inline');
						c.css('display', 'none');
						return true;
					}
					if (toggle && $(toggle).length) {
						$(toggle).css('display', 'inline');
						c.css('display', 'none');
						return true;
					}
					break;
				case 'callback':
					var callback = c.attr('data-callback');
					if (callback && $.isFunction(module[callback])) {
						output(i, 0);
						module[callback].call(c);
						return true;
					}
					break;
			}
			countdowns[i].countup = true;
			output(i, 0);
			return false;
		}

		function update () {
			var now = Date.now();
			var countdownsToRemove = [];
			$.each(countdowns.slice(0), function (i, countdown) {
				var diff = Math.floor((countdown.date - now) / 1000);
				if (diff <= 0 && !countdown.countup) {
					if (end(i)) countdownsToRemove.push(i);
				} else {
					output(i, Math.abs(diff));
				}
			});
			var x;
			while((x = countdownsToRemove.pop()) !== undefined) {
				countdowns.splice(x, 1);
			}
			if (countdowns.length) {
				window.setTimeout(function () {
					update();
				}, 1000);
			}
		}

		function getOptions (node) {
			/*jshint bitwise:false*/
			var text = node.parent().attr('data-options'),
				opts = 0;
			if (text) {
				if (/no-leading-zeros/.test(text)) {
					opts |= NO_LEADING_ZEROS;
				}
				if (/short-format/.test(text)) {
					opts |= SHORT_FORMAT;
				}
				if (/no-zeros/.test(text)) {
					opts |= NO_ZEROS;
				}
			}
			return opts;
		}

		function init() {
			var countdown = $('.countdown:not(.handled)');
			if (!countdown.length) return;
			$('.nocountdown').css('display', 'none');
			countdown
				.css('display', 'inline')
				.find('.countdowndate')
				.each(function () {
					var $this = $(this),
						date = (new Date($this.text())).valueOf();
					if (isNaN(date)) {
						$this.text('BAD DATE');
						return;
					}
					countdowns.push({
						node: $this,
						opts: getOptions($this),
						date: date,
					});
				});
			countdown.addClass('handled');
			if (countdowns.length) {
				update();
			}
		}

		mw.hook('wikipage.content').add(init);

	}(window.countdownTimer = window.countdownTimer || {}, mediaWiki, jQuery));

	// End Countdown


})(mediaWiki, jQuery);