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' }, iframeMapRender: { srcdoc: '<!DOCTYPE html><html data-bs-theme="dark" lang="ru"><head><meta charset="UTF-8"><meta content="width=device-width,initial-scale=1" name="viewport"><meta content="Просмотр карт SS14 Kerisar" name="description"><title>🗺 Просмотр карт SS14 Kerisar 🦎</title><link crossorigin="anonymous" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" rel="stylesheet"><style>img{image-rendering:pixelated}body{background-color:#2c2c2c}.image-container{width:100%;height:70vh;border:1px solid rgba(255,255,255,.1);position:relative;overflow:hidden;margin-top:20px;margin-left:auto;margin-right:auto}.image-container img{max-width:none;height:auto;position:absolute;top:0;left:0}:fullscreen .image-container{border:none}#buttonsGroupPlus{display:flex;justify-content:space-between;flex-direction:row}</style></head><body><div class="container"><div class="mb-3 d-flex flex-wrap" id="folder-list"></div><div class="mb-3" id="file-list"></div><div class="image-container" id="image-container"><img alt="Изображение" id="panzoom-image" src=""></div><div class="m-1" id="buttonsGroupPlus"><button class="btn btn-outline-light btn-sm" id="fullscreen-toggle">Полный экран</button> <button class="btn btn-outline-light btn-sm" id="open-in-new-window">Открыть в отдельном окне</button><a class="btn btn-outline-light btn-sm" href="https://station14.ru" target="_top">Вернуться на главную страницу вики</a></div></div><script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"></script><script src="https://unpkg.com/@panzoom/[email protected]/dist/panzoom.min.js"></script><script>async function fetchFolderData(e){const t=await fetch(e),n=await t.text();return{folderNames:[...n.matchAll(/{"count":{.*?},"name":"(.*?)",.*?"type":"folder"}/g)].map(e=>e[1]),fileNames:[...n.matchAll(/{"name":"(.*?)",.*?"type":"file"}/g)].map(e=>e[1])}}async function processFolder(e){const t="https://api.codetabs.com/v1/proxy/?quest=https://cloud.mail.ru/public/6P5o/RLnoP6pLa/"+e,{folderNames:n,fileNames:o}=await fetchFolderData(t),l=n.filter(e=>"MapImages"!==e);return{name:e,files:o.sort(),subfolders:await Promise.all(l.map(t=>processFolder(e+"/"+t))).then(e=>e.sort((e,t)=>e.name.localeCompare(t.name,void 0,{numeric:!0})))}}function displayFolderData(e){const t=document.createElement("div");t.classList.add("folder");const n=document.createElement("button");n.classList.add("btn","btn-outline-light","m-1","btn-sm"),n.textContent=e.name,n.onclick=(()=>loadFiles(e,n)),t.appendChild(n),document.getElementById("folder-list").appendChild(t)}function loadFiles(e,t){const n=document.getElementById("file-list");n.innerHTML="",e.files.forEach(t=>{const o=document.createElement("button");o.textContent=t,o.classList.add("btn","btn-outline-light","m-1","btn-sm"),o.onclick=(()=>showMap(e.name,t,o)),n.appendChild(o)}),document.querySelectorAll("#folder-list button").forEach(e=>e.classList.remove("active")),t.classList.add("active")}function showMap(e,t,n){const o="https://thumb.cloud.mail.ru/weblink/thumb/xw1/6P5o/RLnoP6pLa/"+e+"/"+t,l=document.getElementById("panzoom-image");l.src=o;const a=Panzoom(l,{canvas:!0}),c=l.parentElement;c.addEventListener("wheel",a.zoomWithWheel),c.addEventListener("wheel",function(e){e.shiftKey&&a.zoomWithWheel(e)});const s=document.getElementById("open-in-new-window");s.style.display="block",s.onclick=(()=>window.open(o,"_blank")),document.querySelectorAll("#file-list button").forEach(e=>e.classList.remove("active")),n.classList.add("active")}function toggleFullscreen(){const e=document.getElementById("image-container");document.fullscreenElement?document.exitFullscreen():e.requestFullscreen().catch(e=>console.log("Ошибка при попытке открыть в полноэкранном режиме"))}document.getElementById("fullscreen-toggle").addEventListener("click",toggleFullscreen),async function(){const{folderNames:e,fileNames:t}=await fetchFolderData("https://api.codetabs.com/v1/proxy/?quest=https://cloud.mail.ru/public/6P5o/RLnoP6pLa");(await Promise.all(e.filter(e=>"MapImages"!==e).map(e=>processFolder(e)))).forEach(e=>{displayFolderData(e)})}().catch(e=>{console.error("Ошибка при запросе:",e)});</script></body></html>', width: '100%', height: '100%', name: 'iframeMapRender', id: 'iframeMapRender' }, 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://api.codetabs.com/v1/proxy/?quest=https://game2.station14.ru/main/server/status", serverconnect: "game2.station14.ru/main/server" }, { name: "server-nova", connect: "https://api.codetabs.com/v1/proxy/?quest=https://game2.station14.ru/nova/server/status", serverconnect: "game2.station14.ru/nova/server" }, { name: "server-athara", connect: "https://api.codetabs.com/v1/proxy/?quest=https://game2.station14.ru/athara/server/status", serverconnect: "game2.station14.ru/athara/server" }, { name: "server-solaris", connect: "https://api.codetabs.com/v1/proxy/?quest=https://game2.station14.ru/solaris/server/status", serverconnect: "game2.station14.ru/solaris/server" }, { name: "server-echo", connect: "https://api.codetabs.com/v1/proxy/?quest=https://game2.station14.ru/echo/server/status", serverconnect: "game2.station14.ru/echo/server" }, { name: "server-elysium", connect: "https://api.codetabs.com/v1/proxy/?quest=https://game2.station14.ru/elysium/server/status", serverconnect: "game2.station14.ru/elysium/server" }, { name: "server-nebula", connect: "https://api.codetabs.com/v1/proxy/?quest=https://game2.station14.ru/nebula/server/status", serverconnect: "game2.station14.ru/nebula/server" }, { name: "server-wl", connect: "https://api.codetabs.com/v1/proxy/?quest=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://api.codetabs.com/v1/proxy/?quest=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=http://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; } function isGray(r, g, b) { var maxDiff = 20; // Максимальное допустимое отклонение между RGB для серого return Math.abs(r - g) < maxDiff && Math.abs(g - b) < maxDiff && Math.abs(r - b) < maxDiff; } 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 <= 40) { factor = 6; } else if (brightness <= 180) { factor = 4; } else { factor = 0; } } // Если цвет серый, уменьшаем фактор if (isGray(r, g, b)) { factor = Math.max(1, factor * 0.50); } // Добавляем фактор вместо умножения r = Math.min(255, r + factor); g = Math.min(255, g + factor); b = Math.min(255, b + factor); return a < 1 ? 'rgba(' + r + ', ' + g + ', ' + b + ', ' + a + ')' : 'rgb(' + r + ', ' + g + ', ' + b + ')'; } // Функция для подсветки ячеек в таблице при наведении function applyHighlighting() { if (/Mobi|Android/i.test(navigator.userAgent)) { return; } 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) { // Проверяем, что <tr> находится на первом уровне, и исключаем строки с вложенными таблицами return row.parentElement === tbody && !row.querySelector('table'); }); // Пропускаем первую строку, если нет <thead> и нет класса 'no-header-table' var topLevelRows = (!thead && !noHeader) ? rows.slice(1) : rows; var hasInvalidRowspan = false; var hasTooManyRowspan = false; // Проверка на ошибки в rowspan Array.prototype.forEach.call(topLevelRows, function(row) { var cells = Array.prototype.slice.call(row.querySelectorAll('td, th')).filter(function(cell) { return !cell.classList.contains('mobile'); }); var cellCount = cells.length; var rowspanCount = cells.filter(function(cell) { return cell.hasAttribute('rowspan') && cell.getAttribute('rowspan') !== '1'; // Игнорируем rowspan="1" }).length; // Проверка на слишком большое количество rowspan или неправильное расположение rowspan if (rowspanCount > 2) { 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, выходим if (hasTooManyRowspan || hasInvalidRowspan) return; // Обработка наведения на строки и ячейки Array.prototype.forEach.call(topLevelRows, function(row, rowIndex) { var cells = Array.prototype.slice.call(row.querySelectorAll('td, th')); // Сохраняем оригинальные стили для каждой ячейки var originalStyles = cells.map(function(cell) { return { backgroundColor: getComputedStyle(cell).backgroundColor, color: getComputedStyle(cell).color }; }); // Наведение на строку (tr) row.addEventListener('mouseover', function() { highlightRow(row, rowIndex, true); }); row.addEventListener('mouseout', function() { highlightRow(row, rowIndex, false); }); // Наведение на ячейки с rowspan Array.prototype.forEach.call(cells, function(cell, cellIndex) { // Обрабатываем только ячейки с rowspan > 1 if (cell.hasAttribute('rowspan') && parseInt(cell.getAttribute('rowspan')) > 1) { var rowspan = parseInt(cell.getAttribute('rowspan')); // Обработка rowspan ячейки cell.addEventListener('mouseover', function() { // Отключаем обработку строки при наведении на rowspan ячейку row.classList.add('highlighted'); highlightRowspanArea(rowIndex, cellIndex, rowspan, true); }); cell.addEventListener('mouseout', function() { // Восстанавливаем состояние строки при уходе курсора row.classList.remove('highlighted'); highlightRowspanArea(rowIndex, cellIndex, rowspan, false); }); } }); // Функция подсветки строки (только текущее tr) function highlightRow(row, rowIndex, highlight) { if (row.classList.contains('highlighted')) return; // Избегаем повторного применения подсветки var rowCells = Array.prototype.slice.call(row.querySelectorAll('td, th')); Array.prototype.forEach.call(rowCells, function(cell, cellIndex) { // Подсвечиваем только ячейки, не имеющие атрибут rowSpan или с rowspan="1" if (!cell.hasAttribute('rowspan') || parseInt(cell.getAttribute('rowspan')) === 1) { var cellStyle = getComputedStyle(cell); cell.style.setProperty('background-color', highlight ? brightenColor(cellStyle.backgroundColor) : originalStyles[cellIndex].backgroundColor, 'important'); cell.style.setProperty('color', highlight ? brightenColor(cellStyle.color) : originalStyles[cellIndex].color, 'important'); } }); } // Функция подсветки для ячеек с rowspan (подсвечивает все строки, которые охватывает ячейка) function highlightRowspanArea(rowIndex, cellIndex, rowspan, highlight) { for (var i = 0; i < rowspan; i++) { var targetRow = topLevelRows[rowIndex + i]; if (targetRow) { var targetCells = Array.prototype.slice.call(targetRow.querySelectorAll('td, th')); // Обработка ячейки в каждой строке, затронутой rowspan Array.prototype.forEach.call(targetCells, function(targetCell) { var targetCellStyle = getComputedStyle(targetCell); // Сохраняем оригинальные стили для каждой ячейки, если подсветка включена if (highlight) { targetCell.originalBackground = targetCellStyle.backgroundColor; targetCell.originalColor = targetCellStyle.color; } // Применяем подсветку или восстанавливаем оригинальные стили targetCell.style.setProperty('background-color', highlight ? brightenColor(targetCellStyle.backgroundColor) : targetCell.originalBackground, 'important'); targetCell.style.setProperty('color', highlight ? brightenColor(targetCellStyle.color) : targetCell.originalColor, 'important'); }); } } } }); } }); } // Функция для логики меню, создаваемого модулем CategoryMenu function initCategorySwitcher() { var categories = document.querySelectorAll('.navigation__categories > div'); var menus = document.querySelectorAll('.navigation__menu-item'); var contentDivs = document.querySelectorAll('.navigation__content > div'); var menuItems = document.querySelectorAll('.navigation__menu-item > div'); var currentCategoryIndex = 0; // Индекс текущей активной категории // Генерация ID из текста function generateIdFromText(text) { return text.trim().replace(/\s+/g, '-').replace(/[^a-zа-яёA-ZА-ЯЁ0-9\-]/g, ''); } // Присвоение ID категориям и пунктам меню function assignIds() { for (var i = 0; i < categories.length; i++) { var category = categories[i]; category.setAttribute('id', generateIdFromText(category.textContent || category.innerText)); var menu = menus[i]; var menuItems = menu.querySelectorAll('div'); for (var j = 0; j < menuItems.length; j++) { menuItems[j].setAttribute('id', generateIdFromText(menuItems[j].textContent || menuItems[j].innerText)); } } } assignIds(); // Вызов функции для присвоения ID // Удаление класса active у всех переданных элементов function clearActiveElements(elements) { for (var i = 0; i < elements.length; i++) { elements[i].classList.remove('active'); } } // Переключение категорий function switchCategory(index) { clearActiveElements(menus); clearActiveElements(contentDivs); clearActiveElements(categories); var selectedCategory = categories[index]; if (selectedCategory) { selectedCategory.classList.add('active'); var selectedMenu = document.querySelector('.' + selectedCategory.classList[0] + '-menu'); if (selectedMenu) { selectedMenu.classList.add('active'); switchContent(selectedMenu.querySelector('div')); // Открытие первого пункта меню } } currentCategoryIndex = index; updateArrowStates(); // Обновление состояния стрелок } // Переключение контента в меню function switchContent(menuItem) { clearActiveElements(menuItems); clearActiveElements(contentDivs); if (menuItem) { var contentClass = menuItem.className + '-content'; var content = document.querySelector('.' + contentClass); if (content) { content.classList.add('active'); menuItem.classList.add('active'); } } } // Обновление состояния стрелок (активные/неактивные) function updateArrowStates() { var prevArrow = document.getElementById('prev-category'); var nextArrow = document.getElementById('next-category'); prevArrow.classList.toggle('disabled', currentCategoryIndex === 0); prevArrow.style.pointerEvents = currentCategoryIndex === 0 ? 'none' : 'auto'; nextArrow.classList.toggle('disabled', currentCategoryIndex === categories.length - 1); nextArrow.style.pointerEvents = currentCategoryIndex === categories.length - 1 ? 'none' : 'auto'; } // Стрелки для переключения категорий document.getElementById('prev-category').addEventListener('click', function() { if (currentCategoryIndex > 0) { switchCategory(--currentCategoryIndex); // Переключение на новую категорию } }); document.getElementById('next-category').addEventListener('click', function() { if (currentCategoryIndex < categories.length - 1) { switchCategory(++currentCategoryIndex); // Переключение на новую категорию } }); // Функция для обработки якорей function handleAnchorChange() { var anchor = window.location.hash.substring(1); // Получаем текущий хеш без символа # var decodedAnchor = decodeURIComponent(anchor); // Декодируем хеш var found = false; // Проверка пунктов меню for (var i = 0; i < menuItems.length; i++) { if (menuItems[i].id === decodedAnchor) { // Сравниваем с декодированным значением var categoryIndex = Array.prototype.indexOf.call(categories, menuItems[i].closest('.navigation__menu-item').previousElementSibling); if (categoryIndex !== -1) { switchCategory(categoryIndex); // Открываем категорию switchContent(menuItems[i]); // Открываем пункт меню found = true; // Якорь найден } break; // Выход из цикла, если якорь найден } } // Проверка категорий, если якорь не найден в пунктах меню if (!found) { for (var i = 0; i < categories.length; i++) { if (categories[i].id === decodedAnchor) { // Сравниваем с декодированным значением switchCategory(i); // Открываем категорию found = true; // Якорь найден break; } } } } // Проверяем якорь при изменении URL window.addEventListener('hashchange', handleAnchorChange); // Инициализация: открытие первой категории и первого пункта if (categories.length > 0) { assignIds(); // Присвоить ID в начале handleAnchorChange(); // Обработка начального якоря // Проверка наличия якоря и открытие первой категории только если ID не найден if (!window.location.hash) { switchCategory(0); } else { switchCategory(currentCategoryIndex); } updateArrowStates(); } // Клик по элементам меню for (var i = 0; i < menuItems.length; i++) { (function(menuItem) { menuItem.addEventListener('click', function() { switchContent(menuItem); // Переключение контента при клике на пункт меню // Убрали изменение хеша при клике }); })(menuItems[i]); } document.querySelector('.navigation__menu-toggle').addEventListener('click', function() { var menu = document.querySelector('.navigation__menu-container'); menu.classList.toggle('active'); // Переключение класса для показа/скрытия меню this.classList.toggle('active'); // Переключение для перемещения кнопки }); } const currentPageTitle = document.title; $(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(); }, 500); } if (currentPageTitle.includes("Kerisar")) { console.log("Привет, Kerisar!"); var button = document.createElement("button"); button.innerText = "Создать архив"; document.getElementById("archivetest").appendChild(button); button.onclick = function() { var apiUrl = "https://station14.ru/api.php?action=parse&page=Шаблон:Бумажная работа/paperWork.json&prop=text&formatversion=2&format=json"; fetch(apiUrl).then(function(response) { return response.json(); }).then(function(data) { var text = data.parse.text; //console.log(text); // Извлекаем содержимое между [testarchivestart] и [testarchivestop] var startTag = "[testarchivestart]"; var endTag = "[testarchivestop]"; var startIndex = text.indexOf(startTag); var endIndex = text.indexOf(endTag); if (startIndex !== -1 && endIndex !== -1) { var extractedContent = text.substring(startIndex + startTag.length, endIndex); // Декодируем base64 в бинарные данные var binaryString = window.atob(extractedContent); var len = binaryString.length; var bytes = new Uint8Array(len); for (var i = 0; i < len; i++) { bytes[i] = binaryString.charCodeAt(i); } // Создаем Blob из бинарных данных var blob = new Blob([bytes], { type: "application/zip" }); // Создаем ссылку для скачивания var link = document.createElement("a"); link.href = URL.createObjectURL(blob); link.download = "archive.zip"; // Имя файла для скачивания link.innerText = "Скачать архив"; document.getElementById("archivetest").appendChild(link); } else { alert("Теги [testarchivestart] и [testarchivestop] не найдены."); } }).catch(function(error) { console.error("Ошибка:", error); }); }; } var categoriesExist = document.querySelectorAll('.navigation'); if (categoriesExist.length > 0) { initCategorySwitcher(); } }); /*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