MediaWiki:Common.js

Version vom 25. September 2026, 00:11 Uhr von Cezary (Diskussion | Beiträge) („Weiter im Thema“ am Artikelende (Rechner und Handy), automatisch aus MediaWiki:Sidebar)

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 () {
		// Themen der Leiste für „Weiter im Thema“ melden (vor dem Umbau der Überschriften)
		try {
			var panel = document.getElementById( 'mw-panel' ), gruppen = [];
			each( panel ? panel.querySelectorAll( '.vector-menu-portal' ) : [], function ( p ) {
				if ( p.id === 'p-tb' ) { return; }
				var kopf = p.querySelector( '.vector-menu-heading' ), links = [];
				each( p.querySelectorAll( 'li > a' ), function ( a ) {
					var text = a.textContent.trim(), href = a.getAttribute( 'href' );
					if ( text && href ) { links.push( [ text, href ] ); }
				} );
				if ( links.length ) { gruppen.push( { id: p.id, label: kopf ? kopf.textContent.trim() : '', links: links } ); }
			} );
			if ( gruppen.length ) { mw.hook( 'ckw.themen' ).fire( gruppen ); }
		} catch ( e ) { mw.log.warn( 'CK-Wissen Themen:', e ); }
		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 ); }
	} );
}() );

/* CK-Wissen (seit 24.09.2026): Handy-Ansicht – „Gerade eine Attacke?“ oben auf der Seite,
   alle Themen der linken Leiste im Menü (☰) und unten auf jeder Seite.
   Die Themen kommen automatisch aus MediaWiki:Sidebar: Das Skript liest die Desktop-Leiste einer
   leeren Seite und merkt sie sich im Browser für 12 Stunden. Gestaltung am Ende von MediaWiki:Common.css. */
( function () {
	'use strict';
	if ( mw.config.get( 'skin' ) !== 'minerva' ) { return; }

	var SPEICHER = 'ckw-themen-v1';
	var HALTBAR = 12 * 60 * 60 * 1000;
	var skript = mw.config.get( 'wgScript' );
	var AKUT_VORAB = [
		[ 'Sauerstoff', skript + '?title=Sauerstoff' ],
		[ 'Triptane', skript + '?title=Triptan' ],
		[ 'In der Notaufnahme', skript + '?title=Cluster-Kopfschmerz_in_der_Notaufnahme' ]
	];
	var hier = mw.config.get( 'wgPageName' );
	var istInhalt = mw.config.get( 'wgAction' ) === 'view' &&
		mw.config.get( 'wgNamespaceNumber' ) >= 0 &&
		!mw.config.get( 'wgIsMainPage' );

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

	function el( tag, klasse, text ) {
		var e = document.createElement( tag );
		if ( klasse ) { e.className = klasse; }
		if ( text ) { e.textContent = text; }
		return e;
	}

	function verweis( paar ) {
		var a = el( 'a', null, paar[ 0 ] );
		a.href = paar[ 1 ];
		if ( titelAus( paar[ 1 ] ) === hier ) {
			a.className = 'ckw-hier';
			a.setAttribute( 'aria-current', 'page' );
		}
		return a;
	}

	/* „Gerade eine Attacke?“ unter dem Seitentitel: die ersten drei Seiten der Gruppe „Gegen die Attacke“ */
	function akutLeiste( links ) {
		var nav = el( 'nav', 'ckw-mobil-akut' );
		nav.setAttribute( 'aria-label', 'Gerade eine Attacke?' );
		nav.appendChild( el( 'div', 'ckw-mobil-akut-titel', 'Gerade eine Attacke?' ) );
		var reihe = el( 'div', 'ckw-mobil-akut-links' );
		links.slice( 0, 3 ).forEach( function ( paar ) { reihe.appendChild( verweis( paar ) ); } );
		nav.appendChild( reihe );
		var alt = document.querySelector( '.ckw-mobil-akut' );
		if ( alt ) { alt.parentNode.replaceChild( nav, alt ); return; }
		var titel = document.querySelector( '.heading-holder' );
		if ( titel ) { titel.parentNode.insertBefore( nav, titel.nextSibling ); }
	}

	function gruppenBlock( gruppen, klasse ) {
		var box = el( 'div', klasse );
		gruppen.forEach( function ( g ) {
			var liste = el( 'ul' ), hatHier = false;
			g.links.forEach( function ( paar ) {
				var li = el( 'li' );
				var a = verweis( paar );
				if ( a.className === 'ckw-hier' ) { hatHier = true; }
				li.appendChild( a );
				liste.appendChild( li );
			} );
			if ( g.id === 'p-navigation' ) {
				var start = el( 'div', 'ckw-gruppe ckw-gruppe-start' );
				start.appendChild( liste );
				box.appendChild( start );
				return;
			}
			var akut = g.id === 'p-Gegen_die_Attacke';
			var d = el( 'details', 'ckw-gruppe' + ( akut ? ' ckw-gruppe-akut' : '' ) );
			d.open = akut || hatHier;
			var s = el( 'summary' );
			s.appendChild( el( 'span', 'ckw-gruppe-name', g.label ) );
			var zahl = el( 'span', 'ckw-gruppe-zahl', String( g.links.length ) );
			zahl.setAttribute( 'aria-hidden', 'true' );
			s.appendChild( zahl );
			d.appendChild( s );
			d.appendChild( liste );
			box.appendChild( d );
		} );
		return box;
	}

	function zeigen( gruppen ) {
		var akut = gruppen.filter( function ( g ) { return g.id === 'p-Gegen_die_Attacke'; } )[ 0 ];
		if ( istInhalt && akut && akut.links.length ) { akutLeiste( akut.links ); }

		// Menü (☰): Themen nach „Start“ und „Zufällige Seite“
		var menue = document.getElementById( 'mw-mf-page-left' );
		if ( menue ) {
			var altM = menue.querySelector( '.ckw-menue-themen' );
			if ( altM ) { altM.parentNode.removeChild( altM ); }
			var themenM = gruppenBlock( gruppen.filter( function ( g ) { return g.id !== 'p-navigation'; } ), 'ckw-menue-themen' );
			var nav = menue.querySelector( '#p-navigation' );
			if ( nav ) { nav.parentNode.insertBefore( themenM, nav.nextSibling ); } else { menue.appendChild( themenM ); }
		}

		// Unten auf der Seite: alle Themen
		var haupt = document.getElementById( 'content' );
		if ( istInhalt && haupt ) {
			var altU = document.querySelector( '.ckw-mobil-themen' );
			if ( altU ) { altU.parentNode.removeChild( altU ); }
			var unten = el( 'nav', 'ckw-mobil-themen' );
			unten.setAttribute( 'aria-labelledby', 'ckw-mobil-themen-titel' );
			var h = el( 'h2', 'ckw-mobil-themen-titel', 'Themen' );
			h.id = 'ckw-mobil-themen-titel';
			unten.appendChild( h );
			unten.appendChild( gruppenBlock( gruppen, 'ckw-mobil-themen-liste' ) );
			haupt.appendChild( unten );
		}
		mw.hook( 'ckw.themen' ).fire( gruppen );
	}

	function ausLeiste( html ) {
		var doc = new DOMParser().parseFromString( html, 'text/html' );
		var gruppen = [];
		Array.prototype.forEach.call( doc.querySelectorAll( '#mw-panel .vector-menu-portal' ), function ( p ) {
			if ( p.id === 'p-tb' ) { return; }
			var kopf = p.querySelector( '.vector-menu-heading' );
			var links = [];
			Array.prototype.forEach.call( p.querySelectorAll( 'li > a' ), function ( a ) {
				var text = a.textContent.trim(), href = a.getAttribute( 'href' );
				if ( text && href ) { links.push( [ text, href ] ); }
			} );
			if ( links.length ) { gruppen.push( { id: p.id, label: kopf ? kopf.textContent.trim() : '', links: links } ); }
		} );
		return gruppen;
	}

	$( function () {
		try {
			if ( istInhalt ) { akutLeiste( AKUT_VORAB ); }
			var gemerkt = null;
			try { gemerkt = JSON.parse( localStorage.getItem( SPEICHER ) || 'null' ); } catch ( e ) {}
			if ( gemerkt && gemerkt.gruppen && Date.now() - gemerkt.zeit < HALTBAR ) {
				zeigen( gemerkt.gruppen );
				return;
			}
			fetch( skript + '?title=Special:BlankPage&useskin=vector&useformat=desktop', { credentials: 'omit' } )
				.then( function ( r ) { return r.ok ? r.text() : Promise.reject( r.status ); } )
				.then( function ( html ) {
					var gruppen = ausLeiste( html );
					if ( !gruppen.length ) { return; }
					try { localStorage.setItem( SPEICHER, JSON.stringify( { zeit: Date.now(), gruppen: gruppen } ) ); } catch ( e ) {}
					zeigen( gruppen );
				} )
				.catch( function () {
					if ( gemerkt && gemerkt.gruppen ) { zeigen( gemerkt.gruppen ); }
				} );
		} catch ( e ) {
			mw.log.warn( 'CK-Wissen Handy-Navigation:', e );
		}
	} );
}() );

/* CK-Wissen (seit 25.09.2026): „Weiter im Thema“ am Ende jedes Artikels – vorherige und nächste Seite
   aus derselben Gruppe der linken Leiste (MediaWiki:Sidebar), am Rechner und auf dem Handy.
   Steht vor „Siehe auch“, „Literatur“, „Weblinks“ bzw. „Einzelnachweise“, sonst am Ende des Textes. */
( function () {
	'use strict';
	var ENDE = /^(Siehe auch|Literatur|Weblinks|Externe Links|Einzelnachweise|Quellen|Belege|Referenzen|Fußnoten|Anmerkungen)\b/i;

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

	function el( tag, klasse, text ) {
		var e = document.createElement( tag );
		if ( klasse ) { e.className = klasse; }
		if ( text ) { e.textContent = text; }
		return e;
	}

	function karte( paar, richtung, klasse ) {
		var a = el( 'a', 'ckw-weiter-karte ' + klasse );
		a.href = paar[ 1 ];
		a.appendChild( el( 'span', 'ckw-weiter-richtung', richtung ) );
		a.appendChild( el( 'span', 'ckw-weiter-titel', paar[ 0 ] ) );
		return a;
	}

	function weiter( gruppen ) {
		if ( mw.config.get( 'wgAction' ) !== 'view' || mw.config.get( 'wgIsMainPage' ) ||
			mw.config.get( 'wgNamespaceNumber' ) < 0 ||
			mw.config.get( 'wgRevisionId' ) !== mw.config.get( 'wgCurRevisionId' ) ) { return; }
		var namen = [ mw.config.get( 'wgPageName' ) ];
		if ( mw.config.get( 'wgRedirectedFrom' ) ) { namen.push( String( mw.config.get( 'wgRedirectedFrom' ) ).replace( / /g, '_' ) ); }
		var spalte = document.querySelector( '#mw-content-text > .mw-parser-output' );
		if ( !spalte ) { return; }

		var gruppe = null, liste = null, stelle = -1;
		gruppen.some( function ( g ) {
			if ( g.id === 'p-navigation' ) { return false; }
			var seiten = g.links.filter( function ( p ) {
				var t = titelAus( p[ 1 ] );
				return t && !/^(Spezial|Special):/.test( t );
			} );
			for ( var i = 0; i < seiten.length; i++ ) {
				if ( namen.indexOf( titelAus( seiten[ i ][ 1 ] ) ) !== -1 ) { gruppe = g; liste = seiten; stelle = i; return true; }
			}
			return false;
		} );
		if ( !gruppe || liste.length < 2 ) { return; }

		var alt = spalte.querySelector( '.ckw-weiter' );
		if ( alt ) { alt.parentNode.removeChild( alt ); }
		var nav = el( 'nav', 'ckw-weiter' );
		nav.setAttribute( 'aria-label', 'Weiter im Thema ' + gruppe.label );
		var kopf = el( 'div', 'ckw-weiter-kopf', 'Weiter im Thema ' );
		kopf.appendChild( el( 'b', null, gruppe.label ) );
		kopf.appendChild( document.createTextNode( ' · Seite ' + ( stelle + 1 ) + ' von ' + liste.length ) );
		nav.appendChild( kopf );
		var karten = el( 'div', 'ckw-weiter-karten' );
		if ( stelle > 0 ) { karten.appendChild( karte( liste[ stelle - 1 ], '← Zurück', 'ckw-weiter-zurueck' ) ); }
		if ( stelle < liste.length - 1 ) { karten.appendChild( karte( liste[ stelle + 1 ], 'Weiter →', 'ckw-weiter-vor' ) ); }
		else if ( stelle >= 2 ) { karten.appendChild( karte( liste[ 0 ], 'Zum Anfang des Themas ↺', 'ckw-weiter-vor' ) ); }
		nav.appendChild( karten );

		// Vor „Siehe auch“, „Literatur“, „Einzelnachweise“ … einsetzen, sonst ans Ende
		var ziel = null;
		Array.prototype.some.call( spalte.querySelectorAll( 'h2' ), function ( h ) {
			if ( h.parentNode !== spalte ) { return false; }
			var t = ( h.querySelector( '.mw-headline' ) || h ).textContent.trim();
			if ( ENDE.test( t ) ) { ziel = h; return true; }
			return false;
		} );
		if ( ziel ) { spalte.insertBefore( nav, ziel ); } else { spalte.appendChild( nav ); }
	}

	mw.hook( 'ckw.themen' ).add( function ( gruppen ) {
		try { weiter( gruppen ); } catch ( e ) { mw.log.warn( 'CK-Wissen Weiter im Thema:', e ); }
	} );
}() );