MediaWiki:Common.js: Difference between revisions

From AnarchyMU Wiki
No edit summary
No edit summary
 
(2 intermediate revisions by the same user not shown)
Line 20: Line 20:
})();
})();


/* Update 045: robust nested Stage dropdown - v5 with extensive debug logging */
/* Update 048: nested Stage dropdown v8 - clean row layout + per-stage overview child */
(function () {
(function () {
   function textOf(el) {
   function textOf(el) {
Line 30: Line 30:
   }
   }


   function urlOf(link) {
   function pageKeyOf(link) {
     if (!link) return '';
     if (!link) return '';
     var href = link.getAttribute('href') || '';
     var href = link.getAttribute('href') || '';
     var title = link.getAttribute('title') || '';
     var title = link.getAttribute('title') || '';
    var parts = href.split('/');
    var page = parts[parts.length - 1] || '';
    return page || title;
  }


  function isStageLabel(label) {
    var fromTitle = title.replace(/\s+/g, '_').trim();
    return /^stage\s+[1-4]$/i.test(label);
    if (fromTitle) return fromTitle;
  }


  function getStageNumber(label) {
     var fromQuery = href.match(/[?&](title|page)=([^&]+)/i);
     var m = label.match(/^stage\s+([1-4])$/i);
     if (fromQuery && fromQuery[2]) return decodeURIComponent(fromQuery[2]);
     return m ? m[1] : null;
  }


  function isMiniLabel(label) {
     var parts = href.split('/');
     return /^mini\s+stage\s+\d+$/i.test(label);
     return decodeURIComponent(parts[parts.length - 1] || '');
  }
 
  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);
    }
   }
   }


Line 70: Line 50:
     for (i = 0; i < li.childNodes.length; i++) {
     for (i = 0; i < li.childNodes.length; i++) {
       var n = li.childNodes[i];
       var n = li.childNodes[i];
       if (n && n.nodeType === 1 && n.tagName && n.tagName.toLowerCase() === 'a') {
       if (n && n.nodeType === 1 && n.tagName && n.tagName.toLowerCase() === 'a') return n;
        return n;
      }
     }
     }
     return null;
     return null;
Line 83: Line 61:
     for (i = 0; i < container.children.length; i++) {
     for (i = 0; i < container.children.length; i++) {
       var c = container.children[i];
       var c = container.children[i];
       if (c && c.tagName && c.tagName.toLowerCase() === 'li') {
       if (c && c.tagName && c.tagName.toLowerCase() === 'li') out.push(c);
        out.push(c);
      }
     }
     }
     return out;
     return out;
  }
  function isStageLabel(labelNorm) {
    return /^stage\s+[1-4]$/i.test(labelNorm);
  }
  function stageNumberFromLabel(labelNorm) {
    var m = labelNorm.match(/^stage\s+([1-4])$/i);
    return m ? m[1] : null;
  }
  function isMiniLabel(labelNorm) {
    return /^mini\s+stage\s+\d+$/i.test(labelNorm);
  }
  function isStageOverviewKey(key, stageNum) {
    return key === 'Stage_' + stageNum + '_Overview';
  }
  function isMiniForStage(key, stageNum) {
    if (stageNum === '1') return /^Mini_Stage_\d+$/.test(key);
    return new RegExp('^Stage_' + stageNum + '_Mini_Stage_\\d+$').test(key);
   }
   }


Line 96: Line 94:
     var stageCount = 0;
     var stageCount = 0;
     var miniCount = 0;
     var miniCount = 0;
    var overviewCount = 0;
     var i;
     var i;


Line 103: Line 100:
       if (/^stage\s+[1-4]$/i.test(label)) stageCount++;
       if (/^stage\s+[1-4]$/i.test(label)) stageCount++;
       if (/^mini\s+stage\s+\d+$/i.test(label)) miniCount++;
       if (/^mini\s+stage\s+\d+$/i.test(label)) miniCount++;
      if (label === 'overview') overviewCount++;
     }
     }


     if (stageCount >= 4 && miniCount >= 8) {
     if (stageCount >= 4 && miniCount >= 8) return stageCount * 100 + miniCount;
      return (stageCount * 100) + miniCount + (overviewCount ? 10 : 0);
    }
     return 0;
     return 0;
   }
   }


   function findStagesListContainer() {
   function findStagesList() {
     var candidates = document.querySelectorAll('ul, ol');
     var lists = document.querySelectorAll('ul, ol');
     var best = null;
     var best = null;
     var bestScore = 0;
     var bestScore = 0;
     var i;
     var i;


     for (i = 0; i < candidates.length; i++) {
     for (i = 0; i < lists.length; i++) {
       var score = scoreContainer(candidates[i]);
       var s = scoreContainer(lists[i]);
       if (score > bestScore) {
       if (s > bestScore) {
         bestScore = score;
         bestScore = s;
         best = candidates[i];
         best = lists[i];
       }
       }
     }
     }
     return best;
     return best;
   }
   }


   function makeToggle(stageLi, toggleBtn) {
   function buildToggleHandler(stageLi, toggleEl) {
     return function (ev) {
     return function (ev) {
       ev.preventDefault();
       ev.preventDefault();
       ev.stopPropagation();
       ev.stopPropagation();
       var open = stageLi.classList.toggle('open');
      ev.stopImmediatePropagation();
      toggleBtn.setAttribute('aria-expanded', open ? 'true' : 'false');
 
       toggleBtn.textContent = open ? '▾' : '▸';
       var open = stageLi.classList.toggle('stages-open');
      console.log('[UPDATE-045] Toggle ' + stageLi.className + ', now ' + (open ? 'open' : 'closed'));
       toggleEl.textContent = open ? '▾' : '▸';
       return false;
       return false;
     };
     };
  }
  function normalizeStageRow(stageLi, stageNum) {
    stageLi.className = (stageLi.className ? stageLi.className + ' ' : '') + 'stages-inner-parent stages-inner-stage-' + stageNum;
    stageLi.setAttribute('data-stage-num', stageNum);
    var link = directLink(stageLi);
    if (!link) return null;
    link.href = '#';
    link.className = (link.className ? link.className + ' ' : '') + 'stages-inner-stage-link';
    var toggle = document.createElement('button');
    toggle.type = 'button';
    toggle.className = 'stages-inner-toggle-btn';
    toggle.setAttribute('aria-label', 'Toggle Stage ' + stageNum);
    toggle.textContent = '▸';
    var handler = buildToggleHandler(stageLi, toggle);
    toggle.addEventListener('click', handler, true);
    link.addEventListener('click', handler, true);
    stageLi.appendChild(toggle);
    return stageLi;
   }
   }


   function applyNestedStages() {
   function applyNestedStages() {
     var list = findStagesListContainer();
     var list = findStagesList();
     if (!list || list.dataset.stagesNestedApplied === '1') {
     if (!list || list.dataset.stagesNestedV8Applied === '1') return;
      console.log('[UPDATE-045] Already applied or no list found.');
      return;
    }


     var items = childLis(list);
     var items = childLis(list);
    console.log('[UPDATE-045] Found ' + items.length + ' list items to process.');
     if (!items.length) return;
     if (!items.length) return;


     var rebuilt = [];
     var rebuilt = [];
     var currentStageLi = null;
     var currentStageLi = null;
    var currentMiniUl = null;
     var currentStageNum = null;
     var currentStageNum = null;
     var currentStageLabel = null;
     var currentSub = null;
     var i;
     var i;


Line 164: Line 175:
       var link = directLink(li);
       var link = directLink(li);
       if (!link) {
       if (!link) {
        console.log('[UPDATE-045] Item ' + i + ' has no link, skipping.');
         rebuilt.push(li.cloneNode(true));
         rebuilt.push(li.cloneNode(true));
         continue;
         continue;
       }
       }


       var label = textOf(link);
       var labelNorm = norm(textOf(link));
      var labelNorm = norm(label);
       var key = pageKeyOf(link);
       var url = urlOf(link);
 
      console.log('[UPDATE-045] Item ' + i + ': label="' + label + '", url="' + url + '", normalized="' + labelNorm + '"');


       if (isStageLabel(labelNorm)) {
       if (isStageLabel(labelNorm)) {
         var stageNum = getStageNumber(labelNorm);
         currentStageNum = stageNumberFromLabel(labelNorm);
         currentStageNum = stageNum;
         currentStageLi = normalizeStageRow(li.cloneNode(true), currentStageNum);
        currentStageLabel = label;
         currentSub = document.createElement('ul');
 
         currentSub.className = 'stages-inner-submenu';
        console.log('[UPDATE-045] -> is Stage ' + stageNum);
         currentStageLi.appendChild(currentSub);
 
        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);
         rebuilt.push(currentStageLi);
         continue;
         continue;
       }
       }


      // Check if this is a mini-stage that belongs to the current stage
       if (currentSub && currentStageNum) {
       if (isMiniLabel(labelNorm)) {
         if (isStageOverviewKey(key, currentStageNum) || isMiniForStage(key, currentStageNum)) {
         console.log('[UPDATE-045] -> is Mini Stage');
           var childLi = li.cloneNode(true);
 
           childLi.className = (childLi.className ? childLi.className + ' ' : '') + 'stages-inner-child-item';
        if (currentMiniUl && currentStageNum) {
          var childLink = directLink(childLi);
           var matches = isMiniLabelForStage(url, label, currentStageNum);
           if (childLink && isStageOverviewKey(key, currentStageNum)) {
           console.log('[UPDATE-045]    -> matches Stage ' + currentStageNum + '? ' + (matches ? 'YES' : 'NO, expected ' + (currentStageNum === '1' ? 'Mini_Stage_*' : 'Stage_' + currentStageNum + '_Mini_Stage_*')));
             childLink.className = (childLink.className ? childLink.className + ' ' : '') + 'stages-overview-child-link';
 
           if (matches) {
             currentMiniUl.appendChild(li.cloneNode(true));
            continue;
           }
           }
        } else {
           currentSub.appendChild(childLi);
           console.log('[UPDATE-045]    -> no current stage to attach to, will be orphan');
          continue;
         }
         }
       }
       }


      // Not a stage, not a matching mini; reset and add as-is
      console.log('[UPDATE-045] -> orphan item, resetting stage context');
       currentStageLi = null;
       currentStageLi = null;
      currentMiniUl = null;
       currentStageNum = null;
       currentStageNum = null;
       currentStageLabel = null;
       currentSub = null;
       rebuilt.push(li.cloneNode(true));
       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]);
     while (list.firstChild) {
      list.removeChild(list.firstChild);
    }
 
     for (i = 0; i < rebuilt.length; i++) {
      list.appendChild(rebuilt[i]);
    }


     list.dataset.stagesNestedApplied = '1';
     list.dataset.stagesNestedV8Applied = '1';
    console.log('[UPDATE-045] Successfully applied nested stages.');
   }
   }


   function boot() {
   function boot() {
    console.log('[UPDATE-045] Boot sequence starting...');
     applyNestedStages();
     applyNestedStages();
     window.setTimeout(function () {
     setTimeout(applyNestedStages, 350);
      console.log('[UPDATE-045] Retry 1 at 350ms');
     setTimeout(applyNestedStages, 900);
      applyNestedStages();
     setTimeout(applyNestedStages, 1600);
    }, 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);
   }
   }



Latest revision as of 11:12, 20 March 2026

/* 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 048: nested Stage dropdown v8 - clean row layout + per-stage overview child */
(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 pageKeyOf(link) {
    if (!link) return '';
    var href = link.getAttribute('href') || '';
    var title = link.getAttribute('title') || '';

    var fromTitle = title.replace(/\s+/g, '_').trim();
    if (fromTitle) return fromTitle;

    var fromQuery = href.match(/[?&](title|page)=([^&]+)/i);
    if (fromQuery && fromQuery[2]) return decodeURIComponent(fromQuery[2]);

    var parts = href.split('/');
    return decodeURIComponent(parts[parts.length - 1] || '');
  }

  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 isStageLabel(labelNorm) {
    return /^stage\s+[1-4]$/i.test(labelNorm);
  }

  function stageNumberFromLabel(labelNorm) {
    var m = labelNorm.match(/^stage\s+([1-4])$/i);
    return m ? m[1] : null;
  }

  function isMiniLabel(labelNorm) {
    return /^mini\s+stage\s+\d+$/i.test(labelNorm);
  }

  function isStageOverviewKey(key, stageNum) {
    return key === 'Stage_' + stageNum + '_Overview';
  }

  function isMiniForStage(key, stageNum) {
    if (stageNum === '1') return /^Mini_Stage_\d+$/.test(key);
    return new RegExp('^Stage_' + stageNum + '_Mini_Stage_\\d+$').test(key);
  }

  function scoreContainer(container) {
    var lis = childLis(container);
    if (!lis.length) return 0;

    var stageCount = 0;
    var miniCount = 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 (stageCount >= 4 && miniCount >= 8) return stageCount * 100 + miniCount;
    return 0;
  }

  function findStagesList() {
    var lists = document.querySelectorAll('ul, ol');
    var best = null;
    var bestScore = 0;
    var i;

    for (i = 0; i < lists.length; i++) {
      var s = scoreContainer(lists[i]);
      if (s > bestScore) {
        bestScore = s;
        best = lists[i];
      }
    }
    return best;
  }

  function buildToggleHandler(stageLi, toggleEl) {
    return function (ev) {
      ev.preventDefault();
      ev.stopPropagation();
      ev.stopImmediatePropagation();

      var open = stageLi.classList.toggle('stages-open');
      toggleEl.textContent = open ? '▾' : '▸';
      return false;
    };
  }

  function normalizeStageRow(stageLi, stageNum) {
    stageLi.className = (stageLi.className ? stageLi.className + ' ' : '') + 'stages-inner-parent stages-inner-stage-' + stageNum;
    stageLi.setAttribute('data-stage-num', stageNum);

    var link = directLink(stageLi);
    if (!link) return null;

    link.href = '#';
    link.className = (link.className ? link.className + ' ' : '') + 'stages-inner-stage-link';

    var toggle = document.createElement('button');
    toggle.type = 'button';
    toggle.className = 'stages-inner-toggle-btn';
    toggle.setAttribute('aria-label', 'Toggle Stage ' + stageNum);
    toggle.textContent = '▸';

    var handler = buildToggleHandler(stageLi, toggle);
    toggle.addEventListener('click', handler, true);
    link.addEventListener('click', handler, true);

    stageLi.appendChild(toggle);
    return stageLi;
  }

  function applyNestedStages() {
    var list = findStagesList();
    if (!list || list.dataset.stagesNestedV8Applied === '1') return;

    var items = childLis(list);
    if (!items.length) return;

    var rebuilt = [];
    var currentStageLi = null;
    var currentStageNum = null;
    var currentSub = null;
    var i;

    for (i = 0; i < items.length; i++) {
      var li = items[i];
      var link = directLink(li);
      if (!link) {
        rebuilt.push(li.cloneNode(true));
        continue;
      }

      var labelNorm = norm(textOf(link));
      var key = pageKeyOf(link);

      if (isStageLabel(labelNorm)) {
        currentStageNum = stageNumberFromLabel(labelNorm);
        currentStageLi = normalizeStageRow(li.cloneNode(true), currentStageNum);
        currentSub = document.createElement('ul');
        currentSub.className = 'stages-inner-submenu';
        currentStageLi.appendChild(currentSub);
        rebuilt.push(currentStageLi);
        continue;
      }

      if (currentSub && currentStageNum) {
        if (isStageOverviewKey(key, currentStageNum) || isMiniForStage(key, currentStageNum)) {
          var childLi = li.cloneNode(true);
          childLi.className = (childLi.className ? childLi.className + ' ' : '') + 'stages-inner-child-item';
          var childLink = directLink(childLi);
          if (childLink && isStageOverviewKey(key, currentStageNum)) {
            childLink.className = (childLink.className ? childLink.className + ' ' : '') + 'stages-overview-child-link';
          }
          currentSub.appendChild(childLi);
          continue;
        }
      }

      currentStageLi = null;
      currentStageNum = null;
      currentSub = null;
      rebuilt.push(li.cloneNode(true));
    }

    while (list.firstChild) list.removeChild(list.firstChild);
    for (i = 0; i < rebuilt.length; i++) list.appendChild(rebuilt[i]);

    list.dataset.stagesNestedV8Applied = '1';
  }

  function boot() {
    applyNestedStages();
    setTimeout(applyNestedStages, 350);
    setTimeout(applyNestedStages, 900);
    setTimeout(applyNestedStages, 1600);
  }

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', boot);
  } else {
    boot();
  }
})();