MediaWiki:Common.js

From Arknights Terra 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.
/**
 * For language wikis, use
 * <code> mw.loader.load("https://arknights.wiki.gg/load.php?lang=en&modules=site&only=scripts&skin=vector"); </code>
 * as the content of common.js to import from this script.
 *
 */

///////////////////////////////////////////////////////////////////////////////////// 
 // 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);

 
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 /**
 *  Modified version of dev.fandom.com/wiki/MediaWiki:Tooltips.js
 *  Require some css is in common.css
 */
var tooltips = {
	debug: false,

	api: false,
	types: [],
	classes: ['basic-tooltip', 'advanced-tooltip'],
	advancedCounter: 1,

	events: [],
	timeouts: [],

	offsetX: 20,
	offsetY: 20,
	waitForImages: false,

	init: function() {
		//if($(document.body).hasClass('mw-special-InfoboxBuilder')) return;
		if(location.search.search(/ttdebug=(1|[Tt]rue)/) != -1 || (typeof tooltips_debug != 'undefined' && tooltips_debug)) tooltips.debug = true;
		//var href = (new mw.Uri($('link[rel="canonical"]').attr('href'))).path;
		var href = (new mw.Uri($('meta[property="og:url"]').attr('content'))).path;
		if(typeof href == 'undefined' || !href) {
			console.log('Tooltips: script couldn\'t find required  link[rel="canonical"]  tag');
			tooltips.disabled = true;
			return false;
		}
		href = href.split('/wiki/');
		tooltips.api = href[0]+'/api.php?format=json&action=parse&disablelimitreport=true&prop=text&title='+href[1];
		if(mw.util.getParamValue('uselang')) tooltips.api += '&uselang='+mw.util.getParamValue('uselang');
		// Cache tooltip contents on the CDN for 10 minutes for anonymous users
		tooltips.api += '&maxage=600&smaxage=600';
		tooltips.api += '&text=';

		tooltips.types['basic-tooltip'] = {};
		tooltips.types['advanced-tooltip'] = {};

		if(!tooltips.config()) {
			console.log('Tooltips: missing config');
			tooltips.disabled = true;
			return false;
		}

		var content = $('#mw-content-text');

		if($('#tooltip-wrapper').length === 0) $('<div id="tooltip-wrapper"></div>').appendTo(document.body);
		if($('#tooltip-storage').length === 0) $('<div id="tooltip-storage"></div>').append('<div class="main-tooltip tt-basic-tooltip" id="tooltip-basic-tooltip">Lorem ipsum dolor sit amet</div>').appendTo(content);

		$('#tooltip-wrapper')
			.css({'position':'fixed','height':'auto','min-height':'0','z-index': 6000000})
			.hide();

		$('#tooltip-storage')
			.css({'height':'0px','min-height':'0','visibility':'hidden','overflow':'hidden','position':'static'});

		$('#tooltip-basic-tooltip').data('type', 'basic-tooltip');

		tooltips.applyTooltips(document);

		mw.hook('wikipage.content').add(function(elem) {
			tooltips.applyTooltips($(elem));
		});

		if(typeof tooltips.events == 'string') tooltips.events = [tooltips.events];
		for(var x=0; x<tooltips.events.length; x++) { $(window).on(tooltips.events[x], function(ev, elem) { tooltips.applyTooltips(elem || this) }) }

		if(tooltips.debug) {
			$('#tooltip-wrapper').css({'background-color':'rgba(255,0,0,0.2)'});
			$('#tooltip-storage').css({'background-color':'rgba(0,255,0,0.2)','height':'500px','overflow-y':'scroll','visibility':'visible'});
		}
	},
	config: function() {
		if(typeof tooltips_list != 'undefined') {
			$(tooltips_list).each(function(i, v) { tooltips.addType(v) });
		}
		if(typeof tooltips_config == 'object') {
			tooltips.offsetX = tooltips_config.offsetX || tooltips.offsetX;
			tooltips.offsetY = tooltips_config.offsetY || tooltips.offsetY;
			tooltips.waitForImages = (tooltips_config.waitForImages || tooltips.waitForImages) && true;
			tooltips.events = tooltips_config.events || tooltips.events;
		}

		return true;
	},
	applyTooltips: function(elem) {
		$(elem).find('.'+tooltips.classes.join(', .')).each(function() {
			$this = $(this);
			if($this.hasClass('tooltips-init-complete')) return;

			$this.find('*').removeAttr('title');
			$this.mouseover(tooltips.handlers.mouseOver);
			$this.mouseout(tooltips.handlers.mouseOut);
			$this.mousemove(tooltips.handlers.mouseMove);

			$this.data('tooltip-contents', $(this).attr('title'));
			$this.removeAttr('title');

			tooltips.advancedTooltip($this);

			$(this).addClass('tooltips-init-complete');
		});
	},
	advancedTooltip: function(elem) {
		elem = $(elem);
		if(!elem.hasClass('advanced-tooltip')) return;
		var tips = elem.find('.tooltip-contents');
		if(!tips.length) return;
		var tip = $('<div class="main-tooltip tt-advanced-tooltip"></div>').attr('id', 'tooltip-advanced-tooltip-'+tooltips.advancedCounter).appendTo('#tooltip-storage').data('type', 'advanced-tooltip').append($(tips[0]).contents()).each(tooltips.calcSize);
		tips.remove();
		elem.data('tooltip-id-advanced-tooltip', tooltips.advancedCounter);
		tooltips.advancedCounter++;
	},
	addType: function(tt) {
		if(typeof tooltips.types[tt.classname] == 'undefined') {
			var obj = {};

			if(typeof tt.parse == 'string' || typeof tt.parse == 'function') var parse = tt.parse; else var parse = false;
			if(typeof tt.text == 'string' || typeof tt.text == 'function') var text = tt.text; else var text = false;

			if(parse) {
				obj.text = parse;
				obj.parse = true;
			} else if(text) {
				obj.text = text;
				obj.parse = false;
			} else return;

			if(typeof obj.text == 'string') obj.parameters = tooltips.getParameters(obj.text); else obj.parameters = [];

			if(typeof tt.delay == 'string' || typeof tt.delay == 'number') obj.delay = parseInt(tt.delay); else obj.delay = false;
			if(typeof tt.onParsed == 'function') obj.onParsed = tt.onParsed;
			if(typeof tt.onShow == 'function') obj.onShow = tt.onShow;
			if(typeof tt.onHide == 'function') obj.onHide = tt.onHide;

			tooltips.types[tt.classname] = obj;
			if(tooltips.classes.indexOf(tt.classname) == -1) tooltips.classes.push(tt.classname);
		} else {
			if(typeof tt.delay == 'string' || typeof tt.delay == 'number') tooltips.types[tt.classname].delay = parseInt(tt.delay);
			if(typeof tt.onParsed == 'function') tooltips.types[tt.classname].onParsed = tt.onParsed;
			if(typeof tt.onShow == 'function') tooltips.types[tt.classname].onShow = tt.onShow;
			if(typeof tt.onHide == 'function') tooltips.types[tt.classname].onHide = tt.onHide;
		}
	},
	getParameters: function(text) {
		var list = [];
		var matches = text.match(/<#\s*[a-z0-9_\-]+?\s*#>/gi);
		if(matches) {
			for(var x=0; x<matches.length; x++) {
				list.push(/<#\s*([a-z0-9_\-]+?)\s*#>/i.exec(matches[x])[1]);
			}
		}
		return list;
	},
	getAPI: function(text) {
		return tooltips.api+encodeURIComponent(text);
	},
	getText: function(type, elem) {
		if(typeof tooltips.types[type].text == 'function') {
			var text = tooltips.types[type].text($(elem)[0]);
		} else {
			var text = tooltips.types[type].text;
			for(var x=0; x<tooltips.types[type].parameters.length; x++) {
				var param = tooltips.types[type].parameters[x];
				var value = $(elem).data(param);
				if(typeof value == 'undefined') value = '';
				var rx = new RegExp('<#\\s*'+param.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&")+'\\s*#>', 'g');
				text = text.replace(rx, value);
			}
		}
		return text;
	},
	getTooltip: function(type, elem) {
		elem = $(elem);
		if(elem.data('tooltip-id-'+type)) return $('#tooltip-'+type+'-'+elem.data('tooltip-id-'+type));

		var text = tooltips.getText(type, elem);
		var id = tooltips.hash(text);
		elem.data('tooltip-id-'+type, id);

		var tip = $('#tooltip-'+type+'-'+elem.data('tooltip-id-'+type));
		if(tip.length) return tip;

		tip = $('<div class="main-tooltip"></div>').attr('id', 'tooltip-'+type+'-'+id).appendTo('#tooltip-storage').data('type', type).addClass('tt-'+type);

		tooltips.wrapperPosition(tooltips.lastKnownMousePos[0], tooltips.lastKnownMousePos[1]);
		tooltips.sameWidth();

		if(!tooltips.types[type].parse) {
			tip.html(text).each(tooltips.calcSize);
			tooltips.wrapperPosition(tooltips.lastKnownMousePos[0], tooltips.lastKnownMousePos[1]);
			tooltips.sameWidth();
		} else {
			tip.addClass('tooltip-loading');
			var api = tooltips.getAPI(text);
			if(tooltips.debug) tip.html('<pre style="padding:2px 3px;font-size:12px;">'+api+'</pre>');
			tip.attr('title', api);
			$.ajax({
				url: api,
				dataType: 'json',
				context: tip,
				success: function(data, textStatus, jqXHR) {
					$(this).html(data['parse']['text']['*']).each(tooltips.calcSize);
					tooltips.wrapperPosition(tooltips.lastKnownMousePos[0], tooltips.lastKnownMousePos[1]);
					tooltips.sameWidth();
					var images = $(this).find('img');
					images.fadeTo(0, 0).one('load', function() {
						if(tooltips.waitForImages) {
							$(this).fadeTo(0,1);
							$(this).addClass('image-loaded');
							tip = $(this).closest('.main-tooltip');
							if(tip.find('img').length == tip.find('img.image-loaded').length) {
								tip.removeClass('tooltip-loading').each(tooltips.calcSize);
								tooltips.wrapperPosition(tooltips.lastKnownMousePos[0], tooltips.lastKnownMousePos[1]);
								tooltips.sameWidth();
							}
						} else $(this).fadeTo(100,1);
					});
					if(tooltips.waitForImages) {
						if(images.length === 0) {
							$(this).removeClass('tooltip-loading').each(tooltips.calcSize);
						}
					} else {
						$(this).removeClass('tooltip-loading').each(tooltips.calcSize);
					}
					var type = $(this).data('type') || false;
					if(type && typeof tooltips.types[type].onParsed == 'function') {
						tooltips.types[type].onParsed.call(this);
						tip.each(tooltips.calcSize);
					}
					if($(this).find('a.new').length > 0) $(this).addClass('has-redlinks');
					tooltips.wrapperPosition(tooltips.lastKnownMousePos[0], tooltips.lastKnownMousePos[1]);
					tooltips.sameWidth();
				}
			});
		}
		return tip;
	},
	getBasicTooltip: function(elem) {
		return $("#tooltip-basic-tooltip").html(mw.html.escape($(elem).data('tooltip-contents')).replace(/\\n/g,'<br />')).each(tooltips.calcSize);
	},
	getAdvancedTooltip: function(elem) {
		return $("#tooltip-advanced-tooltip-"+$(elem).data('tooltip-id-advanced-tooltip'));
	},
	getTooltips: function(elem) {
		elem = $(elem);
		var classes = elem.attr('class').split(' ');
		var tips = [];
		for(var i=0;i<classes.length;i++) {
			var tip = false;
			if(classes[i] == 'advanced-tooltip') tip = tooltips.getAdvancedTooltip(elem);
			else if(classes[i] == 'basic-tooltip') tip = tooltips.getBasicTooltip(elem);
			else if(typeof tooltips.types[classes[i]] != 'undefined') tip = tooltips.getTooltip(classes[i], elem);
			if(tip) tips.push(tip[0]);
		}
		return $(tips);
	},
	setOwnWidth: function() {
		$this = $(this);
		if(typeof $this.data('width') != 'undefined') $this.css('width', $this.data('width')+'px');
		else $this.css('width', '');
	},
	calcSize: function() {
		$this = $(this);
		$this.css('position', 'absolute');
		var temp = $this.css('width');
		$this.css('width', '');
		$this.data('width', parseFloat(window.getComputedStyle($this[0]).width));
		$this.data('height', parseFloat(window.getComputedStyle($this[0]).height));
		$this.data('outerwidth', $this.outerWidth(true));
		$this.data('outerheight', $this.outerHeight(true));
		$this.css('width', $this.data('width')+'px');
		$this.css('position', '');
		$this.css('width', temp);
	},
	sameWidth: function() {
		var $wrapper = $("#tooltip-wrapper");
		if($wrapper.find('.main-tooltip').length === 1) {
			$("#tooltip-wrapper").find('.main-tooltip').each(tooltips.setOwnWidth);
		} else {
			var width = 0;
			$wrapper.find('.main-tooltip').each(function() { width = Math.max(width, $(this).data('width') || 0); });
			$wrapper.find('.main-tooltip').each(function() { $(this).css('width', width+'px'); });
		}
	},
	wrapperPosition: function(mouseX, mouseY) {
		var $wrapper = $("#tooltip-wrapper");
		var $window = $(window);
		var tipH = parseInt($wrapper.css('padding-top')) + parseInt($wrapper.css('padding-bottom'));
		var tipW = 0;
		var barH = $('#wikigg-header').height();

		$wrapper.find('.main-tooltip').each( function(){ if(typeof $(this).data('outerheight') != 'undefined') tipH += $(this).data('outerheight'); });
		$wrapper.find('.main-tooltip').each( function(){ if(typeof $(this).data('outerwidth') != 'undefined') tipW = Math.max(tipW, $(this).data('outerwidth') + parseInt($("#tooltip-wrapper").css('padding-left')) + parseInt($("#tooltip-wrapper").css('padding-right'))); });

		mouseX = mouseX - $window.scrollLeft();
		mouseY = mouseY - $window.scrollTop();

		var spaceTop = mouseY - tooltips.offsetY - barH;
		var spaceLeft = mouseX - tooltips.offsetX;
		var spaceRight = $(window).width() - mouseX - tooltips.offsetX;
		var spaceBottom = $(window).height()  - mouseY - tooltips.offsetY;

		var coordX = mouseX + tooltips.offsetX;
		var coordY = mouseY + tooltips.offsetY;

		if(spaceRight < tipW && spaceLeft >= tipW){
			coordX = mouseX - tipW - tooltips.offsetX;
		}
		if(spaceBottom < tipH && spaceTop >= tipH){
			coordY = mouseY - tipH - tooltips.offsetY;
		}

		coordX = Math.min(coordX, $(window).width() - tipW);
		coordY = Math.min(coordY, $(window).height() - tipH);

		if ($wrapper.css('position') !== 'fixed') {
			coordX = coordX+$(window).scrollLeft();
			coordY = coordY+$(window).scrollTop();
		}
		
		coordX = Math.floor(coordX);
		coordY = Math.floor(coordY);

		$wrapper.css({left: coordX + 'px', top: coordY + 'px'});
	},
	handlers: (window.innerWidth || document.documentElement.clientWidth) > 725 ? {
		mouseOver: function(e) {
			tooltips.lastKnownMousePos = [e.pageX, e.pageY];
			tooltips.wrapperPosition(e.pageX, e.pageY);

			var tips = tooltips.getTooltips(this);
			$("#tooltip-wrapper").prepend(tips).show();
			tooltips.sameWidth();

			var handle = this;
			tips.each(function() {
				var $this = $(this);
				var type = $(this).data('type') || false;

				$this.show();
				$(window).trigger('scroll');// trigger image lazy loader
				if(type && typeof tooltips.types[type] != 'undefined' && tooltips.types[type].delay) {
					$this.hide();
					tooltips.timeouts[$(this).attr('id')] = setTimeout(function(){
						$this.show();
						if(type && typeof tooltips.types[type].onShow == 'function') tooltips.types[type].onShow.call($this[0], handle);
					}, tooltips.types[type].delay);
				} else if(type && typeof tooltips.types[type].onShow == 'function') tooltips.types[type].onShow.call(this, handle);
			});
		},
		mouseOut: function(e) {
			tooltips.lastKnownMousePos = [e.pageX, e.pageY];
			tooltips.wrapperPosition(e.pageX, e.pageY);

			var handle = this;
			$("#tooltip-wrapper").hide();
			$("#tooltip-wrapper").find('.main-tooltip').appendTo('#tooltip-storage').each(function() {
				var type = $(this).data('type') || false;
				if(type && typeof tooltips.types[type].onHide == 'function') tooltips.types[type].onHide.call(this, handle);
				$(this).show();
				clearTimeout(tooltips.timeouts[$(this).attr('id')]);
				delete tooltips.timeouts[$(this).attr('id')];
			});
		},
		mouseMove: function(e) {
			tooltips.lastKnownMousePos = [e.pageX, e.pageY];
			tooltips.wrapperPosition(e.pageX, e.pageY);
		}
	} : undefined,
	hash: function(text) {
		/* Source: https://archive.is/nq2F9 */
		var hash = 0, i, char;
		if (text.length === 0) return hash;
		for (i = 0, l = text.length; i < l; i++) {
			char  = text.charCodeAt(i);
			hash  = ((hash<<5)-hash)+char;
			hash |= 0; // Convert to 32bit integer
		}
		return hash;
	},
};
$(tooltips.init);
/* TOOLTIPS
----first number represents description; blank = no description, 2 = has desc----
----second number represents number of effects
*/

window.tooltips_list = [
	{
		classname: 'item-tooltip',
		parse: '{' + '{Template:Item tip|1=<#name#>|2=<#title#>|3=<#tier#>|4=<#use#>|5=<#desc#>|6=<#obtain#>|7=<#firstdrop#>|8=<#regdrop#>|9=<#specdrop#>|10=<#base#>|11=<#cond#>}}',
	},  {
		classname: 'enemy-tooltip',
		parse: '{' + '{Template:Enemy tip|1=<#name#>|2=<#title#>|3=<#code#>|4=<#race#>|5=<#type#>}}',
	},  {
		classname: 'character-tooltip',
		parse: '{' + '{Template:Character tip|1=<#name#>|2=<#title#>|3=<#class#>|4=<#branch#>|5=<#star#>|6=<#faction#>}}',
	},	{
		classname: 'collectible-tooltip',
		parse: '{' + '{Template:Collectible tip|1=<#name#>|2=<#title#>|3=<#num#>|4=<#qlt#>|5=<#use#>|6=<#desc#>|7=<#theme#>|8=<#cond#>}}',
	},	{
		classname: 'furniture-tooltip',
		parse: '{' + '{Template:Furniture tip|1=<#name#>|2=<#title#>|3=<#use#>|4=<#desc#>|5=<#ambience#>|6=<#cat#>|7=<#theme#>|8=<#set#>|9=<#obtain#>|10=<#firstdrop#>|11=<#regdrop#>}}',
	},	{
		classname: 'outfit-tooltip',
		parse: '{' + '{Template:Outfit tip|1=<#name#>|2=<#model#>|3=<#num#>|4=<#artist#>|5=<#use#>|6=<#quote#>|7=<#series#>|8=<#desc#>|9=<#obtain#>}}',
	},	{
		classname: 'glossary',
		parse: '{' + '{Template:Glossary tip|1=<#name#>|2=<#title#>|3=<#desc#>}}',
	},	{
		classname: 'profile-picture-tooltip',
		parse: '{' + '{Template:Profile picture tip|1=<#name#>|2=<#desc#>|3=<#obtain#>}}',
	}
];

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

(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); 

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

mw.loader.getScript( 'https://commons.wiki.gg/index.php?title=MediaWiki:Common-base.js&action=raw&ctype=text/javascript' ).then(function(){
////////////////////////////////////////////////////////////////////////////////
	$('<div class="menu-toggle"/>').insertAfter($('#p-logo')).on("click", function(event){
		event.stopPropagation();
		$(this).toggleClass('expanded');
	});
////////////////////////////////////////////////////////////////////////////////
/*end of mw.loader.getScript().then callback*/ });