MediaWiki:Common.js: различия между версиями
Pok (обсуждение | вклад) Нет описания правки |
Kerisar (обсуждение | вклад) мНет описания правки |
||
| Строка 102: | Строка 102: | ||
return getXHRInfo(connect).then(function(serverData) { | return getXHRInfo(connect).then(function(serverData) { | ||
var unixStartRound = getUnixTime(serverData.round_start_time); | var unixStartRound = getUnixTime(serverData.round_start_time); | ||
var timeDisplay = unixStartRound ? secondsToDHM(currentUnixTime - | var timeDisplay = unixStartRound ? secondsToDHM(currentUnixTime - unixStartRound) : "–"; | ||
var currentPlayers = serverData.players; | var currentPlayers = serverData.players; | ||
var maxPlayers = serverData.soft_max_players; | var maxPlayers = serverData.soft_max_players; | ||
// Вычисляем процент | // Вычисляем процент | ||
var percentage = maxPlayers > 0 ? Math.round((currentPlayers / | var percentage = maxPlayers > 0 ? Math.round((currentPlayers / maxPlayers) * 100) : 0; | ||
// Форматируем строку с количеством игроков | // Форматируем строку с количеством игроков | ||
var playersDisplay = currentPlayers && maxPlayers ? currentPlayers + '/' + | var playersDisplay = currentPlayers && maxPlayers ? currentPlayers + '/' + maxPlayers : "–"; | ||
// Функция для проверки на пустые данные | // Функция для проверки на пустые данные | ||
function checkValue(value) { | function checkValue(value) { | ||
| Строка 155: | Строка 152: | ||
function getServerTime() { | function getServerTime() { | ||
return fetch( | 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); | return getUnixTime(data.query.general.time); | ||
}); | }); | ||
| Строка 171: | Строка 167: | ||
2: "Завершение" | 2: "Завершение" | ||
}; | }; | ||
serverStatusFrame.querySelector(".serverInfoRoundSet").textContent = "#" + | serverStatusFrame.querySelector(".serverInfoRoundSet").textContent = "#" + serverObj.round; | ||
serverStatusFrame.querySelector(".serverStatusSet").textContent = statusText[serverObj.status] || | |||
serverStatusFrame.querySelector(".serverStatusSet").textContent = statusText[ | "Неизвестный статус"; | ||
serverStatusFrame.querySelector(".serverMapSet").textContent = serverObj.map; | serverStatusFrame.querySelector(".serverMapSet").textContent = serverObj.map; | ||
serverStatusFrame.querySelector(".serverTimeSet").textContent = serverObj.time; | serverStatusFrame.querySelector(".serverTimeSet").textContent = serverObj.time; | ||
| Строка 183: | Строка 178: | ||
return server.name === serverName; | return server.name === serverName; | ||
}); | }); | ||
var connectionStringElement = serverStatusFrame.querySelector( | var connectionStringElement = serverStatusFrame.querySelector(".serverConnectSet"); | ||
if (connectionStringElement) { | if (connectionStringElement) { | ||
connectionStringElement.textContent = serverData ? serverData.serverconnect : | connectionStringElement.textContent = serverData ? serverData.serverconnect : '-'; // Устанавливаем строку подключения | ||
} | } | ||
// Обновляем прогресс-бар | // Обновляем прогресс-бар | ||
| Строка 233: | Строка 226: | ||
function escapeAndMinifyCSS(css) { | function escapeAndMinifyCSS(css) { | ||
return css.replace(/\\/g, '\\\\') // Экранируем обратный слэш | |||
.replace(/\n/g, '') // Удаляем переносы строк (по желанию) | |||
.replace(/\/\*[\s\S]*?\*\//g, '') // Удаляем комментарии | |||
.replace(/\s+/g, ' ') // Заменяем множественные пробелы на один | |||
.trim(); // Удаляем пробелы в начале и конце | |||
} | } | ||
const addedStyles = new Set(); | const addedStyles = new Set(); | ||
function customCSS(textCSS) { | 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); | |||
} | |||
} | } | ||
| Строка 291: | Строка 280: | ||
return iframe; | return iframe; | ||
} | } | ||
// Функция для увеличения яркости цвета | // Функция для увеличения яркости цвета | ||
function brightenColor(color, factor) { | 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 < 30) { | |||
if (r > g && r > b) { | |||
factor = 1.1 + (30 - brightness) / 300; // Красный доминирует | |||
} else if (g > r && g > b) { | |||
factor = 1.1 + (30 - brightness) / 200; // Зеленый доминирует | |||
} else { | |||
factor = 1.1 + (30 - brightness) / 50; // Темный цвет с доминированием синего | |||
} | |||
} else if (brightness <= 55) { | |||
factor = 1.09; | |||
} else if (brightness < 140) { | |||
factor = 1.045; | |||
} 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() { | 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'; | |||
}).length; | |||
// Проверка на слишком большое количество rowspan или неправильное расположение rowspan | |||
if (rowspanCount > 2 || (cellCount <= 3 && rowspanCount > 1)) { | |||
hasTooManyRowspan = true; | |||
return; | |||
} | |||
var hasValidRowspanEdge = cells.some(function(cell, index) { | |||
return cell.hasAttribute('rowspan') && cell.getAttribute('rowspan') !== '1' && (index === 0 || | |||
index === cells.length - 1); | |||
}); | |||
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) { | |||
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 | |||
if (!cell.hasAttribute('rowspan')) { | |||
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, targetCellIndex) { | |||
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'); | |||
}); | |||
} | |||
} | |||
} | |||
}); | |||
} | |||
}); | |||
} | } | ||
// Функции для добавления кастомных заголовков в TOC | // Функции для добавления кастомных заголовков в TOC | ||
function addHeadingsWithTOC() { | function addHeadingsWithTOC() { | ||
mw.hook('wikipage.content').add(function($content) { | |||
var $toc = $('#toc ul'); // Находим TOC | |||
$toc.empty(); | |||
// Ищем заголовки h1 - h6 и элементы с классом .custom-heading | |||
var $headings = $content.find('h1, h2, h3, h4, h5, h6, .custom-heading'); | |||
var tocCounters = [0]; // Счётчики для уровней заголовков | |||
var lastLevel = 1; // Уровень предыдущего заголовка | |||
var $currentList = $toc; // Текущий список для вложенности | |||
// Функция для обновления счётчиков для текущего уровня | |||
function updateCounters(level) { | |||
if (level > lastLevel) { | |||
tocCounters.push(0); // Добавляем новый уровень | |||
} else if (level < lastLevel) { | |||
tocCounters = tocCounters.slice(0, level); // Убираем лишние уровни | |||
} | |||
tocCounters[level - 1]++; | |||
lastLevel = level; | |||
} | |||
// Функция для получения номера секции в стиле "1", "1.1", "1.1.1" | |||
function getSectionNumber() { | |||
return tocCounters.join('.'); | |||
} | |||
// Функция для создания нового <ul> для вложенного уровня | |||
function createNestedList($parent) { | |||
var $nestedList = $('<ul>'); | |||
$parent.append($nestedList); | |||
return $nestedList; | |||
} | |||
// Перебираем все заголовки и элементы с классом .custom-heading | |||
$headings.each(function() { | |||
var $heading = $(this); | |||
var level; | |||
// Определяем уровень | |||
if ($heading.hasClass('custom-heading')) { | |||
level = 1; | |||
} else { | |||
// Проверяем наличие <span class="mw-headline"> внутри заголовка | |||
var $headlineSpan = $heading.find('span.mw-headline'); | |||
if ($headlineSpan.length === 0) { | |||
return; // Пропускаем заголовок, если нет нужного <span> | |||
} | |||
// Определяем уровень заголовка по тегу | |||
var tagName = $heading.prop('tagName').toLowerCase(); | |||
level = parseInt(tagName.charAt(1), 10); | |||
} | |||
updateCounters(level); // Обновляем счётчики для текущего уровня | |||
var sectionId = $heading.find('span.mw-headline').attr('id') || 'heading-' + getSectionNumber(); | |||
$heading.find('span.mw-headline').attr('id', sectionId); // Присваиваем уникальный id заголовку | |||
// Извлекаем текст заголовка | |||
var headingText = $heading.find('span.mw-headline').text().trim() || $heading.text().trim(); | |||
// Проверяем, что текст заголовка не пустой | |||
if (headingText.length > 0) { | |||
// Создание вложенных списков, если уровень больше 1 | |||
if (level > 1) { | |||
while (tocCounters.length > level) { | |||
$currentList = $currentList.parent(); // Переходим к родительскому <ul> | |||
} | |||
if ($currentList.children('ul').length === 0) { | |||
$currentList = createNestedList($currentList); // Создаём новый вложенный список | |||
} | |||
} else { | |||
// Если уровень 1, сбрасываем вложенные списки | |||
$currentList = $toc; | |||
} | |||
// Создаём элемент списка в оглавлении (TOC) | |||
var tocItem = $('<li>').addClass('toclevel-' + (level - 1)).append($('<a>').attr('href', '#' + | |||
sectionId).append($('<span>').addClass('tocnumber').text(getSectionNumber()), $('<span>').addClass( | |||
'toctext').text(headingText))); | |||
// Добавляем элемент в текущий список | |||
$currentList.append(tocItem); | |||
} | |||
}); | |||
}); | |||
} | } | ||
const currentPageTitle = document.title; | |||
$(document).ready(function() { | $(document).ready(function() { | ||
var serversStatus = document.querySelectorAll('.serversStatus'); | var serversStatus = document.querySelectorAll('.serversStatus'); | ||
| Строка 584: | Строка 529: | ||
} | } | ||
} | } | ||
const copyServerConnectionIcon = document.querySelectorAll( | const copyServerConnectionIcon = document.querySelectorAll('.copyServerConnectionIcon'); | ||
copyServerConnectionIcon.forEach(function(icon) { | copyServerConnectionIcon.forEach(function(icon) { | ||
icon.addEventListener('click', function() { | icon.addEventListener('click', function() { | ||
| Строка 606: | Строка 550: | ||
var tables = document.querySelectorAll('.wikitable'); | var tables = document.querySelectorAll('.wikitable'); | ||
if (tables.length > 0) { | if (tables.length > 0) { | ||
setTimeout(function() { | |||
applyHighlighting(); | |||
}, 500); | |||
} | } | ||
var customHeadings = document.querySelectorAll('.custom-heading'); | var customHeadings = document.querySelectorAll('.custom-heading'); | ||
if (customHeadings.length > 0) { | if (customHeadings.length > 0) { | ||
addHeadingsWithTOC(); | |||
} | |||
if (currentPageTitle.includes("Обсуждение_участницы:Kerisar")) { | |||
var button = document.createElement("button"); | |||
button.innerText = "Создать архив"; | |||
document.getElementById("container").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; | |||
// Извлекаем содержимое между [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 base64Content = btoa(unescape(encodeURIComponent(extractedContent))); | |||
// Создаем ссылку для скачивания | |||
var link = document.createElement("a"); | |||
link.href = "data:application/octet-stream;base64," + base64Content; | |||
link.download = "archive.txt"; // Имя файла для скачивания | |||
link.innerText = "Скачать архив"; | |||
document.getElementById("container").appendChild(link); | |||
} else { | |||
alert("Теги [testarchivestart] и [testarchivestop] не найдены."); | |||
} | |||
}).catch(function(error) { | |||
console.error("Ошибка:", error); | |||
}); | |||
}; | |||
} | } | ||
}); | }); | ||
| Строка 618: | Строка 596: | ||
if ($.inArray(mw.config.get('wgAction'), ['edit', 'submit']) !== -1) { | if ($.inArray(mw.config.get('wgAction'), ['edit', 'submit']) !== -1) { | ||
mw.loader.load( | mw.loader.load( | ||
'//ru.wikipedia.org/w/index.php?title=MediaWiki:Gadget-wikificator.js&action=raw&ctype=text/javascript' | '//ru.wikipedia.org/w/index.php?title=MediaWiki:Gadget-wikificator.js&action=raw&ctype=text/javascript'); | ||
} | } | ||
var customizeToolbar = function() { | var customizeToolbar = function() { | ||
| Строка 682: | Строка 659: | ||
// Create button starting with hide text | // Create button starting with hide text | ||
// Will be changed to the show text while calculating the maximum button size | // Will be changed to the show text while calculating the maximum button size | ||
var $buttonTemplate = $('<span>').addClass( | var $buttonTemplate = $('<span>').addClass('mw-editsection-like load-page-button').append('[ ', $( | ||
'<span>').addClass('jslink').text(i18n.hideText), ' ]'); | |||
var extractList = function($contentContainer, listClass) { | var extractList = function($contentContainer, listClass) { | ||
var $content = $contentContainer.find('.mw-parser-output > ul > li > ul') | var $content = $contentContainer.find('.mw-parser-output > ul > li > ul').children( | ||
':not(.nbttree-inherited)'); | |||
if (listClass) { | if (listClass) { | ||
$content.addClass(listClass); | $content.addClass(listClass); | ||
| Строка 717: | Строка 693: | ||
$heading.append($button); | $heading.append($button); | ||
// Move the edit button to the right spot | // Move the edit button to the right spot | ||
$contentContainer.find('.mw-editsection, .mw-editsection-like').insertAfter( | $contentContainer.find('.mw-editsection, .mw-editsection-like').insertAfter($button); | ||
// Find max button width, and set its min-width to it | // Find max button width, and set its min-width to it | ||
var hideWidth = $button.width(); | var hideWidth = $button.width(); | ||
| Строка 749: | Строка 724: | ||
$('.pageloader-contentloaded').each(function() { | $('.pageloader-contentloaded').each(function() { | ||
var $fLoader = $(this); | var $fLoader = $(this); | ||
if ($fLoader.data('page') === page && $fLoader.data( | if ($fLoader.data('page') === page && $fLoader.data('pageloader-content')) { | ||
$contentContainer.html($fLoader.data('pageloader-content')).removeClass('noscript'); | |||
$contentContainer.html($fLoader.data('pageloader-content')).removeClass( | |||
mw.hook('wikipage.content').fire($contentContainer); | mw.hook('wikipage.content').fire($contentContainer); | ||
if (treeview) { | if (treeview) { | ||
| Строка 773: | Строка 746: | ||
// $spinner will be false if the content somehow loaded before the module did | // $spinner will be false if the content somehow loaded before the module did | ||
if ($spinner) { | if ($spinner) { | ||
$spinner = $.createSpinner().addClass('mw-editsection-like').css( | $spinner = $.createSpinner().addClass('mw-editsection-like').css('min-width', $button.css( | ||
'min-width')); | |||
$button.hide().after($spinner); | $button.hide().after($spinner); | ||
} | } | ||
| Строка 797: | Строка 770: | ||
var $link = $(this); | var $link = $(this); | ||
if ($link.attr('href') === curPage) { | if ($link.attr('href') === curPage) { | ||
$link.replaceWith($('<strong>').addClass('selflink').append( | $link.replaceWith($('<strong>').addClass('selflink').append($link.contents())); | ||
} | } | ||
}); | }); | ||