Zum Inhalt springen
Das Halloween-Event (Süßes oder Saures) findet vom 24.10.2025 bis 07.11.2025 statt.

MediaWiki:Common.js: Unterschied zwischen den Versionen

Aus Firestone Idle RPG Wiki
Keine Bearbeitungszusammenfassung
Keine Bearbeitungszusammenfassung
Zeile 2: Zeile 2:
/* ==== Sidebar: klickbare Kopfzeile + Gruppen mit Auf/Zu + Persistenz (Vector-2022 & Drawer) ==== */
/* ==== Sidebar: klickbare Kopfzeile + Gruppen mit Auf/Zu + Persistenz (Vector-2022 & Drawer) ==== */
(function () {
(function () {
   if (mw.config.get('skin') !== 'vector-2022') return;
  'use strict';
   if ( mw.config.get('skin') !== 'vector-2022' ) return;


   var STORAGE_KEY = 'kr-sb-state:v3:' + (mw.config.get('wgUserName') || 'anon');
  /* ---------- Persistenz ---------- */
   var STORAGE_KEY = 'kr-sb-state:v3:' + ( mw.config.get('wgUserName') || 'anon' );
  function loadState() {
    try { return JSON.parse( localStorage.getItem(STORAGE_KEY) || '{}' ); }
    catch (e) { return {}; }
  }
  function saveState(state) {
    try { localStorage.setItem( STORAGE_KEY, JSON.stringify(state) ); }
    catch (e) {}
  }
   var STATE = loadState();
   var STATE = loadState();


   function loadState(){ try { return JSON.parse(localStorage.getItem(STORAGE_KEY) || '{}'); } catch(e){ return {}; } }
   /* ---------- Utils ---------- */
  function saveState(){ try { localStorage.setItem(STORAGE_KEY, JSON.stringify(STATE)); } catch(e){} }
   function extractDirective(a) {
 
   function extractDirective(a){
     if (!a) return null;
     if (!a) return null;
     var href = a.getAttribute('href') || '';
     var href = a.getAttribute('href') || '';
     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.includes('#group:')) return { kind:'group', value: decodeURIComponent(s.split('#group:').pop()) };
     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())  };
     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;
     a.setAttribute('href', (a.getAttribute('href') || '').replace(/#(?:link|group):[^#]*$/i, ''));
     a.setAttribute('href', (a.getAttribute('href') || '').replace(/#(?:link|group):[^#]*$/i, '') );
   }
   }
   function hasRealHref(a){
   function hasRealHref(a) {
     if (!a) return false;
     if (!a) return false;
     var h = a.getAttribute('href') || '';
     return !/^#/.test( a.getAttribute('href') || '' );
    return !/^#/.test(h);
   }
   }
   function normId(s){ return String(s||'').trim().toLowerCase().replace(/\s+/g,'-').replace(/[^\w\-:.|]/g,''); }
   function normId(s) {
   function toggleSet(li, arrow, expanded){
    return String(s || '').trim().toLowerCase()
     if (expanded){ li.classList.remove('is-collapsed'); arrow.setAttribute('aria-expanded','true'); }
      .replace(/\s+/g, '-').replace(/[^\w\-:.|]/g, '');
     else { li.classList.add('is-collapsed'); arrow.setAttribute('aria-expanded','false'); }
  }
   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, containerUL, labelOverride){
  /* ---------- Kopf + ID ---------- */
   function makeHead(li, keepLink, containerUL, labelOverride) {
     var a = li.querySelector(':scope > a');
     var a = li.querySelector(':scope > a');
     var head = document.createElement('div'); head.className = 'kr-head';
 
     var head = document.createElement('div');
    head.className = 'kr-head has-toggle'; // ganze Kopfzeile darf toggeln (außer Link)
 
     var btn = document.createElement('button');
     var btn = document.createElement('button');
     btn.type = 'button';
     btn.type = 'button';
Zeile 43: Zeile 57:
     head.appendChild(btn);
     head.appendChild(btn);


    /* >>> NEU: unsichtbare Klickfläche rechts */
     if (keepLink && a) { stripDirectiveHash(a); head.appendChild(a); }
    var hit = document.createElement('span');
    hit.className = 'kr-hit';
    head.appendChild(hit);
 
     if (keepLink && a){ stripDirectiveHash(a); head.appendChild(a); }
     else {
     else {
       var span = document.createElement('span'); span.className='kr-title';
       var span = document.createElement('span');
      span.className = 'kr-title';
       span.textContent = labelOverride || (a && a.textContent) || '';
       span.textContent = labelOverride || (a && a.textContent) || '';
       head.appendChild(span); if (a) a.remove();
       head.appendChild(span);
      if (a) a.remove();
     }
     }


     li.insertBefore(head, li.firstChild);
     li.insertBefore(head, li.firstChild);


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


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


Zeile 70: Zeile 82:
   }
   }


   function handleArrowClick(btn){
  /* ---------- Toggle-Handler ---------- */
     var li = btn.closest('li.kr-group, li.kr-top'); if (!li) return;
   function handleArrowClick(btn) {
     var li = btn.closest('li.kr-group, li.kr-top');
    if (!li) return;
 
     var willExpand = li.classList.contains('is-collapsed');
     var willExpand = li.classList.contains('is-collapsed');
     toggleSet(li, btn, willExpand);
     toggleSet(li, btn, willExpand);


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


   // Globaler Delegations-Listener – funktioniert auch im Drawer
   /* Globale Delegation: Pfeil klickt immer; Kopfzeile klickt nur, wenn NICHT im Link */
   document.addEventListener('click', function (e) {
   document.addEventListener('click', function (e) {
    // 1) Klick auf Pfeil ODER Hit-Zone: toggle
     var arrow = e.target && e.target.closest && e.target.closest('.kr-arrow');
     var toggleEl = e.target && e.target.closest && e.target.closest('.kr-arrow, .kr-hit');
     if (arrow) {
     if (toggleEl) {
       e.preventDefault();
       e.preventDefault();
       handleArrowClick(toggleEl.classList.contains('kr-arrow') ? toggleEl
       handleArrowClick(arrow);
                          : toggleEl.closest('.kr-head').querySelector('.kr-arrow'));
       return;
       return;
     }
     }
    // 2) Klick irgendwo auf die Kopfzeile, aber NICHT auf den Link => toggle
     var head = e.target && e.target.closest && e.target.closest('.kr-head');
     var head = e.target && e.target.closest && e.target.closest('.kr-head');
     if (head && !e.target.closest('a')) {
     if (head && !e.target.closest('.kr-head a')) {
       var arrow = head.querySelector('.kr-arrow');
      e.preventDefault(); // freie Fläche der Kopfzeile
       if (arrow) {
       var btn = head.querySelector('.kr-arrow');
        e.preventDefault();
       if (btn) handleArrowClick(btn);
        handleArrowClick(arrow);
      }
     }
     }
   }, true); // capture=true
   }, true); // capture=true, damit nichts dazwischenfunkt
 


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


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


       if (d && d.kind === 'group'){
       if (d && d.kind === 'group') {
         var clickable = hasRealHref(a);
         var clickable = hasRealHref(a);
         li.classList.add('kr-group');
         li.classList.add('kr-group');
Zeile 119: Zeile 128:
         if (!clickable && a) a.remove(); else if (clickable) stripDirectiveHash(a);
         if (!clickable && a) a.remove(); else if (clickable) stripDirectiveHash(a);


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


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


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


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


       if (topIdx >= 0){
       if (topIdx >= 0) {
         var top = items[topIdx];
         var top = items[topIdx];
         top.classList.add('kr-top');
         top.classList.add('kr-top');
         makeHead(top, true, ul);
         makeHead(top, true, ul);
         stripDirectiveHash(top.querySelector(':scope > a'));
         stripDirectiveHash( top.querySelector(':scope > a') );
 
        var sub = document.createElement('ul');
        sub.className = 'kr-sub';
        top.appendChild(sub);


        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]);
         for (var i = topIdx + 1; i < items.length; i++) sub.appendChild(items[i]);
         buildGroups(sub);
         buildGroups(sub);
Zeile 153: Zeile 171:
       }
       }


       // aufräumen
       // Direktiven aus hrefs entfernen; Vector-Portlet-Heading ausblenden
       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 161: Zeile 179:
   }
   }


   function processRoot(root){
  /* ---------- Bootstrapping ---------- */
   function processRoot(root) {
     if (!root || root.dataset.krSbRootDone) return;
     if (!root || root.dataset.krSbRootDone) return;
     root.dataset.krSbRootDone = '1';
     root.dataset.krSbRootDone = '1';
Zeile 168: Zeile 187:
   }
   }


   function init(){
   function init() {
     document.querySelectorAll('#mw-panel, #vector-main-menu, #vector-main-menu-pinned, .vector-drawer')
     document.querySelectorAll('#mw-panel, #vector-main-menu, #vector-main-menu-pinned, .vector-drawer')
       .forEach(processRoot);
       .forEach(processRoot);
Zeile 176: Zeile 195:
   mw.hook('wikipage.content').add(init);
   mw.hook('wikipage.content').add(init);


   // Drawer erscheint dynamisch → beobachten
   // Drawer/DOM-Änderungen beobachten
   var mo = new MutationObserver(function(muts){
   var mo = new MutationObserver(function (muts) {
     muts.forEach(function(m){
     muts.forEach(function (m) {
       m.addedNodes && m.addedNodes.forEach(function(n){
       m.addedNodes && m.addedNodes.forEach(function (n) {
         if (!(n instanceof Element)) return;
         if (!(n instanceof Element)) return;
         if (n.matches('#vector-main-menu, #vector-main-menu-pinned, .vector-drawer, #mw-panel')) processRoot(n);
         if (n.matches('#vector-main-menu, #vector-main-menu-pinned, .vector-drawer, #mw-panel')) processRoot(n);
Zeile 187: Zeile 206:
     });
     });
   });
   });
   mo.observe(document.body, { childList:true, subtree:true });
   mo.observe(document.body, { childList: true, subtree: true });
})();
})();

Version vom 10. Oktober 2025, 19:56 Uhr

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

  /* ---------- Persistenz ---------- */
  var STORAGE_KEY = 'kr-sb-state:v3:' + ( mw.config.get('wgUserName') || 'anon' );
  function loadState() {
    try { return JSON.parse( localStorage.getItem(STORAGE_KEY) || '{}' ); }
    catch (e) { return {}; }
  }
  function saveState(state) {
    try { localStorage.setItem( STORAGE_KEY, JSON.stringify(state) ); }
    catch (e) {}
  }
  var STATE = loadState();

  /* ---------- Utils ---------- */
  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;
    return !/^#/.test( a.getAttribute('href') || '' );
  }
  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'); }
  }

  /* ---------- Kopf + ID ---------- */
  function makeHead(li, keepLink, containerUL, labelOverride) {
    var a = li.querySelector(':scope > a');

    var head = document.createElement('div');
    head.className = 'kr-head has-toggle'; // ganze Kopfzeile darf toggeln (außer Link)

    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
    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; gespeicherten Zustand anwenden
    toggleSet(li, btn, false);
    var st = STATE[ li.dataset.krId ];
    if (st === 1) toggleSet(li, btn, true);

    return head;
  }

  /* ---------- Toggle-Handler ---------- */
  function handleArrowClick(btn) {
    var li = btn.closest('li.kr-group, li.kr-top');
    if (!li) return;

    var willExpand = li.classList.contains('is-collapsed');
    toggleSet(li, btn, willExpand);

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

  /* Globale Delegation: Pfeil klickt immer; Kopfzeile klickt nur, wenn NICHT im Link */
  document.addEventListener('click', function (e) {
    var arrow = e.target && e.target.closest && e.target.closest('.kr-arrow');
    if (arrow) {
      e.preventDefault();
      handleArrowClick(arrow);
      return;
    }
    var head = e.target && e.target.closest && e.target.closest('.kr-head');
    if (head && !e.target.closest('.kr-head a')) {
      e.preventDefault(); // freie Fläche der Kopfzeile
      var btn = head.querySelector('.kr-arrow');
      if (btn) handleArrowClick(btn);
    }
  }, true); // capture=true, damit nichts dazwischenfunkt

  /* ---------- Aufbau der Gruppen ---------- */
  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);
        li.classList.add('kr-group');
        makeHead(li, clickable, ul, (a && a.textContent) || d.value || '');
        if (!clickable && a) a.remove(); 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);
      }
    });
  }

  /* ---------- Portlets verarbeiten ---------- */
  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);
        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);
      } else {
        buildGroups(ul);
      }

      // Direktiven aus hrefs entfernen; Vector-Portlet-Heading ausblenden
      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();
    });
  }

  /* ---------- Bootstrapping ---------- */
  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);

  // Drawer/DOM-Änderungen beobachten
  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 });
})();