MediaWiki:Common.js: различия между версиями
Страница интерфейса MediaWiki
Дополнительные действия
Dantes (обсуждение | вклад) Нет описания правки Метка: ручная отмена |
Defer (обсуждение | вклад) Нет описания правки |
||
| (не показано 35 промежуточных версий 3 участников) | |||
| Строка 1: | Строка 1: | ||
/* Загрузка лого морпехов */ | |||
$(document).ready(function() { | |||
var secondUrl = "https://spacestories.club/Marine_Corps"; | |||
var secondLogoImgUrl = "https://spacestories.club/images/0/0d/CMlog.png"; | |||
if ($('.second-mw-logo').length === 0) { | |||
var $secondLogo = $('<a>', { | |||
href: secondUrl, | |||
class: 'second-mw-logo citizen-cdx-button--size-large cdx-button cdx-button--fake-button cdx-button--fake-button--enabled cdx-button--icon-only cdx-button--weight-quiet', | |||
title: 'Перейти на заглавную страницу Marines Corps', | |||
}).append( | |||
$('<img>', { | |||
class: 'mw-logo-icon', | |||
src: secondLogoImgUrl, | |||
alt: 'Второе лого', | |||
'aria-hidden': 'true', | |||
height: '32', | |||
width: '32' | |||
}) | |||
); | |||
$('.mw-logo').after($secondLogo); | |||
} | |||
}); | |||
/* Аудиоплееры */ | |||
(function(){ | |||
var players = document.getElementsByClassName('audio-player'); | var players = document.getElementsByClassName('audio-player'); | ||
for (var i = 0; i < players.length; i++) { | for(var i=0;i<players.length;i++){ | ||
var src = | (function(p){ | ||
var src = p.getAttribute('data-src'); | |||
if(src){ | |||
var audio = document.createElement('audio'); | |||
audio.setAttribute('controls','controls'); | |||
audio.setAttribute('preload','none'); | |||
var source = document.createElement('source'); | |||
source.setAttribute('src',src); | |||
source.setAttribute('type','audio/mpeg'); | |||
audio.appendChild(source); | |||
} | p.appendChild(audio); | ||
} | |||
})(players[i]); | |||
} | } | ||
}); | })(); | ||
/* Подгрузка внешних CSS/JS */ | |||
(function(){ | |||
if(typeof mw === 'undefined') return; | |||
function getFilesFromUrl(param){ | |||
if(!param) return []; | |||
return param.split('|').map(function(file){ return file.trim(); }); | |||
function getFilesFromUrl(param) { | |||
if (!param) return []; | |||
return param.split('|').map(function(file) { return file.trim(); }); | |||
} | } | ||
function getBaseUrl() { | function getBaseUrl(){ | ||
var server = mw.config.get('wgServer').replace(/^http:/, 'https:'); | var server = mw.config.get('wgServer').replace(/^http:/,'https:'); | ||
var script = mw.config.get('wgScript'); | var script = mw.config.get('wgScript'); | ||
return server + script + '?action=raw&ctype=text/'; | return server + script + '?action=raw&ctype=text/'; | ||
} | } | ||
function isValidExtension(ext) { return ext === 'js' || ext === 'css'; } | function isValidExtension(ext){ return ext === 'js' || ext === 'css'; } | ||
function getFileUrl(file) { | function getFileUrl(file){ | ||
var prefix = file.indexOf('MediaWiki:') === 0 ? 'MediaWiki:' : 'User:' + (mw.config.get('wgUserName') || '') + '/'; | var prefix = file.indexOf('MediaWiki:') === 0 ? 'MediaWiki:' : 'User:' + (mw.config.get('wgUserName') || '') + '/'; | ||
var fullName = file.indexOf(':') > -1 ? file : prefix + file; | var fullName = file.indexOf(':') > -1 ? file : prefix + file; | ||
var ext = file.split('.').pop().toLowerCase(); | var ext = file.split('.').pop().toLowerCase(); | ||
if (!isValidExtension(ext)) { | if(!isValidExtension(ext)){ | ||
console.error('Недопустимое расширение файла:', file); | console.error('Недопустимое расширение файла:', file); | ||
return null; | return null; | ||
} | } | ||
var timestamp = new Date().getTime(); | var timestamp = new Date().getTime(); | ||
return getBaseUrl() + (ext === 'js' ? 'javascript' : 'css') + '&title=' + encodeURIComponent(fullName) + '&_=' + timestamp; | return getBaseUrl() + (ext==='js' ? 'javascript' : 'css') + '&title=' + encodeURIComponent(fullName) + '&_=' + timestamp; | ||
} | } | ||
function loadFiles(files) { | function loadFiles(files){ | ||
files. | for(var i=0;i<files.length;i++){ | ||
var url = getFileUrl( | var url = getFileUrl(files[i]); | ||
if (url) { | if(url){ | ||
var ext = | var ext = files[i].split('.').pop().toLowerCase(); | ||
mw.loader.load(url, 'text/' + (ext === 'js' ? 'javascript' : 'css')); | mw.loader.load(url,'text/' + (ext==='js' ? 'javascript' : 'css')); | ||
} | } | ||
} | } | ||
} | } | ||
var params = mw.util.getParamValue('use'); | mw.loader.using('mediawiki.util', function(){ | ||
var params = mw.util.getParamValue('use'); | |||
var files = getFilesFromUrl(params); | |||
}); | loadFiles(files); | ||
}); | |||
})(); | |||
/* | /* Перенос page-info и цвет заголовков */ | ||
(function(){ | |||
var footerPlaces = document.getElementById('footer-places'); | |||
(function() { | var pageInfo = document.querySelector('.page-info'); | ||
if(footerPlaces && pageInfo){ | |||
footerPlaces.insertAdjacentElement('afterend', pageInfo.cloneNode(true)); | |||
pageInfo.parentNode.removeChild(pageInfo); | |||
var | |||
if ( | |||
} | } | ||
var content = headerColorElement.textContent.split('|'); | var headerColorElement = document.querySelector('.headerColor'); | ||
if(headerColorElement){ | |||
var content = headerColorElement.textContent.split('|'); | |||
if(content.length === 2){ | |||
var headers = document.querySelectorAll('.citizen-section-heading, .citizen-section-heading--collapsed'); | var headers = document.querySelectorAll('.citizen-section-heading, .citizen-section-heading--collapsed'); | ||
for (var | for(var hi=0; hi<headers.length; hi++){ | ||
var header = headers[ | var header = headers[hi], | ||
indicator = header.querySelector('.citizen-section-indicator'), | indicator = header.querySelector('.citizen-section-indicator'), | ||
headline = header.querySelector('.mw-headline'); | headline = header.querySelector('.mw-headline'); | ||
if(!indicator || !headline) continue; | |||
if (header.classList.contains('citizen-section-heading--collapsed')) { | if(header.classList.contains('citizen-section-heading--collapsed')){ | ||
indicator.style.cssText = 'background: black; box-shadow: unset;'; | |||
} else | } else { | ||
indicator.style.cssText = 'background: ' + content[1] + '; box-shadow: 0 0 20px 0px ' + content[1] + 'cc;'; | indicator.style.cssText = 'background: ' + content[1] + '; box-shadow: 0 0 20px 0px ' + content[1] + 'cc;'; | ||
headline.style.cssText = 'border-image: linear-gradient(to right top, ' + content[0] + ', black); border-image-slice: 1;'; | headline.style.cssText = 'border-image: linear-gradient(to right top,' + content[0] + ', black); border-image-slice:1;'; | ||
} | } | ||
} | } | ||
} | } | ||
} | } | ||
} | })(); | ||
/* | /* Sidebar для ролей */ | ||
(function(){ | |||
var jobsContainer = document.querySelector('.JobsTableContainer'); | |||
var jobsContainer = document.querySelector('.JobsTableContainer'); | if(jobsContainer && jobsContainer.innerHTML.trim()){ | ||
if (jobsContainer && jobsContainer.innerHTML.trim()) { | var bodyContent = document.getElementById('bodyContent'); | ||
if(bodyContent){ | |||
bodyContent.insertAdjacentHTML('beforebegin', jobsContainer.innerHTML); | |||
var jobTable = document.getElementById('IdJobsTableContainer1'); | |||
if(jobTable) jobTable.id = 'IdJobsTableContainer2'; | |||
} | |||
} | } | ||
} | })(); | ||
/* | /* Хронология */ | ||
(function(){ | |||
if(!window.jQuery) return; | |||
if (window.jQuery) | jQuery(function($){ | ||
jQuery(function($) { | $('.timeline-header').on('click', function(){ | ||
$('.timeline-header').on('click', function() { | |||
$(this).next('.timeline-content').slideToggle(); | $(this).next('.timeline-content').slideToggle(); | ||
}).trigger('click'); | }).trigger('click'); | ||
}); | }); | ||
} | })(); | ||
/* | /* Галерея */ | ||
(function(){ | (function(){ | ||
var root=document.getElementById('ss-art-gallery'); | var root = document.getElementById('ss-art-gallery'); | ||
if(!root) return; | if(!root) return; | ||
function q(a,b){return a.querySelector(b);} | function q(a,b){ return a.querySelector(b); } | ||
function qa(a,b){return Array.prototype.slice.call(a.querySelectorAll(b));} | function qa(a,b){ return Array.prototype.slice.call(a.querySelectorAll(b)); } | ||
var chips=qa(root,'.ss-chip'); | |||
var chips = qa(root,'.ss-chip'); | |||
function setFilter(val){ | function setFilter(val){ | ||
chips. | for(var ci=0; ci<chips.length; ci++){ | ||
c | var c = chips[ci]; | ||
} | var active = (c.getAttribute('data-filter')===val || (val==='all' && c.getAttribute('data-filter')==='all')); | ||
c.classList.toggle('ss-chip-active', active); | |||
} | |||
qa(root,'.ss-section'). | var sections = qa(root,'.ss-section'); | ||
var cards=qa(section,'.ss-card'); | for(var si=0; si<sections.length; si++){ | ||
var visibleCount=0; | var section = sections[si]; | ||
cards. | var cards = qa(section,'.ss-card'); | ||
var show=(val==='all'||val===card.getAttribute('data-artist')); | var visibleCount = 0; | ||
card.classList.toggle('ss-hidden',!show); | for(var cj=0; cj<cards.length; cj++){ | ||
var card = cards[cj]; | |||
var show = (val==='all' || val===card.getAttribute('data-artist')); | |||
card.classList.toggle('ss-hidden', !show); | |||
if(show) visibleCount++; | if(show) visibleCount++; | ||
} | } | ||
section.style.display=(val==='all'||visibleCount>0)?'block':'none'; | section.style.display = (val==='all' || visibleCount>0)?'block':'none'; | ||
} | } | ||
} | } | ||
chips. | for(var i=0;i<chips.length;i++){ | ||
(function(ch){ | |||
ch.addEventListener('click', function(){ setFilter(ch.getAttribute('data-filter')); }); | |||
})(chips[i]); | |||
} | |||
setFilter('all'); | setFilter('all'); | ||
var modal=document.createElement('div'); | var modal = document.createElement('div'); | ||
modal.className='ss-modal'; | modal.className = 'ss-modal'; | ||
modal.innerHTML='<div class="ss-modal-inner"><img class="ss-modal-img" alt=""/></div><div class="ss-modal-close" role="button">✖ Закрыть</div>'; | modal.innerHTML = '<div class="ss-modal-inner"><img class="ss-modal-img" alt=""/></div><div class="ss-modal-close" role="button">✖ Закрыть</div>'; | ||
root.appendChild(modal); | root.appendChild(modal); | ||
function originalFromThumb(u){ | var modalImg = q(modal,'.ss-modal-img'); | ||
function originalFromThumb(u){ | |||
if(!u) return u; | if(!u) return u; | ||
if(u.indexOf('/thumb/')>-1){ | if(u.indexOf('/thumb/')>-1){ | ||
var s=u.replace('/thumb/','/'); s=s.replace(/\/[^\/]*$/,''); return s; | var s = u.replace('/thumb/','/'); | ||
s = s.replace(/\/[^\/]*$/,''); | |||
return s; | |||
} | } | ||
return u; | return u; | ||
} | } | ||
qa(root,'.ss-card img'). | var images = qa(root,'.ss-card img'); | ||
for(var ii=0; ii<images.length; ii++){ | |||
(function(img){ | |||
img.style.cursor='zoom-in'; | |||
img.addEventListener('click', function(e){ | |||
e.preventDefault(); | |||
var src = originalFromThumb(img.getAttribute('src')) || img.getAttribute('src'); | |||
modalImg.setAttribute('src',src); | |||
modalImg.style.maxWidth='90vw'; | |||
}); | modalImg.style.maxHeight='90vh'; | ||
} | modal.classList.add('open'); | ||
}); | |||
})(images[ii]); | |||
} | |||
function closeModal(){ modal.classList.remove('open'); } | function closeModal(){ modal.classList.remove('open'); } | ||
modal.addEventListener('click',function(e){ if(e.target===modal||e.target.classList.contains('ss-modal-close')) closeModal(); }); | modal.addEventListener('click', function(e){ if(e.target===modal||e.target.classList.contains('ss-modal-close')) closeModal(); }); | ||
document.addEventListener('keydown',function(e){ if(e.key==='Escape') closeModal(); }); | document.addEventListener('keydown', function(e){ if(e.key==='Escape') closeModal(); }); | ||
})(); | })(); | ||
/* | /* Меню лора */ | ||
(function(){ | |||
(function() { | |||
var items = document.querySelectorAll('.custom-item'); | var items = document.querySelectorAll('.custom-item'); | ||
for(var mi=0; mi<items.length; mi++){ | |||
for (var | (function(item){ | ||
(function(item) { | |||
var icon = item.querySelector('.custom-icon'); | var icon = item.querySelector('.custom-icon'); | ||
var linkEl = item.querySelector('a'); | var linkEl = item.querySelector('a'); | ||
if (!linkEl) return; | if(!linkEl) return; | ||
var href = linkEl.getAttribute('href'); | var href = linkEl.getAttribute('href'); | ||
item.style.cursor = 'pointer'; | item.style.cursor='pointer'; | ||
item.onclick = function() { window.location.href = href; }; | item.onclick = function(){ window.location.href = href; }; | ||
item.onmousemove = function(e) { | item.onmousemove = function(e){ | ||
var rect = item.getBoundingClientRect(); | var rect = item.getBoundingClientRect(); | ||
var x = e.clientX - rect.left; | var x = e.clientX - rect.left; | ||
var y = e.clientY - rect.top; | var y = e.clientY - rect.top; | ||
var moveX = (x - rect.width/2) * 0.02; | var moveX = (x - rect.width/2)*0.02; | ||
var moveY = (y - rect.height/2) * 0.02; | var moveY = (y - rect.height/2)*0.02; | ||
item.style.backgroundPosition = (50 + moveX) + '% ' + (50 + moveY) + '%'; | item.style.backgroundPosition = (50 + moveX) + '% ' + (50 + moveY) + '%'; | ||
if(icon) icon.style.transform = 'translateY(-8px) scale(1.08)'; | if(icon) icon.style.transform = 'translateY(-8px) scale(1.08)'; | ||
var hue = Math.round((x / rect.width) * 360); | var hue = Math.round((x/rect.width)*360); | ||
item.style.boxShadow = | item.style.boxShadow = | ||
'0 0 15px hsla(' + hue + ', 100%, 60%, 0.7), ' + | '0 0 15px hsla(' + hue + ',100%,60%,0.7), ' + | ||
'0 0 30px hsla(' + ((hue+30)%360) + ', 100%, 50%, 0.4), ' + | '0 0 30px hsla(' + ((hue+30)%360) + ',100%,50%,0.4), ' + | ||
'0 8px 20px rgba(0,0,0,0.5)'; | '0 8px 20px rgba(0,0,0,0.5)'; | ||
}; | }; | ||
item.onmouseleave = function() { | item.onmouseleave = function(){ | ||
item.style.backgroundPosition = '50% 50%'; | item.style.backgroundPosition='50% 50%'; | ||
if(icon) icon.style.transform = 'translateY(0) scale(1)'; | if(icon) icon.style.transform='translateY(0) scale(1)'; | ||
item.style.boxShadow = '0 4px 10px rgba(0,0,0,0.3)'; | item.style.boxShadow='0 4px 10px rgba(0,0,0,0.3)'; | ||
}; | }; | ||
})(items[ | })(items[mi]); | ||
} | } | ||
})(); | })(); | ||
/* | /* Химия */ | ||
(function() { | |||
if (typeof mw === 'undefined' || !window.document) return; | |||
(function(){ | |||
function initCollapse() { | |||
var headings = document.querySelectorAll('.chem-heading'); | |||
var hi = 0; | |||
var len = headings.length; | |||
for (; hi < len; hi++) { | |||
(function(node) { | |||
if (node.getAttribute('data-chem-attached')) return; | |||
node.setAttribute('data-chem-attached', '1'); | |||
node.style.cursor = 'pointer'; | |||
node.addEventListener('click', function() { | |||
var kind = node.getAttribute('data-kind') || ''; | |||
var wrapper = findWrapper(node, kind); | |||
if (!wrapper) return; | |||
var btn = node.querySelector('.collapse-btn'); | |||
if (wrapper.classList.contains('collapsed')) { | |||
wrapper.style.maxHeight = wrapper.scrollHeight + 'px'; | |||
wrapper.classList.remove('collapsed'); | |||
wrapper.classList.add('expanded'); | |||
if (btn) btn.textContent = 'свернуть'; | |||
var cleanup = function() { | |||
wrapper.style.maxHeight = ''; | |||
wrapper.removeEventListener('transitionend', cleanup); | |||
}; | |||
wrapper.addEventListener('transitionend', cleanup); | |||
} else { | |||
var currentHeight = wrapper.scrollHeight; | |||
wrapper.style.maxHeight = currentHeight + 'px'; | |||
wrapper.offsetHeight; | |||
wrapper.style.maxHeight = '0px'; | |||
wrapper.classList.remove('expanded'); | |||
wrapper.classList.add('collapsed'); | |||
if (btn) btn.textContent = 'развернуть'; | |||
} | |||
}); | |||
})(headings[hi]); | |||
} | |||
} | |||
function findWrapper(node, kind) { | |||
var parent = node.parentNode; | |||
if (!parent) return null; | |||
var wrappers = parent.querySelectorAll('.collapsible'); | |||
var wi = 0; | |||
var wlen = wrappers.length; | |||
for (; wi < wlen; wi++) { | |||
if (wrappers[wi].getAttribute('data-kind') === kind) { | |||
return wrappers[wi]; | |||
} | |||
} | |||
return null; | |||
} | |||
if (document.readyState === 'complete' || document.readyState === 'interactive') { | |||
initCollapse(); | |||
} else { | |||
document.addEventListener('DOMContentLoaded', initCollapse); | |||
} | |||
})(); | |||
/* Каталог страниц Marine Corps ----------------------------------------------------------------- */ | |||
(function () { | |||
if (typeof mw === 'undefined' || !window.document) return; | |||
var statusMap = { | |||
green: { emoji: '🟢', label: 'Готово', words: ['готово', 'готов'] }, | |||
yellow: { emoji: '🟡', label: 'Нужно обновить', words: ['нужно обновить', 'обновить', 'устарело'] }, | |||
red: { emoji: '🔴', label: 'Нет страницы', words: ['нет страницы', 'нет', 'пусто'] }, | |||
blue: { emoji: '🔵', label: 'Заморожено', words: ['заморожено', 'заморожен'] } | |||
}; | |||
function ready(fn) { | |||
if (document.readyState === 'complete' || document.readyState === 'interactive') { | |||
fn(); | |||
} else { | |||
document.addEventListener('DOMContentLoaded', fn); | |||
} | |||
} | |||
ready(function () { | |||
var catalog = document.querySelector('.scmc-catalog'); | |||
if (!catalog) return; | |||
var tools = catalog.querySelector('.scmc-catalog-tools'); | |||
var rows = Array.prototype.slice.call(catalog.querySelectorAll('.scmc-catalog-row')); | |||
if (!tools || !rows.length) return; | |||
buildTools(tools); | |||
enhanceRows(rows); | |||
var search = tools.querySelector('.scmc-catalog-search'); | |||
var filters = Array.prototype.slice.call(tools.querySelectorAll('.scmc-catalog-filter')); | |||
var counter = tools.querySelector('.scmc-catalog-counter'); | |||
var activeStatus = 'all'; | |||
update(); | |||
search.addEventListener('input', update); | |||
filters.forEach(function (button) { | |||
button.addEventListener('click', function () { | |||
activeStatus = button.getAttribute('data-status') || 'all'; | |||
filters.forEach(function (other) { | |||
other.classList.toggle('is-active', other === button); | |||
}); | |||
update(); | |||
}); | |||
}); | |||
document.addEventListener('click', function (event) { | |||
var statusButton = event.target.closest('.scmc-catalog-status'); | |||
if (statusButton) { | |||
event.preventDefault(); | |||
event.stopPropagation(); | |||
openStatusMenu(statusButton); | |||
return; | |||
} | |||
var menu = document.querySelector('.scmc-status-menu'); | |||
if (menu && !event.target.closest('.scmc-status-menu')) { | |||
menu.remove(); | |||
} | } | ||
}); | }); | ||
function buildTools(target) { | |||
var | target.innerHTML = ''; | ||
if(! | |||
var input = document.createElement('input'); | |||
input.className = 'scmc-catalog-search'; | |||
input.type = 'search'; | |||
input.placeholder = 'Поиск по названию, разделу, статусу или комментарию...'; | |||
var filtersWrap = document.createElement('div'); | |||
filtersWrap.className = 'scmc-catalog-filters'; | |||
[ | |||
['all', 'Все'], | |||
['green', '🟢 Готово'], | |||
['yellow', '🟡 Нужно обновить'], | |||
['red', '🔴 Нет страницы'], | |||
['blue', '🔵 Заморожено'], | |||
['problem', 'Проблемные'] | |||
].forEach(function (item, index) { | |||
var button = document.createElement('button'); | |||
button.type = 'button'; | |||
button.className = 'scmc-catalog-filter' + (index === 0 ? ' is-active' : ''); | |||
button.setAttribute('data-status', item[0]); | |||
button.textContent = item[1]; | |||
filtersWrap.appendChild(button); | |||
}); | |||
var count = document.createElement('div'); | |||
count.className = 'scmc-catalog-counter'; | |||
target.appendChild(input); | |||
target.appendChild(filtersWrap); | |||
target.appendChild(count); | |||
} | |||
function enhanceRows(rowList) { | |||
rowList.forEach(function (row) { | |||
var status = normalizeStatus(row.getAttribute('data-status')); | |||
row.setAttribute('data-status', status); | |||
var note = row.getAttribute('data-note'); | |||
if (note && !row.querySelector('.scmc-catalog-note')) { | |||
var noteNode = document.createElement('span'); | |||
noteNode.className = 'scmc-catalog-note'; | |||
noteNode.textContent = note; | |||
row.appendChild(noteNode); | |||
} | |||
var button = document.createElement('button'); | |||
button.type = 'button'; | |||
button.className = 'scmc-catalog-status'; | |||
button.setAttribute('data-status', status); | |||
button.title = 'Изменить статус'; | |||
button.textContent = statusMap[status].emoji; | |||
row.appendChild(button); | |||
}); | |||
} | |||
function openStatusMenu(button) { | |||
var oldMenu = document.querySelector('.scmc-status-menu'); | |||
if (oldMenu) oldMenu.remove(); | |||
var row = button.closest('.scmc-catalog-row'); | |||
if (!row) return; | |||
var rect = button.getBoundingClientRect(); | |||
var menu = document.createElement('div'); | |||
menu.className = 'scmc-status-menu'; | |||
[ | |||
['green', '🟢 Готово'], | |||
['yellow', '🟡 Нужно обновить'], | |||
['red', '🔴 Нет страницы'], | |||
['blue', '🔵 Заморожено'] | |||
].forEach(function (item) { | |||
var status = item[0]; | |||
var label = item[1]; | |||
var option = document.createElement('button'); | |||
option.type = 'button'; | |||
option.textContent = label; | |||
option.addEventListener('click', function (event) { | |||
event.preventDefault(); | |||
event.stopPropagation(); | |||
menu.remove(); | |||
changeStatus(row, button, status); | |||
}); | |||
menu.appendChild(option); | |||
}); | |||
document.body.appendChild(menu); | |||
var left = Math.min(rect.left, window.innerWidth - menu.offsetWidth - 12); | |||
var top = rect.bottom + 8; | |||
menu.style.left = Math.max(12, left) + 'px'; | |||
menu.style.top = top + 'px'; | |||
} | |||
function changeStatus(row, button, newStatus) { | |||
var oldStatus = row.getAttribute('data-status'); | |||
if (!newStatus || newStatus === oldStatus) return; | |||
var page = row.getAttribute('data-page') || ''; | |||
var title = row.getAttribute('data-title') || page; | |||
button.classList.add('scmc-status-saving'); | |||
saveStatus(page, oldStatus, newStatus, title) | |||
.then(function () { | |||
row.setAttribute('data-status', newStatus); | |||
button.setAttribute('data-status', newStatus); | |||
button.textContent = statusMap[newStatus].emoji; | |||
button.classList.remove('scmc-status-saving'); | |||
update(); | |||
}) | |||
.catch(function (error) { | |||
button.classList.remove('scmc-status-saving'); | |||
alert(error.message || 'Не удалось сохранить статус. Проверьте вход в аккаунт и права редактора.'); | |||
}); | |||
} | |||
function saveStatus(page, oldStatus, newStatus, title) { | |||
var api = new mw.Api(); | |||
var currentPage = mw.config.get('wgPageName'); | |||
return api.get({ | |||
action: 'query', | |||
prop: 'revisions', | |||
titles: currentPage, | |||
rvprop: 'content', | |||
rvslots: 'main', | |||
formatversion: 2 | |||
}).then(function (data) { | |||
var pageData = data.query.pages[0]; | |||
var content = pageData.revisions[0].slots.main.content; | |||
var result = replaceStatusInSource(content, page, oldStatus, newStatus); | |||
if (!result.changed) { | |||
throw new Error('Не нашёл строку для страницы: ' + title); | |||
} | |||
if (result.ambiguous) { | |||
throw new Error('Нашлось несколько строк для страницы: ' + title + '. Лучше изменить вручную.'); | |||
} | |||
return api.postWithToken('csrf', { | |||
action: 'edit', | |||
title: currentPage, | |||
text: result.text, | |||
summary: 'Обновление статуса страницы: ' + title, | |||
minor: true | |||
}); | |||
}); | |||
} | |||
function replaceStatusInSource(content, page, oldStatus, newStatus) { | |||
var lines = content.split('\n'); | |||
var indexes = []; | |||
for (var i = 0; i < lines.length; i++) { | |||
if (lineMatchesPage(lines[i], page) && lineHasStatus(lines[i], oldStatus)) { | |||
indexes.push(i); | |||
} | |||
} | |||
if (!indexes.length) { | |||
return { changed: false, ambiguous: false, text: content }; | |||
} | |||
if (indexes.length > 1) { | |||
return { changed: false, ambiguous: true, text: content }; | |||
} | |||
var index = indexes[0]; | |||
lines[index] = lines[index].replace(/data-status=(["'])(green|yellow|red|blue)\1/, 'data-status=$1' + newStatus + '$1'); | |||
return { changed: true, ambiguous: false, text: lines.join('\n') }; | |||
} | |||
function lineMatchesPage(line, page) { | |||
return line.indexOf('data-page="' + page + '"') !== -1 || line.indexOf("data-page='" + page + "'") !== -1; | |||
} | |||
function lineHasStatus(line, status) { | |||
return line.indexOf('data-status="' + status + '"') !== -1 || line.indexOf("data-status='" + status + "'") !== -1; | |||
} | |||
function update() { | |||
var query = clean(search.value).toLowerCase(); | |||
var words = query ? query.split(/\s+/).filter(Boolean) : []; | |||
var visible = 0; | |||
rows.forEach(function (row) { | |||
var status = row.getAttribute('data-status'); | |||
var text = buildSearchText(row).toLowerCase(); | |||
var statusOk = | |||
activeStatus === 'all' || | |||
activeStatus === status || | |||
(activeStatus === 'problem' && status !== 'green'); | |||
var textOk = words.every(function (word) { | |||
return text.indexOf(word) !== -1 || statusWordMatches(status, word); | |||
}); | |||
var show = statusOk && textOk; | |||
row.classList.toggle('scmc-catalog-hidden', !show); | |||
if (show) visible++; | |||
}); | |||
counter.textContent = 'Показано: ' + visible + ' из ' + rows.length; | |||
} | |||
function buildSearchText(row) { | |||
var status = row.getAttribute('data-status'); | |||
var note = row.getAttribute('data-note') || ''; | |||
var words = statusMap[status] ? statusMap[status].words.join(' ') : ''; | |||
return [ | |||
row.getAttribute('data-title') || '', | |||
row.getAttribute('data-page') || '', | |||
row.getAttribute('data-section') || '', | |||
note, | |||
status, | |||
words, | |||
row.textContent || '' | |||
].join(' '); | |||
} | |||
function statusWordMatches(status, word) { | |||
if (!statusMap[status]) return false; | |||
var words = statusMap[status].words; | |||
for (var i = 0; i < words.length; i++) { | |||
if (words[i].indexOf(word) !== -1 || word.indexOf(words[i]) !== -1) return true; | |||
} | |||
return false; | |||
} | |||
function normalizeStatus(status) { | |||
return statusMap[status] ? status : 'blue'; | |||
} | |||
function clean(text) { | |||
return String(text || '').replace(/\s+/g, ' ').trim(); | |||
} | |||
}); | |||
})(); | })(); | ||
/* | /* Каталог страниц Marine Corps --------------------------------------------------------------END */ | ||
Текущая версия от 20:07, 12 июня 2026
/* Загрузка лого морпехов */
$(document).ready(function() {
var secondUrl = "https://spacestories.club/Marine_Corps";
var secondLogoImgUrl = "https://spacestories.club/images/0/0d/CMlog.png";
if ($('.second-mw-logo').length === 0) {
var $secondLogo = $('<a>', {
href: secondUrl,
class: 'second-mw-logo citizen-cdx-button--size-large cdx-button cdx-button--fake-button cdx-button--fake-button--enabled cdx-button--icon-only cdx-button--weight-quiet',
title: 'Перейти на заглавную страницу Marines Corps',
}).append(
$('<img>', {
class: 'mw-logo-icon',
src: secondLogoImgUrl,
alt: 'Второе лого',
'aria-hidden': 'true',
height: '32',
width: '32'
})
);
$('.mw-logo').after($secondLogo);
}
});
/* Аудиоплееры */
(function(){
var players = document.getElementsByClassName('audio-player');
for(var i=0;i<players.length;i++){
(function(p){
var src = p.getAttribute('data-src');
if(src){
var audio = document.createElement('audio');
audio.setAttribute('controls','controls');
audio.setAttribute('preload','none');
var source = document.createElement('source');
source.setAttribute('src',src);
source.setAttribute('type','audio/mpeg');
audio.appendChild(source);
p.appendChild(audio);
}
})(players[i]);
}
})();
/* Подгрузка внешних CSS/JS */
(function(){
if(typeof mw === 'undefined') return;
function getFilesFromUrl(param){
if(!param) return [];
return param.split('|').map(function(file){ return file.trim(); });
}
function getBaseUrl(){
var server = mw.config.get('wgServer').replace(/^http:/,'https:');
var script = mw.config.get('wgScript');
return server + script + '?action=raw&ctype=text/';
}
function isValidExtension(ext){ return ext === 'js' || ext === 'css'; }
function getFileUrl(file){
var prefix = file.indexOf('MediaWiki:') === 0 ? 'MediaWiki:' : 'User:' + (mw.config.get('wgUserName') || '') + '/';
var fullName = file.indexOf(':') > -1 ? file : prefix + file;
var ext = file.split('.').pop().toLowerCase();
if(!isValidExtension(ext)){
console.error('Недопустимое расширение файла:', file);
return null;
}
var timestamp = new Date().getTime();
return getBaseUrl() + (ext==='js' ? 'javascript' : 'css') + '&title=' + encodeURIComponent(fullName) + '&_=' + timestamp;
}
function loadFiles(files){
for(var i=0;i<files.length;i++){
var url = getFileUrl(files[i]);
if(url){
var ext = files[i].split('.').pop().toLowerCase();
mw.loader.load(url,'text/' + (ext==='js' ? 'javascript' : 'css'));
}
}
}
mw.loader.using('mediawiki.util', function(){
var params = mw.util.getParamValue('use');
var files = getFilesFromUrl(params);
loadFiles(files);
});
})();
/* Перенос page-info и цвет заголовков */
(function(){
var footerPlaces = document.getElementById('footer-places');
var pageInfo = document.querySelector('.page-info');
if(footerPlaces && pageInfo){
footerPlaces.insertAdjacentElement('afterend', pageInfo.cloneNode(true));
pageInfo.parentNode.removeChild(pageInfo);
}
var headerColorElement = document.querySelector('.headerColor');
if(headerColorElement){
var content = headerColorElement.textContent.split('|');
if(content.length === 2){
var headers = document.querySelectorAll('.citizen-section-heading, .citizen-section-heading--collapsed');
for(var hi=0; hi<headers.length; hi++){
var header = headers[hi],
indicator = header.querySelector('.citizen-section-indicator'),
headline = header.querySelector('.mw-headline');
if(!indicator || !headline) continue;
if(header.classList.contains('citizen-section-heading--collapsed')){
indicator.style.cssText = 'background: black; box-shadow: unset;';
} else {
indicator.style.cssText = 'background: ' + content[1] + '; box-shadow: 0 0 20px 0px ' + content[1] + 'cc;';
headline.style.cssText = 'border-image: linear-gradient(to right top,' + content[0] + ', black); border-image-slice:1;';
}
}
}
}
})();
/* Sidebar для ролей */
(function(){
var jobsContainer = document.querySelector('.JobsTableContainer');
if(jobsContainer && jobsContainer.innerHTML.trim()){
var bodyContent = document.getElementById('bodyContent');
if(bodyContent){
bodyContent.insertAdjacentHTML('beforebegin', jobsContainer.innerHTML);
var jobTable = document.getElementById('IdJobsTableContainer1');
if(jobTable) jobTable.id = 'IdJobsTableContainer2';
}
}
})();
/* Хронология */
(function(){
if(!window.jQuery) return;
jQuery(function($){
$('.timeline-header').on('click', function(){
$(this).next('.timeline-content').slideToggle();
}).trigger('click');
});
})();
/* Галерея */
(function(){
var root = document.getElementById('ss-art-gallery');
if(!root) return;
function q(a,b){ return a.querySelector(b); }
function qa(a,b){ return Array.prototype.slice.call(a.querySelectorAll(b)); }
var chips = qa(root,'.ss-chip');
function setFilter(val){
for(var ci=0; ci<chips.length; ci++){
var c = chips[ci];
var active = (c.getAttribute('data-filter')===val || (val==='all' && c.getAttribute('data-filter')==='all'));
c.classList.toggle('ss-chip-active', active);
}
var sections = qa(root,'.ss-section');
for(var si=0; si<sections.length; si++){
var section = sections[si];
var cards = qa(section,'.ss-card');
var visibleCount = 0;
for(var cj=0; cj<cards.length; cj++){
var card = cards[cj];
var show = (val==='all' || val===card.getAttribute('data-artist'));
card.classList.toggle('ss-hidden', !show);
if(show) visibleCount++;
}
section.style.display = (val==='all' || visibleCount>0)?'block':'none';
}
}
for(var i=0;i<chips.length;i++){
(function(ch){
ch.addEventListener('click', function(){ setFilter(ch.getAttribute('data-filter')); });
})(chips[i]);
}
setFilter('all');
var modal = document.createElement('div');
modal.className = 'ss-modal';
modal.innerHTML = '<div class="ss-modal-inner"><img class="ss-modal-img" alt=""/></div><div class="ss-modal-close" role="button">✖ Закрыть</div>';
root.appendChild(modal);
var modalImg = q(modal,'.ss-modal-img');
function originalFromThumb(u){
if(!u) return u;
if(u.indexOf('/thumb/')>-1){
var s = u.replace('/thumb/','/');
s = s.replace(/\/[^\/]*$/,'');
return s;
}
return u;
}
var images = qa(root,'.ss-card img');
for(var ii=0; ii<images.length; ii++){
(function(img){
img.style.cursor='zoom-in';
img.addEventListener('click', function(e){
e.preventDefault();
var src = originalFromThumb(img.getAttribute('src')) || img.getAttribute('src');
modalImg.setAttribute('src',src);
modalImg.style.maxWidth='90vw';
modalImg.style.maxHeight='90vh';
modal.classList.add('open');
});
})(images[ii]);
}
function closeModal(){ modal.classList.remove('open'); }
modal.addEventListener('click', function(e){ if(e.target===modal||e.target.classList.contains('ss-modal-close')) closeModal(); });
document.addEventListener('keydown', function(e){ if(e.key==='Escape') closeModal(); });
})();
/* Меню лора */
(function(){
var items = document.querySelectorAll('.custom-item');
for(var mi=0; mi<items.length; mi++){
(function(item){
var icon = item.querySelector('.custom-icon');
var linkEl = item.querySelector('a');
if(!linkEl) return;
var href = linkEl.getAttribute('href');
item.style.cursor='pointer';
item.onclick = function(){ window.location.href = href; };
item.onmousemove = function(e){
var rect = item.getBoundingClientRect();
var x = e.clientX - rect.left;
var y = e.clientY - rect.top;
var moveX = (x - rect.width/2)*0.02;
var moveY = (y - rect.height/2)*0.02;
item.style.backgroundPosition = (50 + moveX) + '% ' + (50 + moveY) + '%';
if(icon) icon.style.transform = 'translateY(-8px) scale(1.08)';
var hue = Math.round((x/rect.width)*360);
item.style.boxShadow =
'0 0 15px hsla(' + hue + ',100%,60%,0.7), ' +
'0 0 30px hsla(' + ((hue+30)%360) + ',100%,50%,0.4), ' +
'0 8px 20px rgba(0,0,0,0.5)';
};
item.onmouseleave = function(){
item.style.backgroundPosition='50% 50%';
if(icon) icon.style.transform='translateY(0) scale(1)';
item.style.boxShadow='0 4px 10px rgba(0,0,0,0.3)';
};
})(items[mi]);
}
})();
/* Химия */
(function() {
if (typeof mw === 'undefined' || !window.document) return;
function initCollapse() {
var headings = document.querySelectorAll('.chem-heading');
var hi = 0;
var len = headings.length;
for (; hi < len; hi++) {
(function(node) {
if (node.getAttribute('data-chem-attached')) return;
node.setAttribute('data-chem-attached', '1');
node.style.cursor = 'pointer';
node.addEventListener('click', function() {
var kind = node.getAttribute('data-kind') || '';
var wrapper = findWrapper(node, kind);
if (!wrapper) return;
var btn = node.querySelector('.collapse-btn');
if (wrapper.classList.contains('collapsed')) {
wrapper.style.maxHeight = wrapper.scrollHeight + 'px';
wrapper.classList.remove('collapsed');
wrapper.classList.add('expanded');
if (btn) btn.textContent = 'свернуть';
var cleanup = function() {
wrapper.style.maxHeight = '';
wrapper.removeEventListener('transitionend', cleanup);
};
wrapper.addEventListener('transitionend', cleanup);
} else {
var currentHeight = wrapper.scrollHeight;
wrapper.style.maxHeight = currentHeight + 'px';
wrapper.offsetHeight;
wrapper.style.maxHeight = '0px';
wrapper.classList.remove('expanded');
wrapper.classList.add('collapsed');
if (btn) btn.textContent = 'развернуть';
}
});
})(headings[hi]);
}
}
function findWrapper(node, kind) {
var parent = node.parentNode;
if (!parent) return null;
var wrappers = parent.querySelectorAll('.collapsible');
var wi = 0;
var wlen = wrappers.length;
for (; wi < wlen; wi++) {
if (wrappers[wi].getAttribute('data-kind') === kind) {
return wrappers[wi];
}
}
return null;
}
if (document.readyState === 'complete' || document.readyState === 'interactive') {
initCollapse();
} else {
document.addEventListener('DOMContentLoaded', initCollapse);
}
})();
/* Каталог страниц Marine Corps ----------------------------------------------------------------- */
(function () {
if (typeof mw === 'undefined' || !window.document) return;
var statusMap = {
green: { emoji: '🟢', label: 'Готово', words: ['готово', 'готов'] },
yellow: { emoji: '🟡', label: 'Нужно обновить', words: ['нужно обновить', 'обновить', 'устарело'] },
red: { emoji: '🔴', label: 'Нет страницы', words: ['нет страницы', 'нет', 'пусто'] },
blue: { emoji: '🔵', label: 'Заморожено', words: ['заморожено', 'заморожен'] }
};
function ready(fn) {
if (document.readyState === 'complete' || document.readyState === 'interactive') {
fn();
} else {
document.addEventListener('DOMContentLoaded', fn);
}
}
ready(function () {
var catalog = document.querySelector('.scmc-catalog');
if (!catalog) return;
var tools = catalog.querySelector('.scmc-catalog-tools');
var rows = Array.prototype.slice.call(catalog.querySelectorAll('.scmc-catalog-row'));
if (!tools || !rows.length) return;
buildTools(tools);
enhanceRows(rows);
var search = tools.querySelector('.scmc-catalog-search');
var filters = Array.prototype.slice.call(tools.querySelectorAll('.scmc-catalog-filter'));
var counter = tools.querySelector('.scmc-catalog-counter');
var activeStatus = 'all';
update();
search.addEventListener('input', update);
filters.forEach(function (button) {
button.addEventListener('click', function () {
activeStatus = button.getAttribute('data-status') || 'all';
filters.forEach(function (other) {
other.classList.toggle('is-active', other === button);
});
update();
});
});
document.addEventListener('click', function (event) {
var statusButton = event.target.closest('.scmc-catalog-status');
if (statusButton) {
event.preventDefault();
event.stopPropagation();
openStatusMenu(statusButton);
return;
}
var menu = document.querySelector('.scmc-status-menu');
if (menu && !event.target.closest('.scmc-status-menu')) {
menu.remove();
}
});
function buildTools(target) {
target.innerHTML = '';
var input = document.createElement('input');
input.className = 'scmc-catalog-search';
input.type = 'search';
input.placeholder = 'Поиск по названию, разделу, статусу или комментарию...';
var filtersWrap = document.createElement('div');
filtersWrap.className = 'scmc-catalog-filters';
[
['all', 'Все'],
['green', '🟢 Готово'],
['yellow', '🟡 Нужно обновить'],
['red', '🔴 Нет страницы'],
['blue', '🔵 Заморожено'],
['problem', 'Проблемные']
].forEach(function (item, index) {
var button = document.createElement('button');
button.type = 'button';
button.className = 'scmc-catalog-filter' + (index === 0 ? ' is-active' : '');
button.setAttribute('data-status', item[0]);
button.textContent = item[1];
filtersWrap.appendChild(button);
});
var count = document.createElement('div');
count.className = 'scmc-catalog-counter';
target.appendChild(input);
target.appendChild(filtersWrap);
target.appendChild(count);
}
function enhanceRows(rowList) {
rowList.forEach(function (row) {
var status = normalizeStatus(row.getAttribute('data-status'));
row.setAttribute('data-status', status);
var note = row.getAttribute('data-note');
if (note && !row.querySelector('.scmc-catalog-note')) {
var noteNode = document.createElement('span');
noteNode.className = 'scmc-catalog-note';
noteNode.textContent = note;
row.appendChild(noteNode);
}
var button = document.createElement('button');
button.type = 'button';
button.className = 'scmc-catalog-status';
button.setAttribute('data-status', status);
button.title = 'Изменить статус';
button.textContent = statusMap[status].emoji;
row.appendChild(button);
});
}
function openStatusMenu(button) {
var oldMenu = document.querySelector('.scmc-status-menu');
if (oldMenu) oldMenu.remove();
var row = button.closest('.scmc-catalog-row');
if (!row) return;
var rect = button.getBoundingClientRect();
var menu = document.createElement('div');
menu.className = 'scmc-status-menu';
[
['green', '🟢 Готово'],
['yellow', '🟡 Нужно обновить'],
['red', '🔴 Нет страницы'],
['blue', '🔵 Заморожено']
].forEach(function (item) {
var status = item[0];
var label = item[1];
var option = document.createElement('button');
option.type = 'button';
option.textContent = label;
option.addEventListener('click', function (event) {
event.preventDefault();
event.stopPropagation();
menu.remove();
changeStatus(row, button, status);
});
menu.appendChild(option);
});
document.body.appendChild(menu);
var left = Math.min(rect.left, window.innerWidth - menu.offsetWidth - 12);
var top = rect.bottom + 8;
menu.style.left = Math.max(12, left) + 'px';
menu.style.top = top + 'px';
}
function changeStatus(row, button, newStatus) {
var oldStatus = row.getAttribute('data-status');
if (!newStatus || newStatus === oldStatus) return;
var page = row.getAttribute('data-page') || '';
var title = row.getAttribute('data-title') || page;
button.classList.add('scmc-status-saving');
saveStatus(page, oldStatus, newStatus, title)
.then(function () {
row.setAttribute('data-status', newStatus);
button.setAttribute('data-status', newStatus);
button.textContent = statusMap[newStatus].emoji;
button.classList.remove('scmc-status-saving');
update();
})
.catch(function (error) {
button.classList.remove('scmc-status-saving');
alert(error.message || 'Не удалось сохранить статус. Проверьте вход в аккаунт и права редактора.');
});
}
function saveStatus(page, oldStatus, newStatus, title) {
var api = new mw.Api();
var currentPage = mw.config.get('wgPageName');
return api.get({
action: 'query',
prop: 'revisions',
titles: currentPage,
rvprop: 'content',
rvslots: 'main',
formatversion: 2
}).then(function (data) {
var pageData = data.query.pages[0];
var content = pageData.revisions[0].slots.main.content;
var result = replaceStatusInSource(content, page, oldStatus, newStatus);
if (!result.changed) {
throw new Error('Не нашёл строку для страницы: ' + title);
}
if (result.ambiguous) {
throw new Error('Нашлось несколько строк для страницы: ' + title + '. Лучше изменить вручную.');
}
return api.postWithToken('csrf', {
action: 'edit',
title: currentPage,
text: result.text,
summary: 'Обновление статуса страницы: ' + title,
minor: true
});
});
}
function replaceStatusInSource(content, page, oldStatus, newStatus) {
var lines = content.split('\n');
var indexes = [];
for (var i = 0; i < lines.length; i++) {
if (lineMatchesPage(lines[i], page) && lineHasStatus(lines[i], oldStatus)) {
indexes.push(i);
}
}
if (!indexes.length) {
return { changed: false, ambiguous: false, text: content };
}
if (indexes.length > 1) {
return { changed: false, ambiguous: true, text: content };
}
var index = indexes[0];
lines[index] = lines[index].replace(/data-status=(["'])(green|yellow|red|blue)\1/, 'data-status=$1' + newStatus + '$1');
return { changed: true, ambiguous: false, text: lines.join('\n') };
}
function lineMatchesPage(line, page) {
return line.indexOf('data-page="' + page + '"') !== -1 || line.indexOf("data-page='" + page + "'") !== -1;
}
function lineHasStatus(line, status) {
return line.indexOf('data-status="' + status + '"') !== -1 || line.indexOf("data-status='" + status + "'") !== -1;
}
function update() {
var query = clean(search.value).toLowerCase();
var words = query ? query.split(/\s+/).filter(Boolean) : [];
var visible = 0;
rows.forEach(function (row) {
var status = row.getAttribute('data-status');
var text = buildSearchText(row).toLowerCase();
var statusOk =
activeStatus === 'all' ||
activeStatus === status ||
(activeStatus === 'problem' && status !== 'green');
var textOk = words.every(function (word) {
return text.indexOf(word) !== -1 || statusWordMatches(status, word);
});
var show = statusOk && textOk;
row.classList.toggle('scmc-catalog-hidden', !show);
if (show) visible++;
});
counter.textContent = 'Показано: ' + visible + ' из ' + rows.length;
}
function buildSearchText(row) {
var status = row.getAttribute('data-status');
var note = row.getAttribute('data-note') || '';
var words = statusMap[status] ? statusMap[status].words.join(' ') : '';
return [
row.getAttribute('data-title') || '',
row.getAttribute('data-page') || '',
row.getAttribute('data-section') || '',
note,
status,
words,
row.textContent || ''
].join(' ');
}
function statusWordMatches(status, word) {
if (!statusMap[status]) return false;
var words = statusMap[status].words;
for (var i = 0; i < words.length; i++) {
if (words[i].indexOf(word) !== -1 || word.indexOf(words[i]) !== -1) return true;
}
return false;
}
function normalizeStatus(status) {
return statusMap[status] ? status : 'blue';
}
function clean(text) {
return String(text || '').replace(/\s+/g, ' ').trim();
}
});
})();
/* Каталог страниц Marine Corps --------------------------------------------------------------END */