MediaWiki:Common.js

Version vom 24. September 2026, 23:36 Uhr von Cezary (Diskussion | Beiträge) (Für alle: linke Leiste zum Aufklappen (Thema der aktuellen Seite geht von selbst auf), „Auf dieser Seite“ neben dem Text; ersetzt den Vorschau-Schalter)

Hinweis: Leere nach dem Veröffentlichen den Browser-Cache, um die Änderungen sehen zu können.

  • Firefox/Safari: Umschalttaste drücken und gleichzeitig Aktualisieren anklicken oder entweder Strg+F5 oder Strg+R (⌘+R auf dem Mac) drücken
  • Google Chrome: Umschalttaste+Strg+R (⌘+Umschalttaste+R auf dem Mac) drücken
  • Edge: Strg+F5 drücken oder Strg drücken und gleichzeitig Aktualisieren anklicken
/* CK-Wissen (seit 24.09.2026): linke Leiste zum Aufklappen, „Auf dieser Seite“ rechts neben dem Text,
   Kleinigkeiten für die Lesespalte. Gestaltung dazu am Ende von MediaWiki:Common.css.
   Ohne Skript bleibt alles sichtbar (die Leiste geht nach 4 Sekunden von selbst auf). */
( function () {
	'use strict';

	var each = function ( liste, fn ) { Array.prototype.forEach.call( liste, fn ); };

	function titelAus( href ) {
		if ( !href ) { return null; }
		// nur einfache Seitenlinks (ohne &action=…, &oldid=… usw.)
		var m = href.match( /^[^?#]*\?title=([^&#]+)$/ ) || href.match( /\/index\.php\/([^?#]+)$/ );
		if ( !m ) { return null; }
		try { return decodeURIComponent( m[ 1 ] ).replace( / /g, '_' ); } catch ( e ) { return null; }
	}

	/* ---------- Linke Leiste ---------- */
	function leiste() {
		var panel = document.getElementById( 'mw-panel' );
		if ( !panel ) { return; }
		var hier = mw.config.get( 'wgPageName' );
		var SPEICHER = 'ckw-leiste';
		var gemerkt = {};
		try { gemerkt = JSON.parse( localStorage.getItem( SPEICHER ) || '{}' ) || {}; } catch ( e ) { gemerkt = {}; }
		var immerOffen = { 'p-navigation': true, 'p-Gegen_die_Attacke': true };

		each( panel.querySelectorAll( '.vector-menu-portal' ), function ( portal ) {
			var hatHier = false;
			each( portal.querySelectorAll( 'li > a' ), function ( a ) {
				if ( titelAus( a.getAttribute( 'href' ) ) === hier ) {
					a.parentNode.classList.add( 'ckw-hier' );
					a.setAttribute( 'aria-current', 'page' );
					hatHier = true;
				}
			} );
			if ( immerOffen[ portal.id ] ) { return; }
			var kopf = portal.querySelector( '.vector-menu-heading' );
			var inhalt = portal.querySelector( '.vector-menu-content' );
			if ( !kopf || !inhalt ) { return; }

			var anzahl = inhalt.querySelectorAll( 'li' ).length;
			if ( !inhalt.id ) { inhalt.id = portal.id + '-inhalt'; }
			var knopf = document.createElement( 'button' );
			knopf.type = 'button';
			knopf.className = 'ckw-klapp';
			knopf.setAttribute( 'aria-controls', inhalt.id );
			var text = document.createElement( 'span' );
			text.className = 'ckw-klapp-text';
			text.textContent = kopf.textContent.trim();
			var zahl = document.createElement( 'span' );
			zahl.className = 'ckw-klapp-zahl';
			zahl.textContent = String( anzahl );
			zahl.setAttribute( 'aria-hidden', 'true' );
			knopf.appendChild( text );
			knopf.appendChild( zahl );
			kopf.textContent = '';
			kopf.appendChild( knopf );

			var setze = function ( offen ) {
				knopf.setAttribute( 'aria-expanded', offen ? 'true' : 'false' );
				knopf.title = ( offen ? 'Zuklappen' : 'Aufklappen' ) + ' (' + anzahl + ' Seiten)';
				portal.classList.toggle( 'ckw-zu', !offen );
			};
			setze( hatHier || gemerkt[ portal.id ] === true );
			knopf.addEventListener( 'click', function () {
				var offen = knopf.getAttribute( 'aria-expanded' ) !== 'true';
				setze( offen );
				gemerkt[ portal.id ] = offen;
				try { localStorage.setItem( SPEICHER, JSON.stringify( gemerkt ) ); } catch ( e ) {}
			} );
		} );
		document.documentElement.classList.add( 'ckw-leiste-bereit' );
	}

	/* ---------- Lesespalte ---------- */
	function istLeseseite() {
		return mw.config.get( 'skin' ) === 'vector' &&
			mw.config.get( 'wgAction' ) === 'view' &&
			!mw.config.get( 'wgIsMainPage' ) &&
			[ 0, 1, 4, 5, 12 ].indexOf( mw.config.get( 'wgNamespaceNumber' ) ) !== -1;
	}

	function lesespalte( spalte ) {
		// Einleitung: erster Absatz vor der ersten Zwischenüberschrift, wenn er nicht zu lang ist
		var ersterAbsatz = null, kind = spalte.firstElementChild;
		while ( kind && kind.tagName !== 'H2' ) {
			if ( kind.tagName === 'P' && kind.textContent.trim().length > 40 ) { ersterAbsatz = kind; break; }
			kind = kind.nextElementSibling;
		}
		if ( ersterAbsatz && ersterAbsatz.textContent.length <= 650 ) { ersterAbsatz.classList.add( 'ckw-einleitung' ); }

		// Zu breite Tabellen seitlich schiebbar machen (nicht bei umflossenen Tabellen)
		each( spalte.querySelectorAll( 'table.wikitable' ), function ( t ) {
			if ( t.parentNode.classList.contains( 'ckw-tabelle' ) ) { return; }
			if ( window.getComputedStyle( t ).cssFloat !== 'none' ) { return; }
			var huelle = document.createElement( 'div' );
			huelle.className = 'ckw-tabelle';
			t.parentNode.insertBefore( huelle, t );
			huelle.appendChild( t );
		} );
	}

	/* ---------- „Auf dieser Seite“ ---------- */
	function seitennav( spalte ) {
		var toc = document.getElementById( 'toc' );
		var inhalt = document.getElementById( 'mw-content-text' );
		if ( !toc || !inhalt || !toc.querySelector( 'li.toclevel-1' ) ) { return; }

		var eintraege = [];
		var baue = function ( ul, ebene ) {
			var ol = document.createElement( 'ol' );
			each( ul.children, function ( li ) {
				var a = li.querySelector( ':scope > a' );
				if ( !a ) { return; }
				var neuLi = document.createElement( 'li' );
				var neuA = document.createElement( 'a' );
				neuA.href = a.getAttribute( 'href' );
				var t = a.querySelector( '.toctext' );
				neuA.textContent = ( t || a ).textContent;
				neuLi.appendChild( neuA );
				var ziel = null;
				try { ziel = document.getElementById( decodeURIComponent( neuA.hash.slice( 1 ) ) ); } catch ( e ) {}
				if ( ziel ) { eintraege.push( { a: neuA, li: neuLi, ziel: ziel.closest( 'h2, h3, h4' ) || ziel, ebene: ebene } ); }
				var unter = li.querySelector( ':scope > ul' );
				if ( unter && ebene < 2 ) { neuLi.appendChild( baue( unter, ebene + 1 ) ); }
				ol.appendChild( neuLi );
			} );
			return ol;
		};

		var nav = document.createElement( 'nav' );
		nav.className = 'ckw-seitennav';
		nav.setAttribute( 'aria-label', 'Auf dieser Seite' );
		var titel = document.createElement( 'div' );
		titel.className = 'ckw-seitennav-titel';
		titel.textContent = 'Auf dieser Seite';
		nav.appendChild( titel );
		nav.appendChild( baue( toc.querySelector( 'ul' ), 1 ) );
		var hoch = document.createElement( 'a' );
		hoch.href = '#top';
		hoch.className = 'ckw-nach-oben';
		hoch.textContent = 'Nach oben';
		nav.appendChild( hoch );
		inhalt.appendChild( nav );
		document.body.classList.add( 'ckw-mit-seitennav' );

		// Mitlaufende Markierung: welcher Abschnitt ist gerade zu sehen?
		var aktiv = null, geplant = false;
		var markiere = function () {
			geplant = false;
			if ( window.getComputedStyle( nav ).display === 'none' ) { return; }
			var neu = null;
			for ( var i = 0; i < eintraege.length; i++ ) {
				if ( eintraege[ i ].ziel.getBoundingClientRect().top <= 120 ) { neu = eintraege[ i ]; } else { break; }
			}
			if ( neu === aktiv ) { return; }
			each( nav.querySelectorAll( '.ckw-aktiv' ), function ( x ) { x.classList.remove( 'ckw-aktiv' ); } );
			each( nav.querySelectorAll( '.ckw-offen' ), function ( x ) { x.classList.remove( 'ckw-offen' ); } );
			aktiv = neu;
			if ( !neu ) { return; }
			neu.a.classList.add( 'ckw-aktiv' );
			var li = neu.li;
			while ( li && li !== nav ) {
				if ( li.tagName === 'LI' ) { li.classList.add( 'ckw-offen' ); }
				li = li.parentNode;
			}
			// Markierten Eintrag in der Liste sichtbar halten, ohne die Seite zu verschieben
			var oben = neu.a.offsetTop, unten = oben + neu.a.offsetHeight;
			if ( oben < nav.scrollTop + 40 ) { nav.scrollTop = Math.max( 0, oben - 60 ); }
			else if ( unten > nav.scrollTop + nav.clientHeight - 40 ) { nav.scrollTop = unten - nav.clientHeight + 60; }
		};
		var plane = function () {
			if ( !geplant ) { geplant = true; window.requestAnimationFrame( markiere ); }
		};
		window.addEventListener( 'scroll', plane, { passive: true } );
		window.addEventListener( 'resize', plane );
		markiere();
	}

	$( function () {
		try { leiste(); } catch ( e ) {
			document.documentElement.classList.add( 'ckw-leiste-offen' );
			mw.log.warn( 'CK-Wissen Leiste:', e );
		}
		if ( !istLeseseite() ) { return; }
		var spalte = document.querySelector( '#mw-content-text > .mw-parser-output' );
		if ( !spalte ) { return; }
		try { lesespalte( spalte ); } catch ( e ) { mw.log.warn( 'CK-Wissen Lesespalte:', e ); }
		try { seitennav( spalte ); } catch ( e ) { mw.log.warn( 'CK-Wissen Seitennavigation:', e ); }
	} );
}() );