MediaWiki:Common.js: Unterschied zwischen den Versionen
Keine Bearbeitungszusammenfassung |
Keine Bearbeitungszusammenfassung |
||
| Zeile 37: | Zeile 37: | ||
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'; | ||
var btn | var btn = document.createElement('button'); | ||
btn.type = 'button'; | |||
btn.className = 'kr-arrow'; | |||
btn.setAttribute('aria-expanded', 'false'); | |||
head.appendChild(btn); | head.appendChild(btn); | ||
/* >>> NEU: unsichtbare Klickfläche rechts */ | |||
var hit = document.createElement('span'); | |||
hit.className = 'kr-hit'; | |||
head.appendChild(hit); | |||
if (keepLink && a){ stripDirectiveHash(a); head.appendChild(a); } | if (keepLink && a){ stripDirectiveHash(a); head.appendChild(a); } | ||
| Zeile 75: | Zeile 83: | ||
// Globaler Delegations-Listener – funktioniert auch im Drawer | // Globaler Delegations-Listener – funktioniert auch im Drawer | ||
document.addEventListener('click', function(e){ | document.addEventListener('click', function (e) { | ||
var | // 1) Klick auf Pfeil ODER Hit-Zone: toggle | ||
if ( | var toggleEl = e.target && e.target.closest && e.target.closest('.kr-arrow, .kr-hit'); | ||
e.preventDefault(); | if (toggleEl) { | ||
e.preventDefault(); | |||
}, true); | handleArrowClick(toggleEl.classList.contains('kr-arrow') ? toggleEl | ||
: toggleEl.closest('.kr-head').querySelector('.kr-arrow')); | |||
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'); | |||
if (head && !e.target.closest('a')) { | |||
var arrow = head.querySelector('.kr-arrow'); | |||
if (arrow) { | |||
e.preventDefault(); | |||
handleArrowClick(arrow); | |||
} | |||
} | |||
}, true); // capture=true | |||
function buildGroups(ul){ | function buildGroups(ul){ | ||
Version vom 10. Oktober 2025, 17:20 Uhr
/* Das folgende JavaScript wird für alle Benutzer geladen. */
/* ==== Sidebar: klickbare Kopfzeile + Gruppen mit Auf/Zu + Persistenz (Vector-2022 & Drawer) ==== */
(function () {
if (mw.config.get('skin') !== 'vector-2022') return;
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){} }
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'); }
}
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);
/* >>> NEU: unsichtbare Klickfläche rechts */
var hit = document.createElement('span');
hit.className = 'kr-hit';
head.appendChild(hit);
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);
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 gespeicherten Zustand anwenden
toggleSet(li, btn, false);
var st = STATE[li.dataset.krId];
if (st === 1) toggleSet(li, btn, true);
return head;
}
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();
}
}
// Globaler Delegations-Listener – funktioniert auch im Drawer
document.addEventListener('click', function (e) {
// 1) Klick auf Pfeil ODER Hit-Zone: toggle
var toggleEl = e.target && e.target.closest && e.target.closest('.kr-arrow, .kr-hit');
if (toggleEl) {
e.preventDefault();
handleArrowClick(toggleEl.classList.contains('kr-arrow') ? toggleEl
: toggleEl.closest('.kr-head').querySelector('.kr-arrow'));
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');
if (head && !e.target.closest('a')) {
var arrow = head.querySelector('.kr-arrow');
if (arrow) {
e.preventDefault();
handleArrowClick(arrow);
}
}
}, true); // capture=true
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);
});
}
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);
}
// 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);
// Drawer erscheint dynamisch → 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 });
})();