MediaWiki:Common.js: Porovnání verzí

Z Wiki JU
Ofara (diskuse | příspěvky)
Bez shrnutí editace
značka: revertováno
Ofara (diskuse | příspěvky)
Bez shrnutí editace
 
(Není zobrazeno 13 mezilehlých verzí od stejného uživatele.)
Řádek 63: Řádek 63:
//Vyhledávání
//Vyhledávání


/* Special:Search – pevné rozšířené, whitelist (remove z DOM), přejmenování, default check, nadpis */
 
(function () {
(function () {
  'use strict';
 
   if (mw.config.get('wgCanonicalSpecialPageName') !== 'Search') return;
   if (mw.config.get('wgCanonicalSpecialPageName') !== 'Search') return;


   // 1) vynutit profil=advanced (jednou přesměrováním, žádná smyčka)
   // ══════════════════════════════════════════════════════════════
   (function ensureAdvanced() {
  // KONFIGURACE
  // ══════════════════════════════════════════════════════════════
 
  // Povolené namespace (ID)
  const ALLOWED_NS = new Set([0, 6, 10, 14, 3004, 3008]);
 
  // Přejmenování labelů
   const RENAME_MAP = new Map([
    ['(Hlavní)', 'Stránky'],
    ['PřF', 'Binolupa – Přírodovědecká fakulta'],
    ['Soubor', 'Soubory'],
    ['Šablona', 'Šablony']
  ]);
 
  // ══════════════════════════════════════════════════════════════
  // 1) UPRAVENÍ URL PŘI PRVNÍM NAČTENÍ
  // ══════════════════════════════════════════════════════════════
 
  function ensureUrlParams() {
     const url = new URL(location.href);
     const url = new URL(location.href);
    let changed = false;
    // Zajisti profile=advanced
     if (url.searchParams.get('profile') !== 'advanced') {
     if (url.searchParams.get('profile') !== 'advanced') {
       url.searchParams.set('profile', 'advanced');
       url.searchParams.set('profile', 'advanced');
       location.replace(url.toString());
       changed = true;
     }
     }
  })();


  // --- konfigurace ---
    // Pokud není žádný ns parametr, nastav ns0=1
  // Standardní ID: 0 Hlavní, 6 Soubor, 10 Šablona, 14 Kategorie
    const hasNsParams = Array.from(url.searchParams.keys()).some(k => /^ns\d+$/.test(k));
  const ALLOW_IDS = new Set([0, 6, 10, 14]);
    if (!hasNsParams) {
  const ALLOW_NAMES = new Set([
      url.searchParams.set('ns0', '1');
    '(Hlavní)', 'Hlavní', 'Stránky',
      changed = true;
    'Aktuality',
    }
    'PřF', 'Binolupa – Přírodovědecká fakulta',
    'Soubor', 'Soubory',
    'Šablona', 'Šablony',
    'Kategorie'
  ]);
  const DEFAULT_CHECK_IDS = new Set([0]); // Stránky
  const RENAME = new Map([
    ['(Hlavní)', 'Stránky'],
    ['Hlavní',  'Stránky'],
    ['PřF',      'Binolupa – Přírodovědecká fakulta'],
    ['Soubor',  'Soubory'],
    ['Šablona',  'Šablony']
  ]);
  const norm = s => String(s || '').replace(/\u00A0/g,' ').replace(/\s+/g,' ').trim();
  const isPrF = t => /^(PřF|Binolupa – Přírodovědecká fakulta)$/i.test(norm(t));


  function getContainer() {
    if (changed) {
    return document.querySelector('#mw-searchoptions, .mw-search-nsoptions');
      location.replace(url.toString());
    }
   }
   }


   function processOnce(root) {
   ensureUrlParams();
    if (!root || root.dataset.nsTweaked === '1') return;
    root.dataset.nsTweaked = '1';


    // nadpis
  // ══════════════════════════════════════════════════════════════
     const heading = root.querySelector('h4, legend');
  // 2) ÚPRAVA NAMESPACE CHECKBOXŮ
     if (heading) heading.textContent = 'Co chcete vyhledávat?';
  // ══════════════════════════════════════════════════════════════
 
  function processNamespaces() {
     const container = document.querySelector('#mw-searchoptions');
     if (!container || container.dataset.processed) return;
    container.dataset.processed = '1';


     // schovat horní profilové záložky
     // Skrýt profil tabs
     const tabs = document.querySelector('.mw-search-profile-tabs');
     const tabs = document.querySelector('.mw-search-profile-tabs');
     if (tabs) tabs.style.display = 'none';
     if (tabs) tabs.style.display = 'none';


     // najdi všechny checkboxy jmenných prostorů
     // Změnit nadpis
     const cbs = root.querySelectorAll('input[type="checkbox"][id^="mw-search-ns"], input[type="checkbox"][name="ns[]"]');
    const heading = container.querySelector('legend');
    cbs.forEach(cb => {
    if (heading) heading.textContent = 'Co chcete vyhledávat?';
      // najdi řádek
 
       const row = cb.closest('.oo-ui-fieldLayout') || cb.closest('div');
    // Načíst ns parametry z URL
    const urlParams = new URLSearchParams(location.search);
    const selectedNs = new Set();
     for (const [key, val] of urlParams) {
      const match = /^ns(\d+)$/.exec(key);
      if (match && val === '1') {
        selectedNs.add(parseInt(match[1], 10));
      }
    }
 
    // Zpracovat checkboxy
    container.querySelectorAll('input[type="checkbox"][id^="mw-search-ns"]').forEach(cb => {
       const row = cb.closest('.oo-ui-fieldLayout');
       if (!row) return;
       if (!row) return;


       // label
       // Získat NS ID z checkbox ID
       const label = (cb.id && root.querySelector('label[for="'+cb.id+'"]')) || row.querySelector('label');
       const match = cb.id.match(/mw-search-ns(\d+)/);
       if (!label) return;
      if (!match) { row.remove(); return; }
        
      const nsId = parseInt(match[1], 10);


       // zjisti ID NS
       // Filtrovat podle whitelistu
      let nsId = NaN;
       if (!ALLOWED_NS.has(nsId)) {
      const m = cb.id && cb.id.match(/mw-search-ns(\d+)/);
         row.remove();
       if (m) nsId = parseInt(m[1], 10);
         return;
      if (isNaN(nsId)) {
         const v = cb.getAttribute('value');
         if (v && /^\d+$/.test(v)) nsId = parseInt(v, 10);
       }
       }


       // původní text a přejmenování
       // Přejmenovat label
       const orig = norm(label.textContent);
       const label = row.querySelector(`label[for="${cb.id}"]`);
      if (RENAME.has(orig)) label.textContent = RENAME.get(orig);
      if (label) {
       const current = norm(label.textContent);
        const origText = label.textContent.trim();
        if (RENAME_MAP.has(origText)) {
          label.textContent = RENAME_MAP.get(origText);
        }
      }
 
      // Nastavit checked stav podle URL
      cb.checked = selectedNs.has(nsId);
    });
 
    // Skrýt "Zapamatovat si výběr" (pokud existuje)
    const rememberCheckbox = container.querySelector('#mw-search-powersearch-remember');
    if (rememberCheckbox) {
      const rememberRow = rememberCheckbox.closest('.oo-ui-fieldLayout');
      if (rememberRow) rememberRow.remove();
    }
 
    // Skrýt toggle buttony (Všechno/Nic)
    const toggleBox = container.querySelector('#mw-search-togglebox');
    if (toggleBox) toggleBox.style.display = 'none';
  }
 
  // ══════════════════════════════════════════════════════════════
  // 3) SUBMIT HANDLER PRO OBA FORMULÁŘE
  // ══════════════════════════════════════════════════════════════
 
  function bindSearchSubmit() {
    // Horní vyhledávací formulář
    const topForm = document.querySelector('#searchform');
    if (topForm && !topForm.dataset.bound) {
      topForm.dataset.bound = '1';
       topForm.addEventListener('submit', handleSubmit);
    }
 
    // Hlavní formulář s checkboxy
    const mainForm = document.querySelector('#powersearch');
    if (mainForm && !mainForm.dataset.bound) {
      mainForm.dataset.bound = '1';
      mainForm.addEventListener('submit', handleSubmit);
    }
  }


      // whitelist → nechtěné rovnou odstraníme z DOM
  function handleSubmit(ev) {
      const keep = ALLOW_IDS.has(nsId) || ALLOW_NAMES.has(orig) || ALLOW_NAMES.has(current);
    ev.preventDefault();
      if (!keep) { row.remove(); return; }
   
    // Najít vyhledávací input (může být v různých formulářích)
    const searchInput = ev.target.querySelector('input[name="search"], input[name="searchtext"], input[type="search"]');
    const query = searchInput ? searchInput.value.trim() : '';


      // default: Stránky + PřF
    // Sestavit URL
      if (DEFAULT_CHECK_IDS.has(nsId) || isPrF(orig) || isPrF(current)) cb.checked = true;
    const targetUrl = new URL(mw.util.getUrl('Special:Search'), location.origin);
     });
    if (query) targetUrl.searchParams.set('search', query);
    targetUrl.searchParams.set('profile', 'advanced');
    targetUrl.searchParams.set('fulltext', '1');
 
    // Přidat vybrané namespace z checkboxů
    const container = document.querySelector('#mw-searchoptions');
    if (container) {
      container.querySelectorAll('input[type="checkbox"][id^="mw-search-ns"]:checked').forEach(cb => {
        const match = cb.id.match(/mw-search-ns(\d+)/);
        if (match) {
          targetUrl.searchParams.set('ns' + match[1], '1');
        }
      });
    }
 
    // Přesměrovat
    location.href = targetUrl.toString();
  }
 
  // ══════════════════════════════════════════════════════════════
  // 4) SPUŠTĚNÍ
  // ══════════════════════════════════════════════════════════════
 
  function init() {
    const container = document.querySelector('#mw-searchoptions');
    const hasCheckboxes = container && container.querySelector('input[type="checkbox"][id^="mw-search-ns"]');
   
    if (hasCheckboxes) {
      processNamespaces();
      bindSearchSubmit();
     } else {
      // Počkat na DOM (max 5 sekund)
      const attempts = 50;
      let count = 0;
      const interval = setInterval(() => {
        const c = document.querySelector('#mw-searchoptions');
        const ready = c && c.querySelector('input[type="checkbox"][id^="mw-search-ns"]');
        if (ready) {
          clearInterval(interval);
          processNamespaces();
          bindSearchSubmit();
        } else if (++count >= attempts) {
          clearInterval(interval);
        }
      }, 100);
    }
   }
   }


   // počkej, až se kontejner i checkboxy opravdu objeví, pak to jednou zpracuj
   // Spustit po DOMContentLoaded
   function waitAndRun(tries=0) {
   if (document.readyState === 'loading') {
     const root = getContainer();
     document.addEventListener('DOMContentLoaded', init);
    const ready = root && root.querySelector('input[type="checkbox"][id^="mw-search-ns"], input[type="checkbox"][name="ns[]"]');
  } else {
    if (ready) { processOnce(root); return; }
     init();
     if (tries < 60) setTimeout(() => waitAndRun(tries+1), 100); // max ~6s, bez zacyklení
   }
   }


   // spustit po načtení stránky i obsahu
   // Hook pro MediaWiki live preview
  document.addEventListener('DOMContentLoaded', () => waitAndRun());
   mw.hook('wikipage.content').add(init);
   mw.hook('wikipage.content').add(() => waitAndRun());
 
})();
})();


//expanded podstránky automaticky
// Vždy vynutit EXPANDED stav edit footeru – i pro přihlášené
mw.loader.using(['mediawiki.util', 'mediawiki.storage']).then(function () {
  var SEL_TOGGLER = '.mw-editfooter-toggler';


// --- Vynucení profile=advanced + ns0=1 už v URL, aby první výsledek hledal i v Hlavním ---
  function expandAll($root) {
(function () {
    var $scope = $root || $(document);
  if (mw.config.get('wgCanonicalSpecialPageName') !== 'Search') return;
    $scope.find(SEL_TOGGLER).each(function () {
      var $btn = $(this);
      var targetId = $btn.attr('aria-controls');
      var $panel = targetId ? $('#' + CSS.escape(targetId)) : $btn.next();


  const url = new URL(location.href);
      // přepni do expanded
  let changed = false;
      $btn.removeClass('mw-icon-arrow-collapsed')
          .addClass('mw-icon-arrow-expanded')
          .attr('aria-expanded', 'true');


  // profil = advanced (ať je UI správné)
      if ($panel.length) {
  if (url.searchParams.get('profile') !== 'advanced') {
        $panel.show().attr('aria-hidden', 'false');
    url.searchParams.set('profile', 'advanced');
      }
     changed = true;
     });
   }
   }


   // pokud v URL není žádný ns parametr NEBO chybí ns0, doplň ns0=1
   // 1) Zruš „zapamatovaný“ stav (lokální i MW storage – někteří skini/gadgety to ukládají)
   const hasAnyNsParam = Array.from(url.searchParams.keys()).some(k => /^ns\d+$/.test(k));
   try {
   if (!hasAnyNsParam || !url.searchParams.has('ns0')) {
    [localStorage, sessionStorage].forEach(function (store) {
     url.searchParams.set('ns0', '1');
      Object.keys(store).forEach(function (k) {
     changed = true;
        if (/(editfooter|mw\-editfooter|mw\-collapsible)/i.test(k)) {
   }
          store.removeItem(k);
        }
      });
    });
    if (mw.storage && mw.storage.remove) {
      // nejlepší snaha – kdyby to někdo ukládal přes mw.storage
      ['editfooter', 'mw-editfooter', 'mw-collapsible'].forEach(function (k) {
        try { mw.storage.remove(k); } catch (e) {}
      });
    }
  } catch (e) {}
 
  // 2) Vynucení po různých fázích načtení (přebije později běžící skripta)
   mw.hook('wikipage.content').add(function ($content) { expandAll($content); });
  $(function () { expandAll(); });          // DOM ready
  $(window).on('load', function () { expandAll(); }); // po všem
 
  // 3) „Hlídač“ – pokud něco zase přepne do collapsed, rovnou to vrátíme
  var obs = new MutationObserver(function (mutations) {
     mutations.forEach(function (m) {
      if (
        m.type === 'attributes' &&
        m.target.matches &&
        m.target.matches(SEL_TOGGLER) &&
        m.target.getAttribute('aria-expanded') !== 'true'
      ) {
        expandAll($(m.target).closest('body'));
      }
     });
   });


   if (changed) {
   // začneme sledovat všechny togglery (i když přibudou později)
    // jednorázově přenačti se správnými parametry (bez nekonečné smyčky)
  function observeNow() {
     location.replace(url.toString());
     document.querySelectorAll(SEL_TOGGLER).forEach(function (el) {
     return;
      obs.observe(el, { attributes: true, attributeFilter: ['class', 'aria-expanded'] });
     });
   }
   }
})();
  observeNow();
  setTimeout(observeNow, 0);
  setTimeout(observeNow, 300);
});

Aktuální verze z 30. 10. 2025, 09:19

/* Zde uvedený JavaScript bude použit pro všechny uživatele při načtení každé stránky */

//Zpětná vazba

$(function () {
  // Zruš jQuery UI vzhled tlačítka
  $('.articleFeedbackv5-submit').button('destroy');
});

document.addEventListener('DOMContentLoaded', function () {
  document.querySelectorAll('.extern-download').forEach(el => {
    el.style.cursor = 'pointer';
    el.style.color = '#0645AD';
    el.style.textDecoration = 'underline';
    el.addEventListener('click', () => {
      const a = document.createElement('a');
      a.href = el.getAttribute('data-url');
      a.setAttribute('download', '');
      a.style.display = 'none';
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
    });
  });
});

(function () {
  var NOTE_ID = 'af5-note';
  var NOTE_HTML =
    '<div id="' + NOTE_ID + '" class="articleFeedbackv5-note">' +
      '<b>Tento formulář slouží pouze jako zpětná vazba pro tuto stránku.</b> ' +
      'Řešíte-li nějaký problém, napište e-mail na ' +
      '<a href="/Servicedesk/Seznam_e-mailových_adres_pro_zadávání_požadavků">službu,</a> které se Váš problém týká.' +
    '</div>';

   function injectNote(root) {
    var $title = (root ? $(root) : $(document))
      .find('.articleFeedbackv5-title-wrap')
      .first();

    if (!$title.length) return false;                 // nadpis není
    if (document.getElementById(NOTE_ID)) return true; // už vloženo

    $title.after(NOTE_HTML);                          // vložit pod nadpis
    return true;
  }

  if (injectNote()) return;

  var obs = new MutationObserver(function (mutations) {
    for (var m of mutations) {
      if (injectNote(m.target)) {
        obs.disconnect();
        break;
      }
    }
  });

  obs.observe(document.body, { childList: true, subtree: true });
  setTimeout(function () { injectNote(); obs.disconnect(); }, 5000);
})();

//Vyhledávání


(function () {
  'use strict';
  
  if (mw.config.get('wgCanonicalSpecialPageName') !== 'Search') return;

  // ══════════════════════════════════════════════════════════════
  // KONFIGURACE
  // ══════════════════════════════════════════════════════════════
  
  // Povolené namespace (ID)
  const ALLOWED_NS = new Set([0, 6, 10, 14, 3004, 3008]);
  
  // Přejmenování labelů
  const RENAME_MAP = new Map([
    ['(Hlavní)', 'Stránky'],
    ['PřF', 'Binolupa – Přírodovědecká fakulta'],
    ['Soubor', 'Soubory'],
    ['Šablona', 'Šablony']
  ]);

  // ══════════════════════════════════════════════════════════════
  // 1) UPRAVENÍ URL PŘI PRVNÍM NAČTENÍ
  // ══════════════════════════════════════════════════════════════
  
  function ensureUrlParams() {
    const url = new URL(location.href);
    let changed = false;

    // Zajisti profile=advanced
    if (url.searchParams.get('profile') !== 'advanced') {
      url.searchParams.set('profile', 'advanced');
      changed = true;
    }

    // Pokud není žádný ns parametr, nastav ns0=1
    const hasNsParams = Array.from(url.searchParams.keys()).some(k => /^ns\d+$/.test(k));
    if (!hasNsParams) {
      url.searchParams.set('ns0', '1');
      changed = true;
    }

    if (changed) {
      location.replace(url.toString());
    }
  }

  ensureUrlParams();

  // ══════════════════════════════════════════════════════════════
  // 2) ÚPRAVA NAMESPACE CHECKBOXŮ
  // ══════════════════════════════════════════════════════════════
  
  function processNamespaces() {
    const container = document.querySelector('#mw-searchoptions');
    if (!container || container.dataset.processed) return;
    container.dataset.processed = '1';

    // Skrýt profil tabs
    const tabs = document.querySelector('.mw-search-profile-tabs');
    if (tabs) tabs.style.display = 'none';

    // Změnit nadpis
    const heading = container.querySelector('legend');
    if (heading) heading.textContent = 'Co chcete vyhledávat?';

    // Načíst ns parametry z URL
    const urlParams = new URLSearchParams(location.search);
    const selectedNs = new Set();
    for (const [key, val] of urlParams) {
      const match = /^ns(\d+)$/.exec(key);
      if (match && val === '1') {
        selectedNs.add(parseInt(match[1], 10));
      }
    }

    // Zpracovat checkboxy
    container.querySelectorAll('input[type="checkbox"][id^="mw-search-ns"]').forEach(cb => {
      const row = cb.closest('.oo-ui-fieldLayout');
      if (!row) return;

      // Získat NS ID z checkbox ID
      const match = cb.id.match(/mw-search-ns(\d+)/);
      if (!match) { row.remove(); return; }
      
      const nsId = parseInt(match[1], 10);

      // Filtrovat podle whitelistu
      if (!ALLOWED_NS.has(nsId)) {
        row.remove();
        return;
      }

      // Přejmenovat label
      const label = row.querySelector(`label[for="${cb.id}"]`);
      if (label) {
        const origText = label.textContent.trim();
        if (RENAME_MAP.has(origText)) {
          label.textContent = RENAME_MAP.get(origText);
        }
      }

      // Nastavit checked stav podle URL
      cb.checked = selectedNs.has(nsId);
    });

    // Skrýt "Zapamatovat si výběr" (pokud existuje)
    const rememberCheckbox = container.querySelector('#mw-search-powersearch-remember');
    if (rememberCheckbox) {
      const rememberRow = rememberCheckbox.closest('.oo-ui-fieldLayout');
      if (rememberRow) rememberRow.remove();
    }

    // Skrýt toggle buttony (Všechno/Nic)
    const toggleBox = container.querySelector('#mw-search-togglebox');
    if (toggleBox) toggleBox.style.display = 'none';
  }

  // ══════════════════════════════════════════════════════════════
  // 3) SUBMIT HANDLER PRO OBA FORMULÁŘE
  // ══════════════════════════════════════════════════════════════
  
  function bindSearchSubmit() {
    // Horní vyhledávací formulář
    const topForm = document.querySelector('#searchform');
    if (topForm && !topForm.dataset.bound) {
      topForm.dataset.bound = '1';
      topForm.addEventListener('submit', handleSubmit);
    }

    // Hlavní formulář s checkboxy
    const mainForm = document.querySelector('#powersearch');
    if (mainForm && !mainForm.dataset.bound) {
      mainForm.dataset.bound = '1';
      mainForm.addEventListener('submit', handleSubmit);
    }
  }

  function handleSubmit(ev) {
    ev.preventDefault();
    
    // Najít vyhledávací input (může být v různých formulářích)
    const searchInput = ev.target.querySelector('input[name="search"], input[name="searchtext"], input[type="search"]');
    const query = searchInput ? searchInput.value.trim() : '';

    // Sestavit URL
    const targetUrl = new URL(mw.util.getUrl('Special:Search'), location.origin);
    if (query) targetUrl.searchParams.set('search', query);
    targetUrl.searchParams.set('profile', 'advanced');
    targetUrl.searchParams.set('fulltext', '1');

    // Přidat vybrané namespace z checkboxů
    const container = document.querySelector('#mw-searchoptions');
    if (container) {
      container.querySelectorAll('input[type="checkbox"][id^="mw-search-ns"]:checked').forEach(cb => {
        const match = cb.id.match(/mw-search-ns(\d+)/);
        if (match) {
          targetUrl.searchParams.set('ns' + match[1], '1');
        }
      });
    }

    // Přesměrovat
    location.href = targetUrl.toString();
  }

  // ══════════════════════════════════════════════════════════════
  // 4) SPUŠTĚNÍ
  // ══════════════════════════════════════════════════════════════
  
  function init() {
    const container = document.querySelector('#mw-searchoptions');
    const hasCheckboxes = container && container.querySelector('input[type="checkbox"][id^="mw-search-ns"]');
    
    if (hasCheckboxes) {
      processNamespaces();
      bindSearchSubmit();
    } else {
      // Počkat na DOM (max 5 sekund)
      const attempts = 50;
      let count = 0;
      const interval = setInterval(() => {
        const c = document.querySelector('#mw-searchoptions');
        const ready = c && c.querySelector('input[type="checkbox"][id^="mw-search-ns"]');
        if (ready) {
          clearInterval(interval);
          processNamespaces();
          bindSearchSubmit();
        } else if (++count >= attempts) {
          clearInterval(interval);
        }
      }, 100);
    }
  }

  // Spustit po DOMContentLoaded
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', init);
  } else {
    init();
  }

  // Hook pro MediaWiki live preview
  mw.hook('wikipage.content').add(init);

})();

//expanded podstránky automaticky
// Vždy vynutit EXPANDED stav edit footeru – i pro přihlášené
mw.loader.using(['mediawiki.util', 'mediawiki.storage']).then(function () {

  var SEL_TOGGLER = '.mw-editfooter-toggler';

  function expandAll($root) {
    var $scope = $root || $(document);
    $scope.find(SEL_TOGGLER).each(function () {
      var $btn = $(this);
      var targetId = $btn.attr('aria-controls');
      var $panel = targetId ? $('#' + CSS.escape(targetId)) : $btn.next();

      // přepni do expanded
      $btn.removeClass('mw-icon-arrow-collapsed')
          .addClass('mw-icon-arrow-expanded')
          .attr('aria-expanded', 'true');

      if ($panel.length) {
        $panel.show().attr('aria-hidden', 'false');
      }
    });
  }

  // 1) Zruš „zapamatovaný“ stav (lokální i MW storage – někteří skini/gadgety to ukládají)
  try {
    [localStorage, sessionStorage].forEach(function (store) {
      Object.keys(store).forEach(function (k) {
        if (/(editfooter|mw\-editfooter|mw\-collapsible)/i.test(k)) {
          store.removeItem(k);
        }
      });
    });
    if (mw.storage && mw.storage.remove) {
      // nejlepší snaha – kdyby to někdo ukládal přes mw.storage
      ['editfooter', 'mw-editfooter', 'mw-collapsible'].forEach(function (k) {
        try { mw.storage.remove(k); } catch (e) {}
      });
    }
  } catch (e) {}

  // 2) Vynucení po různých fázích načtení (přebije později běžící skripta)
  mw.hook('wikipage.content').add(function ($content) { expandAll($content); });
  $(function () { expandAll(); });           // DOM ready
  $(window).on('load', function () { expandAll(); }); // po všem

  // 3) „Hlídač“ – pokud něco zase přepne do collapsed, rovnou to vrátíme
  var obs = new MutationObserver(function (mutations) {
    mutations.forEach(function (m) {
      if (
        m.type === 'attributes' &&
        m.target.matches &&
        m.target.matches(SEL_TOGGLER) &&
        m.target.getAttribute('aria-expanded') !== 'true'
      ) {
        expandAll($(m.target).closest('body'));
      }
    });
  });

  // začneme sledovat všechny togglery (i když přibudou později)
  function observeNow() {
    document.querySelectorAll(SEL_TOGGLER).forEach(function (el) {
      obs.observe(el, { attributes: true, attributeFilter: ['class', 'aria-expanded'] });
    });
  }
  observeNow();
  setTimeout(observeNow, 0);
  setTimeout(observeNow, 300);
});