Difference between revisions of "MediaWiki:Common.js"

From Team Fortress Wiki
Jump to: navigation, search
m (Fix "addPortletLink" for user sandbox button sometimes returning undefined because of being called before the DOM is loaded)
(Behold! THE audio player!)
(6 intermediate revisions by one other user not shown)
Line 335: Line 335:
 
};
 
};
 
$(langFixes.init);
 
$(langFixes.init);
 +
 +
// Custom tooltip component. See Template:Tooltip
 +
var wikiTooltip = {
 +
    init: function() {
 +
        var $tooltips = $('.wiki-tooltip .wiki-tooltip-content');
 +
        if ($tooltips[0]) {
 +
            $tooltips.each(function() {
 +
                var $this = $(this);
 +
                function handler() {
 +
                    // Check for links and handle them on handheld devices
 +
                    if (!!$this.closest('a').attr('href')) {
 +
                        $this.closest('a').removeAttr('title');
 +
 +
                        widthRect <= 1000 ? wikiTooltip.hasLink($this) : wikiTooltip.removeAnchor($this);
 +
                    }
 +
                   
 +
                    // Prevent tooltips from overflowing the viewport
 +
                    var bounding = $this[0].getBoundingClientRect();
 +
                    edgeRect = {
 +
                        left: Math.floor(($this.width() + bounding.left + 10)),
 +
                        right: Math.floor(($this.width() + bounding.right + 10))
 +
                    };
 +
 +
                    widthRect = $(window).innerWidth();
 +
                    if (edgeRect.left >= (widthRect || document.documentElement.clientWidth)) {
 +
                        $this.css('left', '');
 +
                        $this.css('right', '50%');
 +
                    } else if (edgeRect.right <= (widthRect || document.documentElement.clientWidth)) {
 +
                        $this.css('right', '');
 +
                        $this.css('left', '50%');
 +
                    }
 +
                }
 +
                $(handler);
 +
                setTimeout(function() {
 +
                    $(window).on('resize', handler);
 +
                }, 250);
 +
            });
 +
        }
 +
    },
 +
    hasLink: function(tooltip) {
 +
        if (tooltip.find('a').length < 1) {
 +
            var $a = $('<a>');
 +
            var url = tooltip.closest('a');
 +
            var href = url.attr('href');
 +
 +
            $a.attr('href', href);
 +
            url.attr('href', 'javascript:void(0);');
 +
            tooltip.append($a);
 +
        }
 +
    },
 +
    removeAnchor: function(tooltip) {
 +
        var url = tooltip.closest('a');
 +
        var href = url.attr('href');
 +
 +
        innerLink = tooltip.find('a').attr('href');
 +
        if (innerLink) {
 +
            href = innerLink;
 +
            url.attr('href', href);
 +
            tooltip.find('a').remove();
 +
        } else {
 +
            return;
 +
        }
 +
    }
 +
};
 +
$(wikiTooltip.init);
  
 
// Logged-in body class injection
 
// Logged-in body class injection
Line 901: Line 966:
 
// Page-specific JavaScript/CSS
 
// Page-specific JavaScript/CSS
 
var pageScripts = {
 
var pageScripts = {
pagesJS: ['Main_Page', 'User:WindPower', 'User:MogDog66', 'User:WindPower/Main_Page', 'User:Lexar/Main_Page/Template:Benjas', 'User:Lexar/RandomPage', 'User:Lexar/responsive_infobox', 'User:Lexar/sandbox', 'User:Tark', 'User:Tark/Sandbox', 'Team_Fortress_Wiki:April_Fools\'_Day/2021/Main_Page', 'User:PhoneWave'],
+
pagesJS: ['Main_Page', 'User:WindPower', 'User:MogDog66', 'User:WindPower/Main_Page', 'User:Lexar/Main_Page/Template:Benjas', 'User:Lexar/RandomPage', 'User:Lexar/responsive_infobox', 'User:Lexar/sandbox', 'User:Tark', 'User:Tark/Sandbox', 'Team_Fortress_Wiki:April_Fools\'_Day/2021/Main_Page', 'User:PhoneWave', 'User:Wookipan/Sandbox'],
pagesCSS: ['Main_Page', 'User:WindPower', 'User:Pilk/armory', 'User:Pilk', 'User:Esky', 'User:Lagg', 'User:MogDog66', 'User:CrushBOT', 'User:MogDog66/userpagev2', 'User:NVis', 'User:NVis/Sandbox', 'User:Lexar', 'User:MogDog66/MPR', 'User:WindPower/Main_Page', 'User:Moussekateer/3DViewer', 'User:T-Wayne', 'User:FreeXMan', 'User:Nixshadow', 'User:Ath', 'User:Carez', 'User:Lexar/Main_Page/Template:Benjas', 'User:Lexar/RandomPage', 'User:MogDog66/Sandbox', 'User:Obilisk', 'User:Lexar/itembox_tooltip', 'User:Lexar/sandbox', 'User:Lexar/responsive_infobox', 'User:Hagbard Celine', 'User:Wookipan', 'User:GrampaSwood', 'Team_Fortress_Wiki:April_Fools\'_Day/2019/Main_Page', 'User:Tark', 'User:Tark/Sandbox', 'User:StargazerG', 'User:Boba', 'User:Boba/Projects', 'User:FanCyy', 'User:Dan_greene', 'User:Boba/Sandbox', 'User:Ashe', 'Team_Fortress_Wiki:April_Fools\'_Day/2021/Main_Page', 'User:PhoneWave', 'User:Foxbite'],
+
pagesCSS: ['Main_Page', 'User:WindPower', 'User:Pilk/armory', 'User:Pilk', 'User:Esky', 'User:Lagg', 'User:MogDog66', 'User:CrushBOT', 'User:MogDog66/userpagev2', 'User:NVis', 'User:NVis/Sandbox', 'User:Lexar', 'User:MogDog66/MPR', 'User:WindPower/Main_Page', 'User:Moussekateer/3DViewer', 'User:T-Wayne', 'User:FreeXMan', 'User:Nixshadow', 'User:Ath', 'User:Carez', 'User:Lexar/Main_Page/Template:Benjas', 'User:Lexar/RandomPage', 'User:MogDog66/Sandbox', 'User:Obilisk', 'User:Lexar/itembox_tooltip', 'User:Lexar/sandbox', 'User:Lexar/responsive_infobox', 'User:Hagbard Celine', 'User:Wookipan', 'User:Wookipan/Sandbox', 'User:GrampaSwood', 'Team_Fortress_Wiki:April_Fools\'_Day/2019/Main_Page', 'User:Tark', 'User:Tark/Sandbox', 'User:StargazerG', 'User:Boba', 'User:Boba/Projects', 'User:FanCyy', 'User:Dan_greene', 'User:Boba/Sandbox', 'User:Ashe', 'Team_Fortress_Wiki:April_Fools\'_Day/2021/Main_Page', 'User:PhoneWave', 'User:Foxbite'],
 
suffixJS: '/Page.js',
 
suffixJS: '/Page.js',
 
suffixCSS: '/Page.css',
 
suffixCSS: '/Page.css',
Line 1,371: Line 1,436:
 
$(logoLocales.init);
 
$(logoLocales.init);
 
// End logo switcher -----
 
// End logo switcher -----
 +
 +
// Start 'Audio player'
 +
var audioPlayer = {
 +
    currentAudio: null,
 +
 +
    init: function () {
 +
        $('.tfwiki-audio-player').each(function () {
 +
            var audioPlayerElement = $(this);
 +
            var audioLink = audioPlayerElement.children('a');
 +
            var audioURL = audioLink.attr('href');
 +
            var audio = null;
 +
            var audioStatus = audioPlayerElement.find('.tfwiki-audio-player-action');
 +
 +
audioStatus.removeClass('inactive');
 +
 +
            audioPlayerElement.on('click', function (e) {
 +
                if (e.target !== audioStatus[0]) {
 +
                    return;
 +
                }
 +
 +
                e.preventDefault();
 +
 +
                if (!audio) {
 +
                    audio = new Audio(audioURL);
 +
                    audio.volume = 0.5;
 +
                    audio.addEventListener('ended', function () {
 +
                        audioStatus.text(audioStatus.data('text-play'));
 +
                        audioStatus.removeClass('playing');
 +
                    });
 +
                }
 +
 +
                if (audioPlayer.currentAudio && audioPlayer.currentAudio !== audio) {
 +
                    audioPlayer.currentAudio.pause();
 +
                    audioPlayer.currentAudio.currentTime = 0;
 +
                    audioPlayer.currentAudioStatus.text(audioPlayer.currentAudioStatus.data('text-play'));
 +
                    audioPlayer.currentAudioStatus.removeClass('playing');
 +
                }
 +
 +
                if (audio.paused) {
 +
                    audio.play();
 +
                    audioStatus.text(audioStatus.data('text-pause'));
 +
                    audioPlayer.currentAudio = audio;
 +
                    audioPlayer.currentAudioStatus = audioStatus;
 +
                    audioStatus.addClass('playing');
 +
                } else {
 +
                    audio.pause();
 +
                    audioStatus.text(audioStatus.data('text-resume'));
 +
                    audioPlayer.currentAudio = null;
 +
                    audioPlayer.currentAudioStatus = null;
 +
                    audioStatus.removeClass('playing');
 +
                }
 +
            });
 +
 +
            audioLink.on('click', function (e) {
 +
                e.preventDefault();
 +
                window.open(audioURL, '_blank');
 +
            });
 +
        });
 +
    }
 +
};
 +
 +
$(audioPlayer.init);
 +
// End 'Audio player'
  
 
// Start get user username
 
// Start get user username

Revision as of 20:25, 9 October 2023

// This is the non-compressed version of MediaWiki:Common.js

// External links open in new windows/tabs:
$('a.external').attr('target', '_blank');

 /** Collapsible tables *********************************************************
  *
  *  Description: Allows tables to be collapsed, showing only the header. See
  *               [[Wikipedia:NavFrame]].
  *  Maintainers: [[User:R. Koot]]
  */
 var hasClass = (function () {
    var reCache = {};
    return function (element, className) {
        return (reCache[className] ? reCache[className] : (reCache[className] = new RegExp("(?:\\s|^)" + className + "(?:\\s|$)"))).test(element.className);
    };
 })(); 

var autoCollapse = 2;
var collapseCaptionLang = {'ar': 'أخف', 'cs': 'sbalit', 'da': 'fold sammen', 'de': 'einklappen', 'es': 'contraer', 'fi': 'supista', 'fr': 'masquer', 'hu': 'becsuk', 'it': 'comprimi', 'ja': '折り畳む', 'ko': '접기', 'nl': 'samenvouwen', 'pl': 'zwiń', 'pt': 'ocultar', 'pt-br': 'ocultar', 'ro': 'restrânge', 'ru': 'свернуть', 'sv': 'dölj', 'tr': 'daralt', 'zh-hans': '折叠', 'zh-hant': '合併'};
var expandCaptionLang = {'ar': 'أظهر', 'cs': 'rozbalit', 'da': 'fold ud', 'de': 'ausklappen', 'es': 'expandir', 'fi': 'Laajenna', 'fr': 'afficher', 'hu': 'kinyit', 'it': 'espandi', 'ja': '展開する', 'ko': '펼치기', 'nl': 'uitvouwen', 'pl': 'rozwiń', 'pt': 'expandir', 'pt-br': 'expandir', 'ro': 'extinde', 'ru': 'развернуть', 'sv': 'visa', 'tr': 'genişlet', 'zh-hans': '展开', 'zh-hant': '展開'};
var collapseCaption = collapseCaptionLang[mw.config.get("wgPageName").split("/").pop()] || 'collapse';
var expandCaption = expandCaptionLang[mw.config.get("wgPageName").split("/").pop()] || 'expand';
 
window.collapseTable = function ( tableIndex ) {
    var Button = document.getElementById( 'collapseButton' + tableIndex );
    var Table = document.getElementById( 'collapsibleTable' + tableIndex );
 
    if ( !Table || !Button ) {
        return false;
    }
 
    var Rows = Table.rows;
    var i;
 
    if ( Button.firstChild.data === collapseCaption ) {
        for ( i = 1; i < Rows.length; i++ ) {
            Rows[i].style.display = 'none';
        }
        Button.firstChild.data = expandCaption;
    } else {
        for ( i = 1; i < Rows.length; i++ ) {
            Rows[i].style.display = Rows[0].style.display;
        }
        Button.firstChild.data = collapseCaption;
    }
};
 
function createCollapseButtons() {
    var tableIndex = 0;
    var NavigationBoxes = {};
    var Tables = document.getElementsByTagName( 'table' );
    var i;
 
    function handleButtonLink( index, e ) {
        window.collapseTable( index );
        e.preventDefault();
    }
 
    for ( i = 0; i < Tables.length; i++ ) {
        if ( $( Tables[i] ).hasClass( 'collapsible' ) ) {
 
            /* only add button and increment count if there is a header row to work with */
            var HeaderRow = Tables[i].getElementsByTagName( 'tr' )[0];
            if ( !HeaderRow ) continue;
            var Header = HeaderRow.getElementsByTagName( 'th' )[0];
            if ( !Header ) continue;
 
            NavigationBoxes[ tableIndex ] = Tables[i];
            Tables[i].setAttribute( 'id', 'collapsibleTable' + tableIndex );
 
            var Button     = document.createElement( 'span' );
            var ButtonLink = document.createElement( 'a' );
            var ButtonText = document.createTextNode( collapseCaption );
 
            Button.className = 'collapseButton';  /* Styles are declared in Common.css */
 
            ButtonLink.style.color = Header.style.color;
            ButtonLink.setAttribute( 'id', 'collapseButton' + tableIndex );
            ButtonLink.setAttribute( 'href', '#' );
            $( ButtonLink ).on( 'click', $.proxy( handleButtonLink, ButtonLink, tableIndex ) );
            ButtonLink.appendChild( ButtonText );
 
            Button.appendChild( document.createTextNode( '[' ) );
            Button.appendChild( ButtonLink );
            Button.appendChild( document.createTextNode( ']' ) );
 
            Header.insertBefore( Button, Header.firstChild );
            tableIndex++;
        }
    }
 
    for ( i = 0;  i < tableIndex; i++ ) {
        if ( $( NavigationBoxes[i] ).hasClass( 'collapsed' ) || ( tableIndex >= autoCollapse && $( NavigationBoxes[i] ).hasClass( 'autocollapse' ) ) ) {
            window.collapseTable( i );
        } 
        else if ( $( NavigationBoxes[i] ).hasClass ( 'innercollapse' ) ) {
            var element = NavigationBoxes[i];
            while ((element = element.parentNode)) {
                if ( $( element ).hasClass( 'outercollapse' ) ) {
                    window.collapseTable ( i );
                    break;
                }
            }
        }
    }
}
 
$( createCollapseButtons );

/** Dynamic Navigation Bars (experimental) *************************************
 *
 *  Description: See [[Wikipedia:NavFrame]].
 *  Maintainers: UNMAINTAINED
 */

// set up the words in your language
var NavigationBarHide = '[' + collapseCaption + ']';
var NavigationBarShow = '[' + expandCaption + ']';

// 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.firstChild.data == NavigationBarHide) {
        for (var NavChild = NavFrame.firstChild; NavChild != null; NavChild = NavChild.nextSibling) {
            if (hasClass(NavChild, 'NavContent') || hasClass(NavChild, 'NavPic')) {
                NavChild.style.display = 'none';
            }
        }
    NavToggle.firstChild.data = NavigationBarShow;

    // if hidden now
    } else if (NavToggle.firstChild.data == NavigationBarShow) {
        for (var NavChild = NavFrame.firstChild; NavChild != null; NavChild = NavChild.nextSibling) {
            if (hasClass(NavChild, 'NavContent') || hasClass(NavChild, 'NavPic')) {
                NavChild.style.display = 'block';
            }
        }
        NavToggle.firstChild.data = NavigationBarHide;
    }
}

// adds show/hide-button to navigation bars
function createNavigationBarToggleButton(){
    var indexNavigationBar = 0;
    // iterate over all < div >-elements 
    var divs = document.getElementsByTagName("div");
    for (var i = 0; NavFrame = divs[i]; i++) {
        // if found a navigation bar
        if (hasClass(NavFrame, "NavFrame")) {

            indexNavigationBar++;
            var NavToggle = document.createElement("a");
            NavToggle.className = 'NavToggle';
            NavToggle.setAttribute('id', 'NavToggle' + indexNavigationBar);
            NavToggle.setAttribute('href', 'javascript:toggleNavigationBar(' + indexNavigationBar + ');');

            var isCollapsed = hasClass( NavFrame, "collapsed" );
            /*
             * Check if any children are already hidden.  This loop is here for backwards compatibility:
             * the old way of making NavFrames start out collapsed was to manually add style="display:none"
             * to all the NavPic/NavContent elements.  Since this was bad for accessibility (no way to make
             * the content visible without JavaScript support), the new recommended way is to add the class
             * "collapsed" to the NavFrame itself, just like with collapsible tables.
             */
            for (var NavChild = NavFrame.firstChild; NavChild != null && !isCollapsed; NavChild = NavChild.nextSibling) {
                if ( hasClass( NavChild, 'NavPic' ) || hasClass( NavChild, 'NavContent' ) ) {
                    if ( NavChild.style.display == 'none' ) {
                        isCollapsed = true;
                    }
                }
            }
            if (isCollapsed) {
                for (var NavChild = NavFrame.firstChild; NavChild != null; NavChild = NavChild.nextSibling) {
                    if ( hasClass( NavChild, 'NavPic' ) || hasClass( NavChild, 'NavContent' ) ) {
                        NavChild.style.display = 'none';
                    }
                }
            }
            var NavToggleText = document.createTextNode(isCollapsed ? NavigationBarShow : NavigationBarHide);
            NavToggle.appendChild(NavToggleText);

            // 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 (hasClass(NavFrame.childNodes[j], "NavHead")) {
                    NavToggle.style.color = NavFrame.childNodes[j].style.color;
                    NavFrame.childNodes[j].appendChild(NavToggle);
                }
            }
            NavFrame.setAttribute('id', 'NavFrame' + indexNavigationBar);
        }
    }
}

 $( createNavigationBarToggleButton );

//END Collapsible tables *********************************************************

// PootTabs by User:WindPower~
// It puts tabs on pages.
var pootTabsHere = {
    animationsEnabled: $.support.opacity,
	getTab:function(poot, index) {
		return $(poot.children('.poot-tabs').children('ul').children('li')[parseInt(index)]);
	},
	changeTab:function(poot, index, duration, force) {
		if(index == parseInt(poot.attr('pootSelected')) && !force && duration) return;
		if(!pootTabsHere.animationsEnabled) {
			duration = 0;
		}
		poot.attr('pootSelected', index.toString());
		var babies = poot.children('.poot-tabs-content').children();
		babies.each(function() {
			$(this).fadeOut(duration, function(){
				$(this).removeClass('poot-tabs-selected');
			});
		});
		$(babies[index]).each(function() {
			$(this).fadeIn(duration, function(){
				$(this).addClass('poot-tabs-selected');
			});
		});
		var cowtabs = poot.children('.poot-tabs').children('ul').children('li');
		cowtabs.removeClass('poot-tabs-selected');
		$(cowtabs[index]).addClass('poot-tabs-selected');
		pootTabsHere.updatePoot(poot, $(babies[index]).height());
	},
	updatePoot:function(poot, babysize) {
		if(poot.hasClass('poot-tabs-notitle')) {
			poot.find('.poot-tabs-titletext').html(pootTabsHere.getTab(poot, poot.attr('pootSelected')).html());
		} else {
			poot.find('.poot-tabs-titletext').html(poot.attr('originalTitle') + ' &mdash; ' + pootTabsHere.getTab(poot, poot.attr('pootSelected')).html());
		}
		if(poot.has('.poot-tabs-edittabs') && poot.has('.poot-tabs-navbar')) {
			try {
				poot.find('.poot-tabs-navbar').html($(poot.children('.poot-tabs-edittabs').children('span')[parseInt(poot.attr('pootSelected'))]).html());
			} catch(e) {}
		}
		var bestHeight = Math.max(poot.children('.poot-tabs-content').height(), Math.max(poot.children('.poot-tabs').height(), babysize)).toString() + 'px';
		poot.children('.poot-tabs-content').css('height', bestHeight);
		if(poot.attr('vertical')) {
			poot.children('.poot-tabs').css('height', bestHeight);
		}
	},
	toggleCollapse:function(poot) {
		var pootLinkText = poot.children('.poot-tabs-showhide').text().split(';');
		var duration = pootTabsHere.animationsEnabled ? parseInt(poot.attr('pootslideduration')) : 0;
		if(poot.attr('pootcollapse') != 'true') {
			poot.attr('pootcollapse', 'true');
			poot.find('.poot-tabs-hidelink a').text(pootLinkText[0]);
			poot.children('.poot-tabs, .poot-tabs-content').slideUp(duration);
		}
		else {
			poot.attr('pootcollapse', '');
			poot.find('.poot-tabs-hidelink a').text(pootLinkText[1]);
			poot.children('.poot-tabs, .poot-tabs-content').slideDown(duration);
		}
	},
	delayHeight:function(poot, selected) {
		setTimeout(function() {
			poot.attr('pootselected', selected.toString());
			pootTabsHere.changeTab(poot, selected, 0, true);
			if(poot.hasClass('poot-tabs-collapsed')) {
				pootTabsHere.toggleCollapse(poot);
			}
		}, 100);
	},
	poot:function() {
		var dis = $(this);
		dis.removeClass('poot-tabs-nojs'); // If this thing runs, JS is on
		var ind = 0;
		dis.attr('originalTitle', dis.find('.poot-tabs-titletext').html());
		var selected = /poot-tabs-selected-(\d+)/i.exec(dis.attr('class'));
		if(selected) {
			pootTabsHere.delayHeight(dis, parseInt(selected[1])-1);
		}
		else {
			pootTabsHere.delayHeight(dis, 0);
		}
		var duration = dis.hasClass('poot-tabs-noanimations') ? 0 : 200;
		dis.attr('pootslideduration', dis.hasClass('poot-tabs-noanimations') ? '0' : '75');
		dis.children('.poot-tabs').children('ul').children('li').each(function(){
			var thisInd = ind;
			$(this).click(function(){
				pootTabsHere.changeTab(dis, thisInd, duration, false);
				$(this).blur();
				$(this).find('*').blur();
				return false;
			});
			ind++;
		});
		var isVertical = dis.hasClass('poot-tabs-vertical');
		dis.attr('pootvertical', isVertical ? 'true' : '');
		if(isVertical) {
			var teenie = dis.children('.poot-tabs').width().toString() + 'px';
			dis.children('.poot-tabs-content').css('margin-left', teenie);
		}
		dis.attr('pootcollapse', ''); // False
		dis.find('.poot-tabs-hidelink a').click(function(){
			pootTabsHere.toggleCollapse(dis);
			return false;
		});
	},
	init:function() {
		$('.poot-tabs-container').each(pootTabsHere.poot);
	}
};
$(pootTabsHere.init);

// Language support fixes
var langFixes = {
	init: function() {
		// Supported list of languages (not including the default one):
		var langList = ['ar', 'cs', 'da', 'de', 'es', 'fi', 'fr', 'hu', 'it', 'ja', 'ko', 'nl', 'no', 'pl', 'pt', 'pt-br', 'ro', 'ru', 'sv', 'zh-hans', 'zh-hant'];
		// Assumed language if the page is in none of the languages above:
		var defaultLang = 'en';
		var lang = defaultLang;
		for(var i in langList) {
			if(mw.config.get('wgPageName').substr(mw.config.get('wgPageName').length - 1 - langList[i].length).toLowerCase() == '/' + langList[i].toLowerCase()) {
				lang = langList[i];
				break;
			}
		}
		$('body').addClass('lang-' + lang);
	}
};
$(langFixes.init);

// Custom tooltip component. See Template:Tooltip
var wikiTooltip = {
    init: function() {
        var $tooltips = $('.wiki-tooltip .wiki-tooltip-content');
        if ($tooltips[0]) {
            $tooltips.each(function() {
                var $this = $(this);
                function handler() {
                    // Check for links and handle them on handheld devices
                    if (!!$this.closest('a').attr('href')) {
                        $this.closest('a').removeAttr('title');

                        widthRect <= 1000 ? wikiTooltip.hasLink($this) : wikiTooltip.removeAnchor($this);
                    }
                    
                    // Prevent tooltips from overflowing the viewport
                    var bounding = $this[0].getBoundingClientRect();
                    edgeRect = {
                        left: Math.floor(($this.width() + bounding.left + 10)),
                        right: Math.floor(($this.width() + bounding.right + 10))
                    };

                    widthRect = $(window).innerWidth();
                    if (edgeRect.left >= (widthRect || document.documentElement.clientWidth)) {
                        $this.css('left', '');
                        $this.css('right', '50%');
                    } else if (edgeRect.right <= (widthRect || document.documentElement.clientWidth)) {
                        $this.css('right', '');
                        $this.css('left', '50%');
                    }
                }
                $(handler);
                setTimeout(function() {
                    $(window).on('resize', handler);
                }, 250);
            });
        }
    },
    hasLink: function(tooltip) {
        if (tooltip.find('a').length < 1) {
            var $a = $('<a>');
            var url = tooltip.closest('a');
            var href = url.attr('href');

            $a.attr('href', href);
            url.attr('href', 'javascript:void(0);');
            tooltip.append($a);
        }
    },
    removeAnchor: function(tooltip) {
        var url = tooltip.closest('a');
        var href = url.attr('href');

        innerLink = tooltip.find('a').attr('href');
        if (innerLink) {
            href = innerLink;
            url.attr('href', href);
            tooltip.find('a').remove();
        } else {
            return;
        }
    }
};
$(wikiTooltip.init);

// Logged-in body class injection
var loggedinBodyClass = {
	init: function() {
		$('body').addClass(mw.config.get('wgUserName') == null ? 'not-logged-in' : 'logged-in');
	}
};
$(loggedinBodyClass.init);

// Resize YouTube embed, turn HD on, etc, by User:WindPower
var youtubeHelper = {
	chromeSize: 25, // This is the height (in pixels) of the chrome of YouTube's embedded video player. Update this whenever they release a new embedded video player
	maxWidth: 0.85, // Maximum fraction of the available width that the video may take
	infoboxes: ['.infobox', '.testchamber'], // Selectors of infobox-style boxes that should be deducted from the page's available width
	ratioR: /ratio-(\d+)x(\d+)/i,
	widthsR: /widths((?:\D+\d+)+)/i,
	setSize:function() {
		var widths = youtubeHelper.widthsR.exec($(this).attr('class'));
		if(widths != null) {
			widths = widths[1].substr(1).split(/\D+/g);
			var availableWidth = $('#bodyContent').width();
			for(var i in youtubeHelper.infoboxes) {
				if($(youtubeHelper.infoboxes[i]).length) {
					availableWidth -= $(youtubeHelper.infoboxes[i]).width();
				}
			}
			availableWidth *= youtubeHelper.maxWidth;
			var intWidths = [];
			for(var w = 0; w < widths.length; w++) {
				intWidths[w] = parseInt(widths[w]);
			}
			intWidths.sort(function(a, b){return b - a;});
			for(var w = 0; w < intWidths.length; w++) {
				if(intWidths[w] <= availableWidth || w == intWidths.length-1) {
					youtubeHelper.setWidth(this, intWidths[w]);
					break;
				}
			}
		}
		else {
			youtubeHelper.setWidth(this, parseFloat(obj.attr('width')));
		}
	},
	setUrl:function() {
		var obj = $(this).children('object');
		if(!obj.length) return;
		obj.append($('<param name="allowscriptaccess" value="true"></param>'));
		obj.append($('<param name="allowfullscreen" value="true"></param>'));
		var titleParts = mw.config.get('wgPageName').split(/\//g);
		var lang = 'en';
		if(titleParts.length == 2 && !mw.config.get('wgCanonicalSpecialPageName')) {
			lang = titleParts[titleParts.length-1];
		}
		var playerUrl = obj.children('param[name="movie"]').attr('value') + '&version=2&fs=1&theme=dark&color=white' + ($(this).hasClass('hd-on') ? '&hd=1' : '') + '&cc_load_policy=1&modestbranding=1&hl=' + lang + '&cc_lang_pref=' + lang;
		obj.children('param[name="movie"]').attr('value', playerUrl);
		obj.children('embed').attr('src', playerUrl).attr('allowscriptaccess', 'always').attr('allowfullscreen', 'true');
		var resultHtml = $(this).html();
		$(this).html('').html(resultHtml);
	},
	setWidth:function(youtube, width) {
		var obj = $(youtube).children('object');
		if(!obj) return;
		if($(youtube).hasClass('youtube-audio')) {
			obj.attr('width', width).attr('height', youtubeHelper.chromeSize); // Set <object> height
			obj.children('embed').attr('width', width).attr('height', youtubeHelper.chromeSize); // Set <embed> height
		}
		else {
			var ratio = youtubeHelper.ratioR.exec($(youtube).attr('class'));
			if(ratio != null) {
				ratio = parseFloat(ratio[1])/parseFloat(ratio[2]);
				var newHeight = Math.round(width / ratio + youtubeHelper.chromeSize).toString();
				obj.attr('width', width).attr('height', newHeight); // Set <object> height
				obj.children('embed').attr('width', width).attr('height', newHeight); // Set <embed> height
			}
		}
	},
	resizeTimer:null,
	resize:function() {
		if(youtubeHelper.resizeTimer != null) {
			clearTimeout(youtubeHelper.resizeTimer);
		}
		youtubeHelper.resizeTimer = setTimeout(youtubeHelper.onResize, 100);
	},
	onResize:function() {
		$('.youtubebox').each(youtubeHelper.setSize);
	},
	init:function() {
		$('.youtubebox').each(youtubeHelper.setUrl);
		$(window).resize(youtubeHelper.resize);
		youtubeHelper.onResize();
	}
};
$(youtubeHelper.init);

// Edittools loader copied from http://en.wikipedia.org/wiki/MediaWiki:Common.js/edit.js?oldid=407371785
// Only slightly modified by seb26

/** 
 *  Edittools javascript loader ************************************************
 *
 *  Description: Pulls in [[MediaWiki:Edittools.js]]. Includes a cache-bypassing
 *  version number in the URL in order to allow any changes to the edittools to
 *  be rapidly deployed to users.
 *
 *  Note that, by default, this function does nothing unless the element with
 *  the ID "editpage-specialchars" (which contains the old edittools code in
 *  [[MediaWiki:Edittools]], and will be retained as a placeholder in the new
 *  implementation) has a class named "edittools-version-NNN", where NNN is a
 *  number.  If the class name has "test" before the number, the code will only
 *  run for users who have set "window.testJsEdittools = true" in their user JS.
 *  The "test" should be retained in the class name until the new edittools
 *  implementation is ready and fully tested, and until at least 30 days have
 *  passed since this loader stub was added (which will be in 27 June 2008).
 *
 *  For compatibility with Alex Smotrov's original implementation, on which this
 *  code is loosely based (see [[mw:User talk:Alex Smotrov/edittools.js]]), this
 *  loader can also be disabled by setting "window.noDefaultEdittools = true".
 *
 *  Maintainers: [[User:Ilmari Karonen]]
 */

if (['edit', 'submit'].indexOf(mw.config.get('wgAction')) !== -1 || mw.config.get('wgPageName') == "Special:Upload") //scripts specific to editing pages
{
 
  // Prevent the static edittools from flashing before the compact edittools below is loaded.
  mw.util.addCSS('div.edittools-text { display:none; }');
 
  $(function () {
    // needs to be deferred until the DOM has fully loaded
    var placeholder = document.getElementById("editpage-specialchars");
    if (!placeholder || window.noDefaultEdittools) {
      //Show the static edittools again for users with "window.noDefaultEdittools=true".
      mw.util.addCSS('div.edittools-text { display:block; }');
      return;
    }
    var match = /(?:^| )edittools-version-(\d+)(?: |$)/.exec(placeholder.className);
 
    // set window.testJsEdittools = true to enable testing before full deployment
    if (!match && window.testJsEdittools)
        match = /(?:^| )edittools-version-(test\d+)(?: |$)/.exec(placeholder.className);
 
    if (!match) return;
    var url = mw.config.get('wgScript') + '?title=MediaWiki:Edittools.js&action=raw&ctype=text/javascript&nocache=' + match[1];
    mw.loader.load(url);
  });
}

/********* MediaWiki:Valve.js *********/
function talkpageplus()
{
    var talkpagelink = document.getElementById('ca-talk');
    if (talkpagelink && talkpagelink.className == 'new')
    {
        talkpagelink.firstChild.href += '&section=new';
    }
}
$(talkpageplus);

// Konami code easter egg by WindPower
// Constants:
	var spaiConstants = {};
	// Editable constants:
		// General info:
			spaiConstants.spaiImage = '/w/images/7/73/Team_Fortress_Wiki_Egg_Spy.png';
			spaiConstants.spaiHeight = 196;
			spaiConstants.sappedLogo = '/w/images/8/8f/Team_Fortress_Wiki_Egg_Logo_Sap.png';
			spaiConstants.spaiSappingMahWikiWav = '/w/images/4/4a/Team_Fortress_Wiki_Egg.wav';
			spaiConstants.timeStep = 40; // In milliseconds; 40 ms => 25 fps
		// Animation timing (all times in milliseconds):
			spaiConstants.anim_spaiFallDown = 2000; // Time for Spy to fall down
			spaiConstants.anim_spaiWait = 900; // Time Spy waits before going back up
			spaiConstants.anim_spaiBackUp = 2000; // Time for Spy to go back up
			spaiConstants.anim_sapperDestroyed = 2250; // Time until Sapper gets destroyed
	// End editable constants
	spaiConstants.theBody = function(){return document.getElementById('content');};
	spaiConstants.preloadedImages = [];
	spaiConstants.preloadingImages = [];
	spaiConstants.preloadingRefs = {};
	spaiConstants.toPreloadImages = [spaiConstants.spaiImage, spaiConstants.sappedLogo];
	spaiConstants.self = null;
	spaiConstants.loadedSound = false;
	spaiConstants.loadedImages = false;
	spaiConstants.fired = false;
// End constants

if (!Array.prototype.indexOf) {
  Array.prototype.indexOf = function (obj, fromIndex) {
    if (fromIndex == null) {
        fromIndex = 0;
    } else if (fromIndex < 0) {
        fromIndex = Math.max(0, this.length + fromIndex);
    }
    for (var i = fromIndex, j = this.length; i < j; i++) {
        if (this[i] === obj)
            return i;
    }
    return -1;
  };
}

var spaiSappinMahWiki = {
	constants: spaiConstants,
	createImgDiv:function(image) {
		var self = spaiConstants.self;
		var div = document.createElement('div');
		var img = document.createElement('img');
		img.src = image;
		div.appendChild(img);
		setTimeout(function(){spaiConstants.theBody().appendChild(div);}, 1);
		return {
			'div': div,
			'img': img
		};
	},
	imagesLoaded:function() {
		spaiConstants.loadedImages = true;
		if(spaiConstants.loadedSound) {
			spaiConstants.self.spySappingMahWiki();
		}
	},
	soundLoaded:function() {
		spaiConstants.loadedSound = true;
		if(spaiConstants.loadedImages) {
			spaiConstants.self.spySappingMahWiki();
		}
	},
	preloadSound:function(sound, callback) {
		var self = spaiConstants.self;
		try {
			var audio = document.createElement('audio');
			audio.setAttribute('src', sound);
			audio.setAttribute('style', 'display: none;');
			audio.setAttribute('preload', 'true');
			spaiConstants.theBody().appendChild(audio);
			audio.addEventListener('canplaythrough', callback, false);
		}
		catch(e) {}
		setTimeout(callback, 1000); // Fallback
	},
	preloadImage:function(image) {
		var self = spaiConstants.self;
		if(spaiConstants.preloadingImages.indexOf(image) == -1) {
			spaiConstants.preloadingImages[spaiConstants.preloadingImages.length] = image;
			var nodes = self.createImgDiv(image);
			spaiConstants.preloadingRefs[image] = nodes['img'];
			nodes['div'].setAttribute('style', 'visibility: hidden; height: 0px; width: 0px; overflow: hidden; z-index: -10000;');
		}
		if(spaiConstants.preloadingRefs[image].width) {
			spaiConstants.preloadedImages[spaiConstants.preloadedImages.length] = image;
		}
		else
		{
			setTimeout(function(){self.preloadImage(image);}, spaiConstants.timeStep);
		}
	},
	preloadImages:function(callback) {
		var self = spaiConstants.self;
		var allPreloaded = true;
		for(var i in spaiConstants.toPreloadImages) {
			if(spaiConstants.preloadedImages.indexOf(spaiConstants.toPreloadImages[i]) == -1) {
				allPreloaded = false;
			}
			if(spaiConstants.preloadingImages.indexOf(spaiConstants.toPreloadImages[i]) == -1) {
				self.preloadImage(spaiConstants.toPreloadImages[i]);
			}
		}
		if(allPreloaded) {
			callback();
		} else {
			setTimeout(function(){self.preloadImages(callback);}, spaiConstants.timeStep);
		}
	},
	destroyNode:function(node) {
		try {
			node.parentNode.removeChild(node);
		} catch(e) {
			// Ze goggles, zey do nothin
		}
	},
	smoothInOut:function(progress) {
		return (Math.sin((progress-.5)*Math.PI)+1)/2;
	},
	inAnimation:function(func, progressTime, totalTime, callback, easing) {
		var self = spaiConstants.self;
		func(easing(progressTime / totalTime));
		if(progressTime >= totalTime) {
			callback();
		} else {
			setTimeout(function(){self.inAnimation(func, progressTime + spaiConstants.timeStep, totalTime, callback, easing);}, spaiConstants.timeStep);
		}
	},
	animate:function(func, totalTime, callback, easing) {
		var self = spaiConstants.self;
		return self.inAnimation(func, 0.0, totalTime, callback, easing);
	},
	playSound:function(sound) {
		var self = spaiConstants.self;
		try {
			var audio = document.createElement('audio');
			audio.setAttribute('src', sound);
			audio.setAttribute('style', 'display: none;');
			audio.setAttribute('autoplay', 'true');
			spaiConstants.theBody().appendChild(audio);
		}
		catch(e) {}
	},
	spyAnimationFinished:function(nodes) {
		var self = spaiConstants.self;
		for(var node in nodes) {
			self.destroyNode(nodes[node]);
		}
		spaiConstants.fired = false;
	},
	spySappingMahWiki:function() {
		var self = spaiConstants.self;
		if(spaiConstants.fired) return;
		spaiConstants.fired = true;
		var spai = document.createElement('a');
		spai.setAttribute('href', '/');
		spai.setAttribute('style', 'display:block; position: absolute; top: 0px; left: 0px; width: 160px; height: 200px; border: 0px; background: url('+spaiConstants.spaiImage+') no-repeat 0px -50000px; z-index: 10000;');
		spaiConstants.theBody().appendChild(spai);
		var logoPortlet = document.getElementById('p-logo');
		logoPortlet.style.backgroundPosition = 'center bottom';
		var changeHeight = function(progress) {
			progress = parseInt(progress * spaiConstants.spaiHeight);
			spai.style.backgroundPosition = '0px ' + (-spaiConstants.spaiHeight + progress).toString() + 'px';
		};
		self.animate(changeHeight, spaiConstants.anim_spaiFallDown, function(){
			self.playSound(spaiConstants.spaiSappingMahWikiWav);
			setTimeout(function(){
				var oldBackground = logoPortlet.getElementsByTagName('a')[0].style.backgroundImage;
				logoPortlet.getElementsByTagName('a')[0].style.backgroundImage = 'url(' + spaiConstants.sappedLogo + ')';
				self.animate(function(progress){changeHeight(1.0-progress);}, spaiConstants.anim_spaiBackUp, function(){
					setTimeout(function(){
						logoPortlet.getElementsByTagName('a')[0].style.backgroundImage = oldBackground;
						self.spyAnimationFinished([spai]);
					}, spaiConstants.anim_sapperDestroyed);
				}, self.smoothInOut);
			}, spaiConstants.anim_spaiWait);
		}, self.smoothInOut);
	},
	hitItDoc:function() {
		var self = spaiConstants.self;
		self.preloadImages(self.imagesLoaded);
		self.preloadSound(spaiConstants.spaiSappingMahWikiWav, self.soundLoaded);
	},
	initKonami: function () {
		var self = spaiConstants.self;
		/*
			 * Konami-JS ~
			 * :: Now with support for touch events and multiple instances for
			 * :: those situations that call for multiple easter eggs!
			 * Code: https://github.com/snaptortoise/konami-js
			 * Copyright (c) 2009 George Mandis (georgemandis.com, snaptortoise.com)
			 * Version: 1.6.2 (7/17/2018)
			 * Licensed under the MIT License (http://opensource.org/licenses/MIT)
			 * Tested in: Safari 4+, Google Chrome 4+, Firefox 3+, IE7+, Mobile Safari 2.2.1+ and Android
		*/
		var Konami = function (callback) {
			var konami = {
				addEvent: function (obj, type, fn, ref_obj) {
					if (obj.addEventListener)
						obj.addEventListener(type, fn, false);
					else if (obj.attachEvent) {
						// IE
						obj["e" + type + fn] = fn;
						obj[type + fn] = function () {
							obj["e" + type + fn](window.event, ref_obj);
						}
						obj.attachEvent("on" + type, obj[type + fn]);
					}
				},
				removeEvent: function (obj, eventName, eventCallback) {
					if (obj.removeEventListener) {
						obj.removeEventListener(eventName, eventCallback);
					} else if (obj.attachEvent) {
						obj.detachEvent(eventName);
					}
				},
				input: "",
				pattern: "38384040373937396665",
				keydownHandler: function (e, ref_obj) {
					if (ref_obj) {
						konami = ref_obj;
					} // IE
					konami.input += e ? e.keyCode : event.keyCode;
					if (konami.input.length > konami.pattern.length) {
						konami.input = konami.input.substr((konami.input.length - konami.pattern.length));
					}
					if (konami.input === konami.pattern) {
						konami.code(konami._currentLink);
						konami.input = '';
						e.preventDefault();
						return false;
					}
				},
				load: function (link) {
					this._currentLink = link;
					this.addEvent(document, "keydown", this.keydownHandler, this);
					this.iphone.load(link);
				},
				unload: function () {
					this.removeEvent(document, 'keydown', this.keydownHandler);
					this.iphone.unload();
				},
				code: function (link) {
					window.location = link
				},
				iphone: {
					start_x: 0,
					start_y: 0,
					stop_x: 0,
					stop_y: 0,
					tap: false,
					capture: false,
					orig_keys: "",
					keys: ["UP", "UP", "DOWN", "DOWN", "LEFT", "RIGHT", "LEFT", "RIGHT", "TAP", "TAP"],
					input: [],
					code: function (link) {
						konami.code(link);
					},
					touchmoveHandler: function (e) {
						if (e.touches.length === 1 && konami.iphone.capture === true) {
							var touch = e.touches[0];
							konami.iphone.stop_x = touch.pageX;
							konami.iphone.stop_y = touch.pageY;
							konami.iphone.tap = false;
							konami.iphone.capture = false;
							konami.iphone.check_direction();
						}
					},
					touchendHandler: function () {
						konami.iphone.input.push(konami.iphone.check_direction());

						if (konami.iphone.input.length > konami.iphone.keys.length) konami.iphone.input.shift();

						if (konami.iphone.input.length === konami.iphone.keys.length) {
							var match = true;
							for (var i = 0; i < konami.iphone.keys.length; i++) {
								if (konami.iphone.input[i] !== konami.iphone.keys[i]) {
									match = false;
								}
							}
							if (match) {
								konami.iphone.code(konami._currentLink);
							}
						}
					},
					touchstartHandler: function (e) {
						konami.iphone.start_x = e.changedTouches[0].pageX;
						konami.iphone.start_y = e.changedTouches[0].pageY;
						konami.iphone.tap = true;
						konami.iphone.capture = true;
					},
					load: function (link) {
						this.orig_keys = this.keys;
						konami.addEvent(document, "touchmove", this.touchmoveHandler);
						konami.addEvent(document, "touchend", this.touchendHandler, false);
						konami.addEvent(document, "touchstart", this.touchstartHandler);
					},
					unload: function () {
						konami.removeEvent(document, 'touchmove', this.touchmoveHandler);
						konami.removeEvent(document, 'touchend', this.touchendHandler);
						konami.removeEvent(document, 'touchstart', this.touchstartHandler);
					},
					check_direction: function () {
						x_magnitude = Math.abs(this.start_x - this.stop_x);
						y_magnitude = Math.abs(this.start_y - this.stop_y);
						x = ((this.start_x - this.stop_x) < 0) ? "RIGHT" : "LEFT";
						y = ((this.start_y - this.stop_y) < 0) ? "DOWN" : "UP";
						result = (x_magnitude > y_magnitude) ? x : y;
						result = (this.tap === true) ? "TAP" : result;
						return result;
					}
				}
			}

			typeof callback === "string" && konami.load(callback);
			if (typeof callback === "function") {
				konami.code = callback;
				konami.load();
			}

			return konami;
		};
		// End of Konami-JS
		var konami = new Konami();
		konami.code = function(){self.hitItDoc.apply(self)};
		konami.load();
	}
};
spaiConstants.self = spaiSappinMahWiki;
$(spaiSappinMahWiki.initKonami);

// Dynamic background by WindPower
// WindPower is secksy and makes this wiki awesome with his very breath. (- Smashman)
var dynamicBg = {
	categories: {
		// Format:
		// 'CategoryName': 'URL of background image',  ---OR--- 'title-PageTitle': 'URL of background image',
		// Categories don't have to be class names, they can be things like "Weapons", "Featured articles", "Maps", "Help", etc.
		'Scout': '/w/images/e/ea/Background_Scout_vector.png',
		'Soldier': '/w/images/5/54/Background_Soldier_vector.png',
		'Pyro': '/w/images/e/ed/Background_Pyro_vector.png',
                'Demoman': '/w/images/5/59/Background_Demoman_vector.png',
                'Engineer': '/w/images/f/f7/Background_Engineer_vector.png',
                'Heavy': '/w/images/0/03/Background_Heavy_vector.png',
                'Medic': '/w/images/2/24/Background_Medic_vector.png',
                'Sniper': '/w/images/e/ed/Background_Sniper_vector.png',
                'Spy': '/w/images/b/b9/Background_Spy_vector.png'
		// (No comma at the end of the last line)
	},
	getCategories:function() {
		var catlinksnode = document.getElementById('catlinks');
		if(!catlinksnode) return [];
		var catlinks = document.getElementById('catlinks').getElementsByTagName('a');
		var cats = [];
		var l;
		for(var i = 0; i < catlinks.length; i++) {
			l = catlinks[i].getAttribute('title');
			if(l.match(/^Category:/i, '')) {
				cats[cats.length] = l.substr(9).replace(/\/[^/]+$/, '');
			}
		}
		return cats;
	},
	inArray:function(haystack, needle) {
		for(var i = 0; i < haystack.length; i++) {
			if(haystack[i] == needle) {
				return i;
			}
		}
		return -1;
	},
	init:function() {
		if(typeof(wPrefs) != 'undefined') {
			if(dynamicBg.inArray(wPrefs, 'noDynamicBackground') != -1) {
				return; // Script disabled
			}
		}
		try {
			var cats = dynamicBg.getCategories();
			var body = document.getElementsByTagName('body')[0];
		} catch(e) {
			return;
		}
		var selectedCats = [];
		if(typeof(dynamicBg.categories['title-' + mw.config.get('wgTitle')]) != 'undefined') {
			selectedCats[0] = dynamicBg.categories['title-' + mw.config.get('wgTitle')];
		}
		else {
			for(var i in dynamicBg.categories) {
				if(dynamicBg.inArray(cats, i) != -1) {
					selectedCats[selectedCats.length] = dynamicBg.categories[i];
				}
			}
		}
		if(!selectedCats.length) return; // No match, keep default style
		var selectedCat = selectedCats[Math.floor(Math.random()*selectedCats.length)];
		body.style.backgroundImage='url('+selectedCat+')';
	}
};
$(dynamicBg.init);

// Page-specific JavaScript/CSS
var pageScripts = {
	pagesJS: ['Main_Page', 'User:WindPower', 'User:MogDog66', 'User:WindPower/Main_Page', 'User:Lexar/Main_Page/Template:Benjas', 'User:Lexar/RandomPage', 'User:Lexar/responsive_infobox', 'User:Lexar/sandbox', 'User:Tark', 'User:Tark/Sandbox', 'Team_Fortress_Wiki:April_Fools\'_Day/2021/Main_Page', 'User:PhoneWave', 'User:Wookipan/Sandbox'],
	pagesCSS: ['Main_Page', 'User:WindPower', 'User:Pilk/armory', 'User:Pilk', 'User:Esky', 'User:Lagg', 'User:MogDog66', 'User:CrushBOT', 'User:MogDog66/userpagev2', 'User:NVis', 'User:NVis/Sandbox', 'User:Lexar', 'User:MogDog66/MPR', 'User:WindPower/Main_Page', 'User:Moussekateer/3DViewer', 'User:T-Wayne', 'User:FreeXMan', 'User:Nixshadow', 'User:Ath', 'User:Carez', 'User:Lexar/Main_Page/Template:Benjas', 'User:Lexar/RandomPage', 'User:MogDog66/Sandbox', 'User:Obilisk', 'User:Lexar/itembox_tooltip', 'User:Lexar/sandbox', 'User:Lexar/responsive_infobox', 'User:Hagbard Celine', 'User:Wookipan', 'User:Wookipan/Sandbox', 'User:GrampaSwood', 'Team_Fortress_Wiki:April_Fools\'_Day/2019/Main_Page', 'User:Tark', 'User:Tark/Sandbox', 'User:StargazerG', 'User:Boba', 'User:Boba/Projects', 'User:FanCyy', 'User:Dan_greene', 'User:Boba/Sandbox', 'User:Ashe', 'Team_Fortress_Wiki:April_Fools\'_Day/2021/Main_Page', 'User:PhoneWave', 'User:Foxbite'],
	suffixJS: '/Page.js',
	suffixCSS: '/Page.css',
	init: function() {
		for(var i in pageScripts.pagesJS) {
			if(mw.config.get('wgPageName') == pageScripts.pagesJS[i]) {
				mw.loader.load(mw.config.get('wgScript') + '?title=' + encodeURIComponent(mw.config.get('wgPageName') + pageScripts.suffixJS) + '&ctype=text/javascript&action=raw');
			}
		}
		for(var i in pageScripts.pagesCSS) {
			if(mw.config.get('wgPageName') == pageScripts.pagesCSS[i]) {
				mw.loader.load(mw.config.get('wgScript') + '?title=' + encodeURIComponent(mw.config.get('wgPageName') + pageScripts.suffixCSS) + '&ctype=text/css&action=raw', 'text/css');
			}
		}
	}
};
$(pageScripts.init);

// Fancy diffs
var fancyDiffs = {
	isBigDiff: false,
	isBigDiffThreshold: 72,
	toggle: function(element) {
		var expanded = element.hasClass('diff-expanded');
		var contents = element.parent().children('.diff-contents');
		if(expanded) { // Just collapse then
			element.removeClass('diff-expanded');
			if(fancyDiffs.isBigDiff) {
				contents.hide();
			} else {
				contents.slideUp('fast');
			}
		} else if(element.hasClass('diff-data-loaded')) { // Stuff is already loaded, expand
			element.addClass('diff-expanded');
			contents.slideDown('fast');
		} else if(!element.hasClass('diff-data-requested')) { // Stuff is not loaded
			element.addClass('diff-data-requested');
			var fileName = element.find('span').text().replace(/^\s+|\s+$/g);
			var patchName = element.closest('.diffname');
			var diffName = mw.config.get('wgPageName');
			if(patchName && patchName.length && patchName.attr('class')) {
				diffName = patchName.attr('class').substr(9);
			}
			$.get('/w/?title=Template:PatchDiff/' + encodeURIComponent(diffName.replace(/^Template:PatchDiff\//, '')) + '/' + encodeURIComponent(fileName) + '&action=raw', function(data) {
				contents.html(data);
				if(fancyDiffs.isBigDiff) {
					contents.show();
				} else {
					contents.slideDown('fast');
				}
				element.removeClass('diff-data-requested').addClass('diff-data-loaded').addClass('diff-expanded');
			});
		}
		
	},
	init: function() {
		var diffText = $('.diff-name-text');
		if(diffText.length) {
			// Preload leetle gif
			$('body').append($('<img/>').attr('src', '/w/images/4/43/Patch_diff_loading.gif').css('display', 'none'));
			diffText.find('span').each(function() {
				$(this).text($(this).find('a').text().replace(/^\s+|\s+$/g));
			});
			diffText.click(function() {
				fancyDiffs.toggle($(this));
				return false;
			});
			fancyDiffs.isBigDiff = $('.diff-file').length > fancyDiffs.isBigDiffThreshold;
		}
	}
};
$(fancyDiffs.init);

// 3D/2D viewer
$('#switch-to-3d').click(function() {
	$('.container-2d').hide();
	$('.viewer-3d, .viewer-3d-multi, .buttons-container-3d').show();
});

$('#switch-to-2d').click(function() {
	$('.viewer-3d, .viewer-3d-multi, .buttons-container-3d').hide();
	$('.container-2d').show();
});

// 3D model viewer
var viewer3d = {
	dragging: null,
	draggingFrameX: 0,
	draggingFrameY: 0,
	viewers: [],
	frameThresholdX: 10,
	frameThresholdY: 128,
	realMod: function(x, y) {
		return ((x % y) + y) % y;
	},
	init: function() {
		$('.viewer-3d').each(viewer3d.bind);
		$(document).mouseup(viewer3d.release);
		$(document).mousemove(viewer3d.move);
	},
	bind: function() {
		var v = $(this);
		var num = viewer3d.viewers.length;
		var allModels = [];
		var modelID = 0;
		var viewerSize = 0;
		while(true) {
			var modelMap = v.find('.viewer-3d-map-' + modelID);
			var urlNode = v.find('.viewer-3d-url-' + modelID);
			if(!modelMap.length || !urlNode.length) break;
			var url = $('<div/>').html(urlNode.text()).text();
			var framesS = $('<div/>').html(modelMap.text()).text().replace(/^\s+|\s+$/g).split(/,/g);
			var frameMap = [];
			var heightMap = [];
			var leftCropMap = [];
			var totalW = parseInt(framesS[0]);
			var maxFrameW = parseInt(framesS[1]);
			var totalH = parseInt(framesS[2]);
			var verticalSteps = parseInt(framesS[3]);
			var midVertical = Math.floor(verticalSteps / 2);
			for(var f = 4; f < framesS.length; f += 3) {
				frameMap.push(parseInt(framesS[f]));
				heightMap.push(parseInt(framesS[f + 1]));
				leftCropMap.push(parseInt(framesS[f + 2]));
			}
			allModels.push({
				imageURL: url,
				map: frameMap,
				cropMap: leftCropMap,
				totalWidth: totalW,
				totalHeight: totalH,
				maxFrameWidth: maxFrameW,
				xStep: verticalSteps
			});
			viewerSize = Math.max(viewerSize, totalH, maxFrameW);
			modelID++;
		}
		if(!modelID) return;
		var overlayNode = $('<div class="viewer-3d-overlay"></div>');
		var frameN = v.find('.viewer-3d-frame');
		v.find('img').detach();
		var klasses = v.attr('class').split(/ /g);
		var startFrame = 0;
		for(var k in klasses) {
			if(klasses[k].substr(0, 11) == 'startframe-') {
				startFrame = Math.max(0, parseInt(klasses[k].substr(11)));
			}
		}
		var viewer = {
			node: v,
			frameX: startFrame,
			frameY: midVertical,
			models: allModels,
			currentModel: -1,
			frameNode: frameN,
			width: viewerSize,
			height: viewerSize,
			mouseX: 0,
			mouseY: 0,
			overlay: overlayNode
		};
		viewer3d.viewers.push(viewer);
		v.hover(viewer3d.hover, viewer3d.unhover).mousedown(viewer3d.drag).append(overlayNode).attr('data-id', num).css({
			width: viewer.width + 'px',
			height: viewer.height + 'px'
		});
		frameN.mousedown(viewer3d.drag).attr('data-id', num).css('height', viewer.height + 'px');
		viewer3d.changeVersion(viewer, 0);
	},
	getCurrentModel: function(v) {
		return v.models[v.currentModel];
	},
	changeVersion: function(v, version) {
		version = Math.max(0, Math.min(v.models.length - 1, version));
		if(v.currentModel == version) return;
		v.currentModel = version;
		v.frameNode.css('background', 'url(' + viewer3d.getCurrentModel(v).imageURL + ') top left no-repeat');
		viewer3d.display(v, v.frameX, v.frameY);
	},
	hover: function(e) {
		var v = viewer3d.getViewer(this);
		if(viewer3d.dragging != v) {
			v.overlay.animate({'opacity': '1'}, 'fast');
		}
	},
	unhover: function(e) {
		var v = viewer3d.getViewer(this);
		if(viewer3d.dragging != v) {
			v.overlay.animate({'opacity': '0.5'}, 'fast');
		}
	},
	drag: function(e) {
		var v = viewer3d.getViewer(this);
		v.mouseX = e.pageX;
		v.mouseY = e.pageY;
		viewer3d.dragging = v;
		draggingFrameX = v.frameX;
		draggingFrameY = v.frameY;
		return false;
	},
	release: function() {
		var v = viewer3d.dragging;
		viewer3d.dragging = null;
		if(v != null) {
			v.frameX = viewer3d.draggingFrameX;
			v.frameY = viewer3d.draggingFrameY;
			v.overlay.animate({'opacity': '0.5'}, 'fast');
		}
		viewer3d.draggingFrameX = 0;
		viewer3d.draggingFrameY = 0;
	},
	getViewer: function(node) {
		return viewer3d.viewers[parseInt($(node).attr('data-id'))];
	},
	display: function(v, frameX, frameY) {
		var model = viewer3d.getCurrentModel(v);
		var frameID = viewer3d.realMod(frameX * model.xStep + frameY, model.map.length);
		var frameOffset = model.map[frameID];
		var frameWidth = 0;
		if(frameID == model.map.length - 1) {
			frameWidth = model.totalWidth - frameOffset;
		} else {
			frameWidth = model.map[frameID + 1] - frameOffset;
		}
		v.frameNode.css({
			backgroundPosition: (-frameOffset - frameID) + 'px 0px',
			left: Math.round((v.width - model.maxFrameWidth) / 2.0 + model.cropMap[frameID]) + 'px',
			top: Math.round((v.height - model.totalHeight) / 2) + 'px',
			width: frameWidth + 'px',
			height: model.totalHeight + 'px'
		});
	},
	move: function(e) {
		if(viewer3d.dragging == null) {
			return;
		}
		var v = viewer3d.dragging;
		var model = viewer3d.getCurrentModel(v);
		var mouseDeltaX = e.pageX - v.mouseX;
		var mouseDeltaY = e.pageY - v.mouseY;
		var frameDeltaX = Math.round(mouseDeltaX / viewer3d.frameThresholdX);
		var frameDeltaY = -Math.round(mouseDeltaY / viewer3d.frameThresholdY);
		viewer3d.draggingFrameX = v.frameX + frameDeltaX;
		viewer3d.draggingFrameY = Math.max(0, Math.min(model.xStep - 1, v.frameY + frameDeltaY));
		viewer3d.display(v, viewer3d.draggingFrameX, viewer3d.draggingFrameY);
	}
};
$(viewer3d.init);
var selector3d = {
	bind: function() {
		var viewer = viewer3d.getViewer($(this).find('.viewer-3d'));
		var keepGoing = true;
		var modelVariant = 0;
		var selector;
		while(keepGoing) {
			selector = $(this).find('.selector-' + modelVariant);
			if(selector.length) {
				selector.attr('data-variant', modelVariant).click(function() {
					viewer3d.changeVersion(viewer, parseInt($(this).attr('data-variant')));
					return false;
				});
			}
			modelVariant++;
			keepGoing = selector.length;
		}
	},
	init: function() {
		$('.viewer-3d-multi, .viewer-3d-container').each(selector3d.bind);
	}
};
$(selector3d.init);

// Code to get 3D viewer drag working on touch devices
// Source: http://www.jquery4u.com/mobile/jquery-add-dragtouch-support-ipad/
$.fn.addTouch = function(){
    this.each(function(i,el){
      $(el).bind('touchstart touchmove touchend touchcancel',function(){
        //we pass the original event object because the jQuery event
        //object is normalized to w3c specs and does not provide the TouchList
        handleTouch(event);
      });
    });

    var handleTouch = function(event)
    {
      var touches = event.changedTouches,
              first = touches[0],
              type = '';

      switch(event.type)
      {
        case 'touchstart':
          type = 'mousedown';
          break;

        case 'touchmove':
          type = 'mousemove';
          event.preventDefault();
          break;

        case 'touchend':
          type = 'mouseup';
          break;

        default:
          return;
      }

      var simulatedEvent = document.createEvent('MouseEvent');
      simulatedEvent.initMouseEvent(type, true, true, window, 1, first.screenX, first.screenY, first.clientX, first.clientY, false, false, false, false, 0/*left*/, null);
      first.target.dispatchEvent(simulatedEvent);
    };
  };

$('.viewer-3d').addTouch();

// End 3D viewer touch device code

// Start weapon wear table tabs -----
var WeaponWearTable = {
	tabSwitch: function($this,tab,weapons,weapon) {
		if (!$this.hasClass('current')) {
			var tabIndex = $this.index();
			$this.parent().find('.current').removeClass('current');
			$this.addClass('current');
			weapons.find('.current').removeClass('current');
			weapon.eq(tabIndex).addClass('current');
		}
	},
	init: function() {
		$('.weapon-wear-table').each(function(){
			var $this = $(this),
			tabs = $this.children('.tabs'),
			tab = tabs.children('li'),
			weapons = $this.children('.weapons'),
			weapon = weapons.children('li');
			tab.click(function(){
				WeaponWearTable.tabSwitch($(this),tab,weapons,weapon);
			});
		});
	}
};
$(WeaponWearTable.init);
// End weapon wear table tabs -----

// Start Bilibili iframe support -----
var Bilibili = {
  init: function() {
    var $videos = $('.bilibili-video');
    $videos.each(function() {
      var $this = $(this);
      var aid = parseInt($this.data('vaid'));
      var danmaku = parseInt($this.data('vdanmaku'));
      var page = parseInt($this.data('vpage'));
      var width = $this.data('vwidth');
      var height = $this.data('vheight');
      var iframeSrc = 'https://www.bilibili.com/blackboard/html5mobileplayer.html?aid=' + aid + '&high_quality=1&danmaku=' + danmaku + '&page=' + page + '&hideCoverInfo=1&hideDanmakuButton=1';
      var iframe = '<iframe src="' + iframeSrc + '" width="' + width + '" height="' + height + '" frameborder="0" allowfullscreen="true"></iframe>';
      $this.append(iframe);
    });
  }
};
$(Bilibili.init);
// End Bilibili iframe support -----

// Start custom username highlighting -----
var uGroupHighlight = {
  init: function() {
    if ($('.mw-userlink')[0]) {
      var params = {
        action: 'query',
        list: 'allusers',
        augroup: ['sysop', 'moderator', 'bot'],
        auprop: 'groups',
        aulimit: 100,
        format: 'json'
      };

      var api = new mw.Api();

      api.get(params).done(function(data) {
        var uGroups = data.query.allusers, user;
        for (user in uGroups) {
          var name = uGroups[user].name;
          var group = uGroups[user].groups;

          $('bdi').each(function() {
            if ($(this).text().match('\\b' + name + '\\b')) {
              $(this).closest('.mw-userlink').addClass(group.includes('bot') ? 'bot' : 'staff');
            }
          });
        }
      });
    }
  },
};
$(uGroupHighlight.init);
// End custom username highlighting -----

// Start login icon randomizer -----
var iconRandomizer = {
    init: function() {
        var classes = [
            '/w/images/3/33/Login_Scout.png',
            '/w/images/d/d8/Login_Soldier.png',
            '/w/images/7/71/Login_Pyro.png',
            '/w/images/5/53/Login_Demoman.png',
            '/w/images/3/35/Login_Heavy.png',
            '/w/images/a/ab/Login_Engineer.png',
            '/w/images/d/d4/Login_Medic.png',
            '/w/images/e/e4/Login_Sniper.png',
            '/w/images/2/27/Login_Spy.png'
        ];

        // pick a random class image out of nine choices
        pickClass = classes[Math.floor(Math.random() * classes.length)];

        // ensure all relative elements are hit
        var nodes = $('#pt-userpage, #pt-anonuserpage, #pt-login');
        if (nodes.length) {
            for (var i = 0; i < nodes.length; i++) {
                nodes.css('background-image', 'url(' + pickClass + ')');
            }
        }
    }
};

$(iconRandomizer.init);
// End login icon randomizer -----

// Start logo switcher -----
var logoLocales = {
    init: function() {
        var logoElement = document.getElementsByClassName("mw-wiki-logo")[0];
        var logoDic = {
            // "cs": "",
            "da": "/w/images/2/2d/Logo_da.png",
            "de": "/w/images/e/e1/Logo_de.png",
            "es": "/w/images/3/30/Logo_es.png",
            // "fi": "",
            "fr": "/w/images/2/2d/Logo_fr.png",
            // "hu": "",
            // "it": "",
            "ja": "/w/images/8/86/Logo_ja.png",
            "ko": "/w/images/8/8d/Logo_ko.png",
            "nl": "/w/images/6/6d/Logo_nl.png",
            "no": "/w/images/b/b6/Logo_no.png",
            "pl": "/w/images/7/7a/Logo_pl.png",
            "pt": "/w/images/3/3a/Logo_pt-br.png",
            "pt-br": "/w/images/3/3a/Logo_pt-br.png",
            // "ro": "",
            "ru": "/w/images/7/75/Logo_ru.png",
            // "sv": "",
            // "tr": "",
            "tr": "/w/images/a/ac/Logo_tr.png",
            "zh-hans": "/w/images/4/45/Logo_zh-hans.png",
            "zh-hant": "/w/images/f/f2/Logo_zh-hant.png"
        };
        for (var lang in logoDic){
            if (mw.config.get("wgPageName").split("/").pop() === lang) {
                logoElement.style.backgroundImage = "url('" + logoDic[lang] + "')";
                logoElement.style.backgroundSize = "125px 125px";
            }
        }
    }
};

$(logoLocales.init);
// End logo switcher -----

// Start 'Audio player'
var audioPlayer = {
    currentAudio: null,

    init: function () {
        $('.tfwiki-audio-player').each(function () {
            var audioPlayerElement = $(this);
            var audioLink = audioPlayerElement.children('a');
            var audioURL = audioLink.attr('href');
            var audio = null;
            var audioStatus = audioPlayerElement.find('.tfwiki-audio-player-action');
		
			audioStatus.removeClass('inactive');

            audioPlayerElement.on('click', function (e) {
                if (e.target !== audioStatus[0]) {
                    return;
                }

                e.preventDefault();
				
                if (!audio) {
                    audio = new Audio(audioURL);
                    audio.volume = 0.5;
                    audio.addEventListener('ended', function () {
                        audioStatus.text(audioStatus.data('text-play'));
                        audioStatus.removeClass('playing');
                    });
                }

                if (audioPlayer.currentAudio && audioPlayer.currentAudio !== audio) {
                    audioPlayer.currentAudio.pause();
                    audioPlayer.currentAudio.currentTime = 0;
                    audioPlayer.currentAudioStatus.text(audioPlayer.currentAudioStatus.data('text-play'));
                    audioPlayer.currentAudioStatus.removeClass('playing');
                }

                if (audio.paused) {
                    audio.play();
                    audioStatus.text(audioStatus.data('text-pause'));
                    audioPlayer.currentAudio = audio;
                    audioPlayer.currentAudioStatus = audioStatus;
                    audioStatus.addClass('playing');
                } else {
                    audio.pause();
                    audioStatus.text(audioStatus.data('text-resume'));
                    audioPlayer.currentAudio = null;
                    audioPlayer.currentAudioStatus = null;
                    audioStatus.removeClass('playing');
                }
            });

            audioLink.on('click', function (e) {
                e.preventDefault();
                window.open(audioURL, '_blank');
            });
        });
    }
};

$(audioPlayer.init);
// End 'Audio player'

// Start get user username
// See Template:WikiUsername
var loggedInUsername = {
  init: function() {
    var wikiUserName = mw.config.get("wgUserName");
    if (!wikiUserName) {
      return
    }
    var pageUsernames = document.querySelectorAll("span.usernamegoeshere");
    for (var i = 0; i < pageUsernames.length; i++) {
      pageUsernames[i].textContent = wikiUserName;
    }
  }
};

$(loggedInUsername.init);
// End get user username

// Start user sandbox button
var userSandboxBtn = {
    init: function() {
        if (mw.config.get("wgUserId")) {
            mw.util.addPortletLink("p-personal", "/wiki/Special:MyPage/Sandbox", "Sandbox", "pt-sandbox", "Your sandbox", "", $("#pt-preferences"));
        }
    }
};
$(userSandboxBtn.init);
// End user sandbox button

/* Google Analytics */
  var _gaq = _gaq || [];
  _gaq.push(['_setAccount', 'UA-18260470-1']);
  _gaq.push(['_setDomainName', '.teamfortress.com']);
  _gaq.push(['_trackPageview']);

  (function() {
    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
  })();
/* GoSquared analytics */
  var GoSquared = {};
  GoSquared.acct = "GSN-106863-S";
  (function(w){
    function gs(){
      w._gstc_lt = +new Date;
      var d = document, g = d.createElement("script");
      g.type = "text/javascript";
      g.src = "//d1l6p2sc9645hc.cloudfront.net/tracker.js";
      var s = d.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(g, s);
    }
    w.addEventListener ?
      w.addEventListener("load", gs, false) :
      w.attachEvent("onload", gs);
  })(window);