MediaWiki:Common.js

Материал из Space Station 14 Вики

Замечание: Возможно, после публикации вам придётся очистить кэш своего браузера, чтобы увидеть изменения.

  • Firefox / Safari: Удерживая клавишу Shift, нажмите на панели инструментов Обновить либо нажмите Ctrl+F5 или Ctrl+R (⌘+R на Mac)
  • Google Chrome: Нажмите Ctrl+Shift+R (⌘+Shift+R на Mac)
  • Internet Explorer / Edge: Удерживая Ctrl, нажмите Обновить либо нажмите Ctrl+F5
  • Opera: Нажмите Ctrl+F5.
const IFRAME_CONFIG = {
	iframe1: {
		src: 'https://example.com',
		srcdoc: '',
		width: '600',
		height: '400',
		name: 'iframe1',
		allow: 'clipboard-write',
		referrerpolicy: 'no-referrer',
		sandbox: 'allow-same-origin',
		id: 'iframe1'
	},
	iframe2: {
		src: '',
		srcdoc: '<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Bootstrap demo</title><link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous"><script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script></head><body><form><div class="mb-3"><label for="exampleInputEmail1" class="form-label">Email address</label><input type="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp"><div id="emailHelp" class="form-text">We ll never share your email with anyone else.</div></div><div class="mb-3"><label for="exampleInputPassword1" class="form-label">Password</label><input type="password" class="form-control" id="exampleInputPassword1"></div><div class="mb-3 form-check"><input type="checkbox" class="form-check-input" id="exampleCheck1"><label class="form-check-label" for="exampleCheck1">Check me out</label></div><button type="submit" class="btn btn-primary">Submit</button></form></body></html>',
		width: '600',
		height: '400',
		name: 'iframe2',
		allow: '',
		referrerpolicy: '',
		sandbox: '',
		id: 'iframe2'
	},
	iframeSkillsTableRed: {
		src: 'https://docs.google.com/spreadsheets/d/1AvfEvRdw98iezJk2i0DVJpTgoJu2sgLnhMK9fp-LqFk/edit?gid=189048440#gid=189048440',
		width: '100%',
		height: '1900px',
		id: "iframeSkillsTableRed"
	},
};
const currentURL = window.location.href;
const serversStatus = [{
	name: "server-main",
	connect: "https://game2.station14.ru/main/server/status",
	serverconnect: "game2.station14.ru/main/server"
}, {
	name: "server-nova",
	connect: "https://game2.station14.ru/nova/server/status",
	serverconnect: "game2.station14.ru/nova/server"
}, {
	name: "server-athara",
	connect: "https://game2.station14.ru/athara/server/status",
	serverconnect: "game2.station14.ru/athara/server"
}, {
	name: "server-solaris",
	connect: "https://game2.station14.ru/solaris/server/status",
	serverconnect: "game2.station14.ru/solaris/server"
}, {
	name: "server-echo",
	connect: "https://game2.station14.ru/echo/server/status",
	serverconnect: "game2.station14.ru/echo/server"
}, {
	name: "server-elysium",
	connect: "https://game2.station14.ru/elysium/server/status",
	serverconnect: "game2.station14.ru/elysium/server"
}, {
	name: "server-nebula",
	connect: "https://game2.station14.ru/nebula/server/status",
	serverconnect: "game2.station14.ru/nebula/server"
}, {
	name: "server-wl",
	connect: "https://game2.station14.ru/wl/server/status",
	serverconnect: "game2.station14.ru/wl/server"
}, {
	name: "server-frontier",
	connect: "https://api.codetabs.com/v1/proxy/?quest=https://arles.station14.ru/frontier-main/server/status",
	serverconnect: "arles.station14.ru/frontier-main/server"
}, {
	name: "server-fallout",
	connect: "https://api.codetabs.com/v1/proxy/?quest=http://188.92.78.98:1221/status",
	serverconnect: "188.92.78.98:1221"
}, {
	name: "server-marines",
	connect: "https://game1.station14.ru/marines-main/server/status",
	serverconnect: "game1.station14.ru/marines-main/server"
}, {
	name: "server-stalker",
	connect: "https://api.codetabs.com/v1/proxy/?quest=https://game.stalkers14.xyz/status",
	serverconnect: "game.stalkers14.xyz"
}];

function getXHRInfo(url) {
	return new Promise(function(resolve, reject) {
		var xhr = new XMLHttpRequest();
		xhr.open('GET', url, true);
		xhr.onload = function() {
			if (xhr.status >= 200 && xhr.status < 300) {
				resolve(JSON.parse(xhr.responseText));
			} else {
				reject('Ошибка при выполнении запроса: ' + xhr.status);
			}
		};
		xhr.onerror = function() {
			reject('Ошибка при выполнении запроса');
		};
		xhr.send();
	});
}

function updateServerInfoByConnect(connect, name) {
	getServerTime().then(function(currentUnixTime) {
		return getXHRInfo(connect).then(function(serverData) {
			var unixStartRound = getUnixTime(serverData.round_start_time);
			var timeDisplay = unixStartRound ? secondsToDHM(currentUnixTime -
				unixStartRound) : "–";
			var currentPlayers = serverData.players;
			var maxPlayers = serverData.soft_max_players;
			// Вычисляем процент
			var percentage = maxPlayers > 0 ? Math.round((currentPlayers /
				maxPlayers) * 100) : 0;
			// Форматируем строку с количеством игроков
			var playersDisplay = currentPlayers && maxPlayers ? currentPlayers + '/' +
				maxPlayers : "–";
			// Функция для проверки на пустые данные
			function checkValue(value) {
				return value ? value : "–";
			}
			var serverInfo = {
				name: checkValue(serverData.name),
				round: checkValue(serverData.round_id),
				status: checkValue(serverData.run_level),
				map: checkValue(serverData.map),
				time: timeDisplay,
				players: playersDisplay,
				preset: checkValue(serverData.preset),
				percentage: percentage
			};
			updateServerInfo(serverInfo, name);
			//console.log("serverInfo", serverInfo, "currentUnixTime", currentUnixTime,"unixStartRound", unixStartRound);
		});
	}).catch(function(error) {
		console.error(error);
	});
}

function getUnixTime(dateString) {
	return Math.floor(new Date(dateString).getTime() / 1000); // Делим на 1000, чтобы получить секунды
}

function secondsToDHM(seconds) {
	var days = Math.floor(seconds / 86400);
	var hours = Math.floor((seconds % 86400) / 3600);
	var minutes = Math.floor((seconds % 3600) / 60);
	var result = "";
	if (days > 0) result += days + " дн, ";
	if (hours > 0) result += hours + " ч ";
	// Проверка на наличие минут
	if (minutes > 0) {
		result += minutes + " мин";
	} else {
		result += "–"; // Символ ожидания, если минут нет
	}
	return result.trim();
}

function getServerTime() {
	return fetch(
		'https://station14.ru/api.php?action=query&meta=siteinfo&siprop=general&format=json'
	).then(function(response) {
		return response.json();
	}).then(function(data) {
		return getUnixTime(data.query.general.time);
	});
}

function updateServerInfo(serverObj, serverName) {
	var serverStatusFrame = document.getElementById(serverName);
	var statusText = {
		0: "Лобби",
		1: "Раунд идёт",
		2: "Завершение"
	};
	serverStatusFrame.querySelector(".serverInfoRoundSet").textContent = "#" +
		serverObj.round;
	serverStatusFrame.querySelector(".serverStatusSet").textContent = statusText[
		serverObj.status] || "Неизвестный статус";
	serverStatusFrame.querySelector(".serverMapSet").textContent = serverObj.map;
	serverStatusFrame.querySelector(".serverTimeSet").textContent = serverObj.time;
	serverStatusFrame.querySelector(".serverPlayersSet").textContent = serverObj.players;
	serverStatusFrame.querySelector(".serverPresetSet").textContent = serverObj.preset;
	// Получаем строку подключения из serversStatus
	const serverData = serversStatus.find(function(server) {
		return server.name === serverName;
	});
	var connectionStringElement = serverStatusFrame.querySelector(
		".serverConnectSet");
	if (connectionStringElement) {
		connectionStringElement.textContent = serverData ? serverData.serverconnect :
			'-'; // Устанавливаем строку подключения
	}
	// Обновляем прогресс-бар
	var progressBar = serverStatusFrame.querySelector(".progressBar"); // Предполагаем, что у вас есть элемент для прогресс-бара
	if (progressBar) {
		progressBar.style.width = serverObj.percentage + '%'; // Устанавливаем ширину прогресс-бара
	}
	// Обновляем отображение процента
	var percentageDisplay = serverStatusFrame.querySelector(".percentageDisplay"); // Элемент для отображения процента
	if (percentageDisplay) {
		percentageDisplay.textContent = serverObj.percentage + '%'; // Устанавливаем текст с процентом
	}
}

function updateAllServersInfo() {
	serversStatus.forEach(function(server) {
		updateServerInfoByConnect(server.connect, server.name);
	});
}

function copyToClipboard(connectUrl) {
	const copyIcons = document.querySelectorAll('.copy-icon');
	copyIcons.forEach(function(icon) {
		icon.addEventListener('click', function() {
			// Находим предыдущий элемент
			const previousElement = this.previousElementSibling;
			if (previousElement) {
				// Получаем текстовое содержимое
				const textToCopy = previousElement.textContent;
				const tempInput = document.createElement('input');
				tempInput.value = textToCopy;
				document.body.appendChild(tempInput);
				tempInput.select();
				document.execCommand('copy');
				document.body.removeChild(tempInput);
				// Визуальный эффект
				this.style.opacity = '0.5'; // Сменяем прозрачность
				setTimeout(function() {
					this.style.opacity = '1'; // Возвращаем прозрачность
				}.bind(this), 200); // Через 1 секунду
			}
		});
	});
}

function escapeAndMinifyCSS(css) {
  return css
    .replace(/\\/g, '\\\\') // Экранируем обратный слэш
    .replace(/\n/g, '') // Удаляем переносы строк (по желанию)
    .replace(/\/\*[\s\S]*?\*\//g, '') // Удаляем комментарии
    .replace(/\s+/g, ' ') // Заменяем множественные пробелы на один
    .trim(); // Удаляем пробелы в начале и конце
}

const addedStyles = new Set();

function customCSS(textCSS) {
  const escapedCSS = escapeAndMinifyCSS(textCSS); // Экранируем CSS

  // Проверяем, был ли уже добавлен этот CSS
  if (!addedStyles.has(escapedCSS)) {
    addedStyles.add(escapedCSS); // Добавляем в множество уникальных стилей

    const styleSheet = document.createElement("style");
    styleSheet.textContent = escapedCSS;
    document.head.appendChild(styleSheet);
  }
}

function createIframe(id) {
	const config = IFRAME_CONFIG[id];
	if (!config) return;
	const iframe = document.createElement('iframe');
	if (config.src && config.src !== "") {
		iframe.src = config.src;
	}
	if (config.srcdoc && config.srcdoc !== "") {
		iframe.srcdoc = config.srcdoc;
	}
	if (config.width && config.width !== "") {
		iframe.width = config.width;
	} else {
		iframe.width = '100%'; // Значение по умолчанию
	}
	if (config.height && config.height !== "") {
		iframe.height = config.height;
	} else {
		iframe.height = '400px'; // Значение по умолчанию
	}
	if (config.name && config.name !== "") {
		iframe.name = config.name;
	}
	if (config.allow && config.allow !== "") {
		iframe.allow = config.allow;
	}
	if (config.referrerpolicy && config.referrerpolicy !== "") {
		iframe.referrerPolicy = config.referrerpolicy;
	}
	if (config.sandbox && config.sandbox !== "") {
		iframe.sandbox = config.sandbox;
	}
	iframe.id = config.id || ''; // id может быть пустым, если не задано
	return iframe;
}

// Функция для увеличения яркости цвета
function brightenColor(color, factor) {
    function getBrightness(r, g, b) {
        return 0.2126 * r + 0.7152 * g + 0.0722 * b;
    }

    var rgbValues = color.match(/\d+(\.\d+)?/g);
    var r = parseInt(rgbValues[0], 10);
    var g = parseInt(rgbValues[1], 10);
    var b = parseInt(rgbValues[2], 10);
    var a = rgbValues.length === 4 ? parseFloat(rgbValues[3]) : 1;  // Обработка альфа-канала

    var brightness = getBrightness(r, g, b);

    if (factor === undefined) {
        if (brightness < 35) {
            if (r > g && r > b) {
                factor = 1.1 + (35 - brightness) / 150;  // Красный доминирует
            } else if (g > r && g > b) {
                factor = 1.1 + (35 - brightness) / 130;  // Зеленый доминирует
            } else {
                factor = 1.1 + (35 - brightness) / 70;   // Темный цвет с доминированием синего
            }
        } else if (brightness < 55) {
            factor = 1.10;
        } else if (brightness < 70) {
            factor = 1.08;
        } else if (brightness < 120) {
            factor = 1.04;
        } else if (brightness < 140) {
            factor = 1.03;
        } else if (brightness < 180) {
            factor = 1.02;
        } else {
            factor = 1.0;
        }
    }

    r = Math.min(255, Math.floor(r * factor));
    g = Math.min(255, Math.floor(g * factor));
    b = Math.min(255, Math.floor(b * factor));

    return a < 1 ? 'rgba(' + r + ', ' + g + ', ' + b + ', ' + a + ')' : 'rgb(' + r + ', ' + g + ', ' + b + ')';
}

// Функция для подсветки ячеек в таблице при наведении
function applyHighlighting() {
    // Проверка, является ли устройство мобильным, если да — функция не выполняется
    if (/Mobi|Android/i.test(navigator.userAgent)) {
        return;
    }

    // Находим все таблицы с классом 'wikitable', кроме тех, что имеют класс 'no-highlight-table'
    var tables = document.querySelectorAll('.wikitable:not(.no-highlight-table)');

    // Проходим по каждой таблице
    Array.prototype.forEach.call(tables, function(table) {
        var tbody = table.querySelector('tbody');
        var thead = table.querySelector('thead');
        var noHeader = table.classList.contains('no-header-table');

        // Проверяем, что тело таблицы существует
        if (tbody) {
            // Получаем все строки <tr> внутри tbody, исключая строки, содержащие вложенные таблицы
            var rows = Array.prototype.slice.call(tbody.querySelectorAll('tr')).filter(function(row) {
                return !row.querySelector('table');
            });

            // Пропускаем первую строку, если нет thead и нет класса 'no-header'
            var topLevelRows = (!thead && !noHeader) ? rows.slice(1) : rows;

            var hasInvalidRowspan = false;
            var hasTooManyRowspan = false;

            // Проходим по строкам первого уровня
            topLevelRows.forEach(function(row) {
                var cells = Array.prototype.slice.call(row.querySelectorAll('td, th')).filter(function(cell) {
                    return !cell.classList.contains('mobile');
                });
                var cellCount = cells.length;

                // Проверяем, есть ли больше двух ячеек с атрибутом rowspan
                var rowspanCount = cells.filter(function(cell) {
                    return cell.hasAttribute('rowspan') && cell.getAttribute('rowspan') !== '1';
                }).length;

                // Если в строке больше двух ячеек с rowspan, функция не применяется
                if (rowspanCount > 2) {
                    hasTooManyRowspan = true;
                    return;
                }

                // Если строка имеет 3 или меньше ячеек и больше одного rowspan, не применяем функцию
                if (cellCount <= 3 && rowspanCount > 1) {
                    hasTooManyRowspan = true;
                    return;
                }

                // Проверяем, есть ли корректные ячейки с rowspan на краю строки (первый или последний элемент)
                var hasValidRowspanEdge = cells.some(function(cell, index) {
                    return cell.hasAttribute('rowspan') && cell.getAttribute('rowspan') !== '1' && (index === 0 || index === cells.length - 1);
                });

                // Если ячейки с rowspan находятся не на краю, устанавливаем флаг ошибки
                if (!hasValidRowspanEdge && cells.some(function(cell, index) {
                    return cell.hasAttribute('rowspan') && cell.getAttribute('rowspan') !== '1' && index > 0 && index < cells.length - 1;
                })) {
                    hasInvalidRowspan = true;
                }
            });

            // Если есть некорректные строки с rowspan или слишком много rowspan, выходим
            if (hasTooManyRowspan || hasInvalidRowspan) return;

            var originalStyles = {};

            // Проходим по каждой строке для добавления событий подсветки
            topLevelRows.forEach(function(row, rowIndex) {
                var cells = Array.prototype.slice.call(row.querySelectorAll('td, th'));

                // Сохраняем исходные стили каждой ячейки
                cells.forEach(function(cell, index) {
                    if (!originalStyles[rowIndex]) originalStyles[rowIndex] = [];
                    originalStyles[rowIndex][index] = {
                        backgroundColor: getComputedStyle(cell).backgroundColor,
                        color: getComputedStyle(cell).color
                    };
                });

                cells.forEach(function(cell, index) {
                    var rowspan = parseInt(cell.getAttribute('rowspan'), 10) || 1;

                    // Добавляем обработчик события при наведении мыши
                    cell.addEventListener('mouseover', function() {
                        // Подсвечиваем все строки, охватываемые rowspan
                        if (rowspan > 1) {
                            for (var i = 0; i < rowspan; i++) {
                                var targetRow = topLevelRows[rowIndex + i];
                                if (targetRow) {
                                    var targetCells = targetRow.querySelectorAll('td, th');
                                    Array.prototype.forEach.call(targetCells, function(innerCell, innerIndex) {
                                        innerCell.style.backgroundColor = brightenColor(getComputedStyle(innerCell).backgroundColor);
                                        innerCell.style.color = brightenColor(getComputedStyle(innerCell).color);
                                    });
                                }
                            }
                        }

                        // Подсвечиваем ячейки в текущем ряду
                        cells.forEach(function(innerCell, innerIndex) {
                            innerCell.style.backgroundColor = brightenColor(getComputedStyle(innerCell).backgroundColor);
                            innerCell.style.color = brightenColor(getComputedStyle(innerCell).color);
                        });
                    });

                    // Добавляем обработчик события, когда мышь уходит с ячейки
                    cell.addEventListener('mouseout', function() {
                        // Восстанавливаем стили для всех строк, затронутых rowspan
                        if (rowspan > 1) {
                            for (var i = 0; i < rowspan; i++) {
                                var targetRow = topLevelRows[rowIndex + i];
                                if (targetRow) {
                                    var targetCells = targetRow.querySelectorAll('td, th');
                                    Array.prototype.forEach.call(targetCells, function(innerCell, innerIndex) {
                                        innerCell.style.backgroundColor = originalStyles[rowIndex + i][innerIndex].backgroundColor;
                                        innerCell.style.color = originalStyles[rowIndex + i][innerIndex].color;
                                    });
                                }
                            }
                        }

                        // Восстанавливаем стили в текущем ряду
                        cells.forEach(function(innerCell, innerIndex) {
                            innerCell.style.backgroundColor = originalStyles[rowIndex][innerIndex].backgroundColor;
                            innerCell.style.color = originalStyles[rowIndex][innerIndex].color;
                        });
                    });
                });
            });
        }
    });
}

$(document).ready(function() {
	var serversStatus = document.querySelectorAll('.serversStatus');
	if (serversStatus.length > 0) {
		updateAllServersInfo();
		// Перезапускать каждую минуту (60000 мс)
		setInterval(updateAllServersInfo, 60000);
	}
	var cssEls = document.querySelectorAll('.customCSS');
	if (cssEls.length > 0) {
		for (var j = 0; j < cssEls.length; j++) {
			const textCSS = cssEls[j].textContent;
			customCSS(textCSS);
		}
	}
	const copyServerConnectionIcon = document.querySelectorAll(
		'.copyServerConnectionIcon');
	copyServerConnectionIcon.forEach(function(icon) {
		icon.addEventListener('click', function() {
			const connectUrl = this.getAttribute('data-connect');
			copyToClipboard(connectUrl);
		});
	});
	copyToClipboard();
	var divs = document.querySelectorAll('.customIFrame');
	if (divs) {
		for (var i = 0; i < divs.length; i++) {
			var div = divs[i];
			var id = div.id;
			var iframe = createIframe(id);
			if (iframe) {
				div.appendChild(iframe);
			}
		}
	}
	var tables = document.querySelectorAll('.wikitable');
	if (tables.length > 0) {
    	setTimeout(function() {
    	    applyHighlighting();
    	}, 300);
	}
});
/*WikiEditor/Викификатор*/
if ($.inArray(mw.config.get('wgAction'), ['edit', 'submit']) !== -1) {
	mw.loader.load(
		'//ru.wikipedia.org/w/index.php?title=MediaWiki:Gadget-wikificator.js&action=raw&ctype=text/javascript'
	);
}
var customizeToolbar = function() {
	$('#wpTextbox1').wikiEditor('addToToolbar', {
		'section': 'advanced',
		'group': 'format',
		'tools': {
			'wikify': {
				label: 'Викификатор',
				type: 'button',
				icon: '//upload.wikimedia.org/wikipedia/commons/0/06/Wikify-toolbutton.png',
				action: {
					type: 'callback',
					execute: function(context) {
						Wikify();
					}
				}
			}
		}
	});
};
if ($.inArray(mw.config.get('wgAction'), ['edit', 'submit']) !== -1) {
	mw.loader.using('user.options', function() {
		if (mw.user.options.get('usebetatoolbar')) {
			mw.loader.using('ext.wikiEditor.toolbar', function() {
				$(document).ready(customizeToolbar);
			});
		}
	});
}
/**
 * Taken from https://wiki.ss220.space/index.php?title=MediaWiki:Common.js&oldid=35626
 */
/* Variables for interface text used throughout the script, for ease of translating */
var i18n = {
	// Collapsible elements and page loader
	hideText: 'скрыть',
	showText: 'показать',
	// Page loader
	loadErrorTitle: 'Возникла ошибка при загрузке содержимого'
};
var mcw = window.mcw = {};
/* Keep track of delegated events on dynamic content */
mcw.events = {};
/* Fired whenever wiki content is added. (#mw-content-text, live preview, load page, etc.) */
mw.hook('wikipage.content').add(function($wikipageContent) {
	/**
	 * Page loader
	 *
	 * Allows a page to be downloaded and displayed on demand.
	 * Use with Template:LoadPage and Template:LoadBox
	 */
	(function() {
		var $loadPage = $wikipageContent.find('.load-page');
		if (!$loadPage.length) {
			return;
		}
		// We need the spinner to show loading is happening, but we don't want
		// to have a delay while the module downloads, so we'll load this now,
		// regardless of if something is clicked
		mw.loader.load('jquery.spinner');
		// Create button starting with hide text
		// Will be changed to the show text while calculating the maximum button size
		var $buttonTemplate = $('<span>').addClass(
			'mw-editsection-like load-page-button').append('[ ', $('<span>').addClass(
			'jslink').text(i18n.hideText), ' ]');
		var extractList = function($contentContainer, listClass) {
			var $content = $contentContainer.find('.mw-parser-output > ul > li > ul')
				.children(':not(.nbttree-inherited)');
			if (listClass) {
				$content.addClass(listClass);
			}
			return $content;
		};
		$loadPage.each(function() {
			var $body = $(this);
			var page = $body.data('page');
			if (!page) {
				return;
			}
			var template = $body.data('template');
			var treeview = $body.data('treeview');
			var treeviewClass = $body.data('treeviewclass');
			var $heading;
			var $contentContainer;
			var $content;
			var $button = $buttonTemplate.clone();
			var $buttonLink = $button.find('.jslink');
			if (treeview) {
				$heading = $body;
				$contentContainer = $('<div>');
			} else {
				$heading = $body.children().first();
				$contentContainer = $body.find('.load-page-content');
			}
			// Add the button
			$heading.append($button);
			// Move the edit button to the right spot
			$contentContainer.find('.mw-editsection, .mw-editsection-like').insertAfter(
				$button);
			// Find max button width, and set its min-width to it
			var hideWidth = $button.width();
			$buttonLink.text(i18n.showText);
			var showWidth = $button.width();
			if (hideWidth !== showWidth) {
				$button.css('min-width', hideWidth > showWidth ? hideWidth : showWidth);
			}
			$buttonLink.click(function() {
				if ($body.hasClass('pageloader-contentloaded')) {
					if ($buttonLink.text() === i18n.showText) {
						if (treeview) {
							$content.insertAfter($body);
						} else {
							$contentContainer.show();
						}
						$buttonLink.text(i18n.hideText);
					} else {
						if (treeview) {
							$content.detach();
						} else {
							$contentContainer.hide();
						}
						$buttonLink.text(i18n.showText);
					}
					return;
				}
				// See if this was loaded elsewhere before making a request
				var gotContent;
				$('.pageloader-contentloaded').each(function() {
					var $fLoader = $(this);
					if ($fLoader.data('page') === page && $fLoader.data(
							'pageloader-content')) {
						$contentContainer.html($fLoader.data('pageloader-content')).removeClass(
							'noscript');
						mw.hook('wikipage.content').fire($contentContainer);
						if (treeview) {
							$body.find('.noscript').remove();
							$content = extractList($contentContainer, treeviewClass);
							$content.insertAfter($body);
						}
						$buttonLink.text(i18n.hideText);
						$body.addClass('pageloader-contentloaded');
						gotContent = true;
						return false;
					}
				});
				if (gotContent) {
					return;
				}
				// Just in-case the spinner module is still not ready yet
				var $spinner = $();
				mw.loader.using('jquery.spinner', function() {
					// $spinner will be false if the content somehow loaded before the module did
					if ($spinner) {
						$spinner = $.createSpinner().addClass('mw-editsection-like').css(
							'min-width', $button.css('min-width'));
						$button.hide().after($spinner);
					}
				});
				var requestData = {
					action: 'parse',
					prop: 'text'
				};
				if (template) {
					requestData.page = page;
				} else {
					requestData.title = mw.config.get('wgPageName');
					requestData.text = '{' + '{:' + page + '}}';
				}
				new mw.Api().get(requestData).done(function(data) {
					var html = data.parse.text['*'];
					$contentContainer.html(html).removeClass('noscript');
					// Resolve self-links
					if (template) {
						var curPage = '/' + mw.config.get('wgPageName');
						$contentContainer.find('a').each(function() {
							var $link = $(this);
							if ($link.attr('href') === curPage) {
								$link.replaceWith($('<strong>').addClass('selflink').append(
									$link.contents()));
							}
						});
						html = $contentContainer.html();
					}
					$body.data('pageloader-content', html);
					// Fire content hook on the new content, running all this stuff again and more :)
					mw.hook('wikipage.content').fire($contentContainer);
					if (treeview) {
						$body.find('.noscript').remove();
						$content = extractList($contentContainer, treeviewClass);
						$content.insertAfter($body);
					}
					$spinner.remove();
					$spinner = false;
					$buttonLink.text(i18n.hideText);
					$button.show();
					$body.addClass('pageloader-contentloaded');
				}).fail(function(_, error) {
					$spinner.remove();
					$spinner = false;
					$button.show();
					var errorText = '';
					if (error.textStatus) {
						errorText = error.textStatus;
					} else if (error.error) {
						errorText = error.error.info;
					}
					mw.notify(errorText, {
						title: i18n.loadErrorTitle,
						autoHide: false
					});
				});
			});
		});
	}());
	/**
	 * Element animator
	 *
	 * Will cycle the active class on any child elements
	 * within an element with the animated class.
	 */
	(function() {
		if (!mcw.animate) {
			mcw.animate = setInterval(function() {
				$('.animated').each(function() {
					var $elem = $(this);
					var $current = $elem.children('.active');
					var $next = $current.nextAll(':not(.skip):first');
					// Loop back to the start
					if (!$next.length) {
						$next = $elem.children(':not(.skip):first');
					}
					$current.removeClass('active');
					$next.addClass('active');
				});
			}, 2000);
		}
	}());
});
// SS220 import end