Zum Inhalt springen


MediaWiki:Common.js: Unterschied zwischen den Versionen

Aus Firestone Idle RPG Wiki
Keine Bearbeitungszusammenfassung
Keine Bearbeitungszusammenfassung
Zeile 1: Zeile 1:
/* Das folgende JavaScript wird für alle Benutzer geladen. */
/* Das folgende JavaScript wird für alle Benutzer geladen. */
/* ==== Sidebar: klickbare Kopfzeile + Gruppen mit Auf/Zu (Vector-2022, auch im Drawer) ==== */
/* ==== Sidebar: klickbare Kopfzeile + Gruppen mit Auf/Zu + Persistenz (Vector-2022, auch im Drawer) ==== */
(function () {
(function () {
   if (mw.config.get('skin') !== 'vector-2022') return;
   if (mw.config.get('skin') !== 'vector-2022') return;


   // ---------- helpers ----------
   // ---------- Persistenz ----------
  var STORAGE_KEY = 'kr-sb-state:v3:' + (mw.config.get('wgUserName') || 'anon');
  var STATE = loadState();
 
  function loadState() {
    try { return JSON.parse(localStorage.getItem(STORAGE_KEY) || '{}'); } catch (e) { return {}; }
  }
  function saveState() {
    try { localStorage.setItem(STORAGE_KEY, JSON.stringify(STATE)); } catch (e) {}
  }
 
  // ---------- Helpers ----------
   function extractDirective(a) {
   function extractDirective(a) {
     if (!a) return null;
     if (!a) return null;
Zeile 10: Zeile 21:
     try { href = new URL(href, location.href).hash || ''; } catch (e) {}
     try { href = new URL(href, location.href).hash || ''; } catch (e) {}
     var s = (href || '').trim().toLowerCase();
     var s = (href || '').trim().toLowerCase();
     if (s.indexOf('#group:') >= 0) return { kind: 'group', value: decodeURIComponent(s.split('#group:').pop()) };
     if (s.includes('#group:')) return { kind: 'group', value: decodeURIComponent(s.split('#group:').pop()) };
     if (s.indexOf('#link:')  >= 0) return { kind: 'link',  value: decodeURIComponent(s.split('#link:').pop()) };
     if (s.includes('#link:'))  return { kind: 'link',  value: decodeURIComponent(s.split('#link:').pop()) };
     return null;
     return null;
   }
   }
   function stripDirectiveHash(a){
   function stripDirectiveHash(a){
     if (!a) return;
     if (!a) return;
     var href = a.getAttribute('href') || '';
     a.setAttribute('href', (a.getAttribute('href') || '').replace(/#(?:link|group):[^#]*$/i,''));
    a.setAttribute('href', href.replace(/#(?:link|group):[^#]*$/i,''));
   }
   }
   function hasRealHref(a){
   function hasRealHref(a){
     if (!a) return false;
     if (!a) return false;
     var h = a.getAttribute('href') || '';
     var h = a.getAttribute('href') || '';
     // reiner Hash => kein echter Link
     return !/^#/.test(h);
     if (/^#/.test(h)) return false;
  }
     return true;
  function normId(s){
     return String(s || '').trim().toLowerCase().replace(/\s+/g,'-').replace(/[^\w\-:.|]/g,'');
  }
  function toggleSet(li, arrow, expanded){
    if (expanded) {
      li.classList.remove('is-collapsed');
      arrow.setAttribute('aria-expanded','true');
    } else {
      li.classList.add('is-collapsed');
      arrow.setAttribute('aria-expanded','false');
     }
   }
   }


   function makeHead(li, keepLink) {
  // Head bauen und ID vergeben (für Persistenz)
   function makeHead(li, keepLink, containerUL, labelOverride) {
     var a = li.querySelector(':scope > a');
     var a = li.querySelector(':scope > a');
     var head = document.createElement('div');
     var head = document.createElement('div');
     head.className = 'kr-head';
     head.className = 'kr-head';
     var btn = document.createElement('button');
     var btn = document.createElement('button');
     btn.type = 'button';
     btn.type = 'button';
     btn.className = 'kr-arrow';
     btn.className = 'kr-arrow';
     btn.setAttribute('aria-expanded', 'false');         // default: zu
     btn.setAttribute('aria-expanded', 'false');
     head.appendChild(btn);
     head.appendChild(btn);


Zeile 41: Zeile 63:
       var span = document.createElement('span');
       var span = document.createElement('span');
       span.className = 'kr-title';
       span.className = 'kr-title';
       span.textContent = (a && a.textContent) || '';
       span.textContent = labelOverride || (a && a.textContent) || '';
       head.appendChild(span);
       head.appendChild(span);
       if (a) a.remove();
       if (a) a.remove();
Zeile 47: Zeile 69:


     li.insertBefore(head, li.firstChild);
     li.insertBefore(head, li.firstChild);
     li.classList.add('is-collapsed');                   // default: zu
 
    // stabile ID für Persistenz: portletId + label
    var portlet = containerUL.closest('.vector-menu');
    var pid = (portlet && (portlet.id || portlet.getAttribute('id'))) || 'portlet';
    var labelText = (labelOverride || (head.querySelector('a, .kr-title') || {}).textContent || '').trim();
     li.dataset.krId = normId(pid + '::' + labelText);
 
    // Default: zu, dann Zustand aus STATE anwenden
    toggleSet(li, btn, /*expanded*/ false);
    var st = STATE[li.dataset.krId];
    if (st === 1) toggleSet(li, btn, true);
 
     return head;
     return head;
   }
   }
Zeile 54: Zeile 87:
     if (containerUL.dataset.krToggleAttached) return;
     if (containerUL.dataset.krToggleAttached) return;
     containerUL.dataset.krToggleAttached = '1';
     containerUL.dataset.krToggleAttached = '1';
     containerUL.addEventListener('click', function (e) {
     containerUL.addEventListener('click', function (e) {
       if (!e.target.classList.contains('kr-arrow')) return;
       // robust: von Ziel nach oben zum Button laufen
       var li = e.target.closest('li.kr-group, li.kr-top');
      var arrow = e.target.closest('.kr-arrow');
      if (!arrow || !containerUL.contains(arrow)) return;
      e.preventDefault();
 
       var li = arrow.closest('li.kr-group, li.kr-top');
       if (!li) return;
       if (!li) return;
       var collapsed = li.classList.toggle('is-collapsed');
 
       e.target.setAttribute('aria-expanded', collapsed ? 'false' : 'true');
       var expanded = li.classList.contains('is-collapsed');
       toggleSet(li, arrow, expanded); // invertieren
 
      var id = li.dataset.krId;
      if (id) {
        STATE[id] = expanded ? 1 : 0;
        saveState();
      }
     });
     });
   }
   }
Zeile 72: Zeile 117:


       if (d && d.kind === 'group') {
       if (d && d.kind === 'group') {
        // Gruppe: mit oder ohne Link
         var clickable = hasRealHref(a);   // Gruppe mit/ohne Link
         var clickable = hasRealHref(a);
         li.classList.add('kr-group');
         li.classList.add('kr-group');
         makeHead(li, clickable);
         makeHead(li, clickable, ul, (a && a.textContent) || d.value || '');
         if (clickable) stripDirectiveHash(a);
         if (!clickable && a) a.remove(); // nur Texttitel
         else if (a) a.remove();
         else if (clickable) stripDirectiveHash(a);


         var sub = document.createElement('ul');
         var sub = document.createElement('ul');
Zeile 86: Zeile 130:
       }
       }


      // Normale Elemente wandern in die aktuelle Gruppe (falls vorhanden)
       if (current && !(d && (d.kind === 'group' || d.kind === 'link'))) {
       if (current && !(d && (d.kind === 'group' || d.kind === 'link'))) {
         current.appendChild(li);
         current.appendChild(li);
Zeile 111: Zeile 154:
         var top = items[topIdx];
         var top = items[topIdx];
         top.classList.add('kr-top');
         top.classList.add('kr-top');
         makeHead(top, true);                 // klickbarer Titel
         makeHead(top, true, ul);         // klickbarer Titel
         stripDirectiveHash(top.querySelector(':scope > a'));
         stripDirectiveHash(top.querySelector(':scope > a'));


Zeile 119: Zeile 162:


         for (var i = topIdx + 1; i < items.length; i++) sub.appendChild(items[i]);
         for (var i = topIdx + 1; i < items.length; i++) sub.appendChild(items[i]);
 
         buildGroups(sub);                 // Gruppen in der Unterliste
         buildGroups(sub);                   // Gruppen in der Unterliste
         attachToggle(ul);                 // Toggle auch für kr-top
         attachToggle(ul);                   // Toggle auch für kr-top (Pfeil am Kopf)
       } else {
       } else {
         buildGroups(ul);
         buildGroups(ul);
       }
       }


       // Rest säubern
       // aufräumen
       ul.querySelectorAll('a[href*="#link:"], a[href*="#group:"]').forEach(stripDirectiveHash);
       ul.querySelectorAll('a[href*="#link:"], a[href*="#group:"]').forEach(stripDirectiveHash);
       var portlet = ul.closest('.vector-menu');
       var portlet = ul.closest('.vector-menu');
Zeile 148: Zeile 190:
   init();
   init();
   mw.hook('wikipage.content').add(init);
   mw.hook('wikipage.content').add(init);
  // auch wenn Drawer dynamisch montiert wird
   var mo = new MutationObserver(function (muts) {
   var mo = new MutationObserver(function (muts) {
     muts.forEach(function (m) {
     muts.forEach(function (m) {

Version vom 10. Oktober 2025, 15:45 Uhr

/* Das folgende JavaScript wird für alle Benutzer geladen. */
/* ==== Sidebar: klickbare Kopfzeile + Gruppen mit Auf/Zu + Persistenz (Vector-2022, auch im Drawer) ==== */
(function () {
  if (mw.config.get('skin') !== 'vector-2022') return;

  // ---------- Persistenz ----------
  var STORAGE_KEY = 'kr-sb-state:v3:' + (mw.config.get('wgUserName') || 'anon');
  var STATE = loadState();

  function loadState() {
    try { return JSON.parse(localStorage.getItem(STORAGE_KEY) || '{}'); } catch (e) { return {}; }
  }
  function saveState() {
    try { localStorage.setItem(STORAGE_KEY, JSON.stringify(STATE)); } catch (e) {}
  }

  // ---------- Helpers ----------
  function extractDirective(a) {
    if (!a) return null;
    var href = a.getAttribute('href') || '';
    try { href = new URL(href, location.href).hash || ''; } catch (e) {}
    var s = (href || '').trim().toLowerCase();
    if (s.includes('#group:')) return { kind: 'group', value: decodeURIComponent(s.split('#group:').pop()) };
    if (s.includes('#link:'))  return { kind: 'link',  value: decodeURIComponent(s.split('#link:').pop()) };
    return null;
  }
  function stripDirectiveHash(a){
    if (!a) return;
    a.setAttribute('href', (a.getAttribute('href') || '').replace(/#(?:link|group):[^#]*$/i,''));
  }
  function hasRealHref(a){
    if (!a) return false;
    var h = a.getAttribute('href') || '';
    return !/^#/.test(h);
  }
  function normId(s){
    return String(s || '').trim().toLowerCase().replace(/\s+/g,'-').replace(/[^\w\-:.|]/g,'');
  }
  function toggleSet(li, arrow, expanded){
    if (expanded) {
      li.classList.remove('is-collapsed');
      arrow.setAttribute('aria-expanded','true');
    } else {
      li.classList.add('is-collapsed');
      arrow.setAttribute('aria-expanded','false');
    }
  }

  // Head bauen und ID vergeben (für Persistenz)
  function makeHead(li, keepLink, containerUL, labelOverride) {
    var a = li.querySelector(':scope > a');
    var head = document.createElement('div');
    head.className = 'kr-head';

    var btn = document.createElement('button');
    btn.type = 'button';
    btn.className = 'kr-arrow';
    btn.setAttribute('aria-expanded', 'false');
    head.appendChild(btn);

    if (keepLink && a) { stripDirectiveHash(a); head.appendChild(a); }
    else {
      var span = document.createElement('span');
      span.className = 'kr-title';
      span.textContent = labelOverride || (a && a.textContent) || '';
      head.appendChild(span);
      if (a) a.remove();
    }

    li.insertBefore(head, li.firstChild);

    // stabile ID für Persistenz: portletId + label
    var portlet = containerUL.closest('.vector-menu');
    var pid = (portlet && (portlet.id || portlet.getAttribute('id'))) || 'portlet';
    var labelText = (labelOverride || (head.querySelector('a, .kr-title') || {}).textContent || '').trim();
    li.dataset.krId = normId(pid + '::' + labelText);

    // Default: zu, dann Zustand aus STATE anwenden
    toggleSet(li, btn, /*expanded*/ false);
    var st = STATE[li.dataset.krId];
    if (st === 1) toggleSet(li, btn, true);

    return head;
  }

  function attachToggle(containerUL){
    if (containerUL.dataset.krToggleAttached) return;
    containerUL.dataset.krToggleAttached = '1';

    containerUL.addEventListener('click', function (e) {
      // robust: von Ziel nach oben zum Button laufen
      var arrow = e.target.closest('.kr-arrow');
      if (!arrow || !containerUL.contains(arrow)) return;
      e.preventDefault();

      var li = arrow.closest('li.kr-group, li.kr-top');
      if (!li) return;

      var expanded = li.classList.contains('is-collapsed');
      toggleSet(li, arrow, expanded); // invertieren

      var id = li.dataset.krId;
      if (id) {
        STATE[id] = expanded ? 1 : 0;
        saveState();
      }
    });
  }

  function buildGroups(ul) {
    var lis = Array.from(ul.querySelectorAll(':scope > li.mw-list-item'));
    var current = null;

    lis.forEach(function (li) {
      var a = li.querySelector(':scope > a');
      var d = extractDirective(a);

      if (d && d.kind === 'group') {
        var clickable = hasRealHref(a);   // Gruppe mit/ohne Link
        li.classList.add('kr-group');
        makeHead(li, clickable, ul, (a && a.textContent) || d.value || '');
        if (!clickable && a) a.remove();  // nur Texttitel
        else if (clickable) stripDirectiveHash(a);

        var sub = document.createElement('ul');
        sub.className = 'kr-sub';
        li.appendChild(sub);
        current = sub;
        return;
      }

      if (current && !(d && (d.kind === 'group' || d.kind === 'link'))) {
        current.appendChild(li);
      }
    });

    attachToggle(ul);
  }

  function buildPortlets(root) {
    var lists = root.querySelectorAll('.vector-menu .vector-menu-content-list');
    lists.forEach(function (ul) {
      if (ul.dataset.krSbDone) return;
      ul.dataset.krSbDone = '1';

      var items = Array.from(ul.querySelectorAll(':scope > li.mw-list-item'));
      var topIdx = items.findIndex(function (li) {
        var a = li.querySelector(':scope > a');
        var d = extractDirective(a);
        return d && d.kind === 'link';
      });

      if (topIdx >= 0) {
        var top = items[topIdx];
        top.classList.add('kr-top');
        makeHead(top, true, ul);          // klickbarer Titel
        stripDirectiveHash(top.querySelector(':scope > a'));

        var sub = document.createElement('ul');
        sub.className = 'kr-sub';
        top.appendChild(sub);

        for (var i = topIdx + 1; i < items.length; i++) sub.appendChild(items[i]);
        buildGroups(sub);                  // Gruppen in der Unterliste
        attachToggle(ul);                  // Toggle auch für kr-top
      } else {
        buildGroups(ul);
      }

      // aufräumen
      ul.querySelectorAll('a[href*="#link:"], a[href*="#group:"]').forEach(stripDirectiveHash);
      var portlet = ul.closest('.vector-menu');
      var heading = portlet && portlet.querySelector(':scope > .vector-menu-heading');
      if (heading) heading.remove();
    });
  }

  function processRoot(root){
    if (!root || root.dataset.krSbRootDone) return;
    root.dataset.krSbRootDone = '1';
    root.classList.add('kr-sb');
    buildPortlets(root);
  }

  function init() {
    document.querySelectorAll('#mw-panel, #vector-main-menu, #vector-main-menu-pinned, .vector-drawer')
      .forEach(processRoot);
  }

  init();
  mw.hook('wikipage.content').add(init);

  // auch wenn Drawer dynamisch montiert wird
  var mo = new MutationObserver(function (muts) {
    muts.forEach(function (m) {
      m.addedNodes && m.addedNodes.forEach(function (n) {
        if (!(n instanceof Element)) return;
        if (n.matches('#vector-main-menu, #vector-main-menu-pinned, .vector-drawer, #mw-panel')) processRoot(n);
        n.querySelectorAll && n.querySelectorAll('#vector-main-menu, #vector-main-menu-pinned, .vector-drawer, #mw-panel')
          .forEach(processRoot);
      });
    });
  });
  mo.observe(document.body, { childList: true, subtree: true });
})();