MediaWiki:Common.js
From AnarchyMU Wiki
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/* Any JavaScript here will be loaded for all users on every page load. */
// Ensures class card images use the intended foreground image if data-src is present.
(function () {
function applyDataSrc() {
var images = document.querySelectorAll('.mw-card-image img[data-src]');
for (var i = 0; i < images.length; i++) {
var img = images[i];
var dataSrc = img.getAttribute('data-src');
if (dataSrc && img.getAttribute('src') !== dataSrc) {
img.setAttribute('src', dataSrc);
}
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', applyDataSrc);
} else {
applyDataSrc();
}
})();
/* Update 045: robust nested Stage dropdown - v5 with extensive debug logging */
(function () {
function textOf(el) {
return (el && el.textContent ? el.textContent : '').replace(/\s+/g, ' ').trim();
}
function norm(s) {
return (s || '').replace(/\s+/g, ' ').trim().toLowerCase();
}
function urlOf(link) {
if (!link) return '';
var href = link.getAttribute('href') || '';
var title = link.getAttribute('title') || '';
var parts = href.split('/');
var page = parts[parts.length - 1] || '';
return page || title;
}
function isStageLabel(label) {
return /^stage\s+[1-4]$/i.test(label);
}
function getStageNumber(label) {
var m = label.match(/^stage\s+([1-4])$/i);
return m ? m[1] : null;
}
function isMiniLabel(label) {
return /^mini\s+stage\s+\d+$/i.test(label);
}
function isMiniLabelForStage(url, label, stageNum) {
if (!isMiniLabel(label)) return false;
if (stageNum === '1') {
// Stage 1 minis should NOT have Stage_ prefix in URL
return !/^Stage_\d+_Mini_Stage/.test(url);
} else {
// Stage N (2-4) minis should have Stage_N_ prefix in URL
var pattern = new RegExp('^Stage_' + stageNum + '_Mini_Stage');
return pattern.test(url);
}
}
function directLink(li) {
if (!li || !li.childNodes) return null;
var i;
for (i = 0; i < li.childNodes.length; i++) {
var n = li.childNodes[i];
if (n && n.nodeType === 1 && n.tagName && n.tagName.toLowerCase() === 'a') {
return n;
}
}
return null;
}
function childLis(container) {
if (!container || !container.children) return [];
var out = [];
var i;
for (i = 0; i < container.children.length; i++) {
var c = container.children[i];
if (c && c.tagName && c.tagName.toLowerCase() === 'li') {
out.push(c);
}
}
return out;
}
function scoreContainer(container) {
var lis = childLis(container);
if (!lis.length) return 0;
var stageCount = 0;
var miniCount = 0;
var overviewCount = 0;
var i;
for (i = 0; i < lis.length; i++) {
var label = norm(textOf(directLink(lis[i])));
if (/^stage\s+[1-4]$/i.test(label)) stageCount++;
if (/^mini\s+stage\s+\d+$/i.test(label)) miniCount++;
if (label === 'overview') overviewCount++;
}
if (stageCount >= 4 && miniCount >= 8) {
return (stageCount * 100) + miniCount + (overviewCount ? 10 : 0);
}
return 0;
}
function findStagesListContainer() {
var candidates = document.querySelectorAll('ul, ol');
var best = null;
var bestScore = 0;
var i;
for (i = 0; i < candidates.length; i++) {
var score = scoreContainer(candidates[i]);
if (score > bestScore) {
bestScore = score;
best = candidates[i];
}
}
return best;
}
function makeToggle(stageLi, toggleBtn) {
return function (ev) {
ev.preventDefault();
ev.stopPropagation();
var open = stageLi.classList.toggle('open');
toggleBtn.setAttribute('aria-expanded', open ? 'true' : 'false');
toggleBtn.textContent = open ? '▾' : '▸';
console.log('[UPDATE-045] Toggle ' + stageLi.className + ', now ' + (open ? 'open' : 'closed'));
return false;
};
}
function applyNestedStages() {
var list = findStagesListContainer();
if (!list || list.dataset.stagesNestedApplied === '1') {
console.log('[UPDATE-045] Already applied or no list found.');
return;
}
var items = childLis(list);
console.log('[UPDATE-045] Found ' + items.length + ' list items to process.');
if (!items.length) return;
var rebuilt = [];
var currentStageLi = null;
var currentMiniUl = null;
var currentStageNum = null;
var currentStageLabel = null;
var i;
for (i = 0; i < items.length; i++) {
var li = items[i];
var link = directLink(li);
if (!link) {
console.log('[UPDATE-045] Item ' + i + ' has no link, skipping.');
rebuilt.push(li.cloneNode(true));
continue;
}
var label = textOf(link);
var labelNorm = norm(label);
var url = urlOf(link);
console.log('[UPDATE-045] Item ' + i + ': label="' + label + '", url="' + url + '", normalized="' + labelNorm + '"');
if (isStageLabel(labelNorm)) {
var stageNum = getStageNumber(labelNorm);
currentStageNum = stageNum;
currentStageLabel = label;
console.log('[UPDATE-045] -> is Stage ' + stageNum);
currentStageLi = li.cloneNode(true);
currentStageLi.className = (currentStageLi.className ? currentStageLi.className + ' ' : '') + 'stages-inner-parent';
// Make the stage link NOT navigate and act as toggle
var stageLink = directLink(currentStageLi);
if (stageLink) {
stageLink.style.fontWeight = 'bold';
stageLink.href = '#';
}
currentMiniUl = document.createElement('ul');
currentMiniUl.className = 'stages-inner-submenu';
var toggle = document.createElement('button');
toggle.type = 'button';
toggle.className = 'stages-inner-toggle';
toggle.setAttribute('aria-expanded', 'false');
toggle.setAttribute('data-stage', stageNum);
toggle.textContent = '▸';
var toggleHandler = makeToggle(currentStageLi, toggle);
toggle.addEventListener('click', toggleHandler);
// Also make the stage link itself toggleable
if (stageLink) {
stageLink.addEventListener('click', function (ev) {
ev.preventDefault();
ev.stopPropagation();
toggle.click();
return false;
});
}
currentStageLi.appendChild(toggle);
currentStageLi.appendChild(currentMiniUl);
rebuilt.push(currentStageLi);
continue;
}
// Check if this is a mini-stage that belongs to the current stage
if (isMiniLabel(labelNorm)) {
console.log('[UPDATE-045] -> is Mini Stage');
if (currentMiniUl && currentStageNum) {
var matches = isMiniLabelForStage(url, label, currentStageNum);
console.log('[UPDATE-045] -> matches Stage ' + currentStageNum + '? ' + (matches ? 'YES' : 'NO, expected ' + (currentStageNum === '1' ? 'Mini_Stage_*' : 'Stage_' + currentStageNum + '_Mini_Stage_*')));
if (matches) {
currentMiniUl.appendChild(li.cloneNode(true));
continue;
}
} else {
console.log('[UPDATE-045] -> no current stage to attach to, will be orphan');
}
}
// Not a stage, not a matching mini; reset and add as-is
console.log('[UPDATE-045] -> orphan item, resetting stage context');
currentStageLi = null;
currentMiniUl = null;
currentStageNum = null;
currentStageLabel = null;
rebuilt.push(li.cloneNode(true));
}
console.log('[UPDATE-045] Rebuilt ' + rebuilt.length + ' items.');
while (list.firstChild) {
list.removeChild(list.firstChild);
}
for (i = 0; i < rebuilt.length; i++) {
list.appendChild(rebuilt[i]);
}
list.dataset.stagesNestedApplied = '1';
console.log('[UPDATE-045] Successfully applied nested stages.');
}
function boot() {
console.log('[UPDATE-045] Boot sequence starting...');
applyNestedStages();
window.setTimeout(function () {
console.log('[UPDATE-045] Retry 1 at 350ms');
applyNestedStages();
}, 350);
window.setTimeout(function () {
console.log('[UPDATE-045] Retry 2 at 900ms');
applyNestedStages();
}, 900);
window.setTimeout(function () {
console.log('[UPDATE-045] Retry 3 at 1800ms');
applyNestedStages();
}, 1800);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', boot);
} else {
boot();
}
})();