MediaWiki:Common.js

Version vom 25. September 2026, 01:09 Uhr von Cezary (Diskussion | Beiträge) (Tabellen als Karten: immer die ganze Seite prüfen (der Inhalts-Haken liefert späten Empfängern nur die letzte Meldung, auf manchen Seiten ein kleiner eingefügter Teil))

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( ' ' ) );
		kopf.appendChild( el( 'span', 'ckw-weiter-stelle', '· 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 ); }
	} );
}() );

/* CK-Wissen (seit 25.09.2026): Tabellen, die nicht in die Seitenbreite passen, werden als Karten gezeigt.
   Jede Zeile wird eine Karte, die Spaltenüberschriften stehen als kleine Beschriftung über jedem Wert.
   Passt die Tabelle in die Breite, bleibt sie eine Tabelle. Gilt am Rechner und am Handy und prüft neu,
   wenn sich die Breite ändert oder ein zugeklappter Abschnitt aufgeht. Nur für einfache Tabellen mit
   Kopfzeile und ohne zusammengefasste Zellen (Zwischenüberschriften über die ganze Breite gehen). Abschalten für eine Tabelle: class="wikitable ckw-nie-karten".
   Die Gestaltung steht in MediaWiki:Common.css unter „Zu breite Tabellen als Karten“. */
( function () {
	'use strict';
	if ( mw.config.get( 'wgAction' ) !== 'view' ) { return; }

	function geeignet( t ) {
		if ( t.classList.contains( 'ckw-nie-karten' ) || t.classList.contains( 'ckw-karten-bereit' ) ) { return false; }
		if ( t.querySelector( 'table' ) || ( t.parentElement && t.parentElement.closest( 'table' ) ) ) { return false; }
		var zeilen = t.rows;
		if ( zeilen.length < 2 ) { return false; }
		var kopf = zeilen[ 0 ].cells;
		if ( kopf.length < 2 ) { return false; }
		for ( var i = 0; i < kopf.length; i++ ) {
			if ( kopf[ i ].tagName !== 'TH' || kopf[ i ].colSpan > 1 || kopf[ i ].rowSpan > 1 ) { return false; }
		}
		for ( var z = 1; z < zeilen.length; z++ ) {
			var zellen = zeilen[ z ].cells;
			// Zwischenüberschrift über die ganze Breite: wird in der Kartenansicht zur Überschrift
			if ( zellen.length === 1 && zellen[ 0 ].colSpan === kopf.length && zellen[ 0 ].rowSpan === 1 ) { continue; }
			if ( zellen.length !== kopf.length ) { return false; }
			for ( var k = 0; k < zellen.length; k++ ) {
				if ( zellen[ k ].colSpan > 1 || zellen[ k ].rowSpan > 1 ) { return false; }
			}
		}
		return true;
	}

	function beschrifte( t ) {
		var kopf = t.rows[ 0 ].cells;
		t.rows[ 0 ].classList.add( 'ckw-karten-kopf' );
		for ( var z = 1; z < t.rows.length; z++ ) {
			var zellen = t.rows[ z ].cells;
			if ( zellen.length === 1 && zellen[ 0 ].colSpan > 1 ) { t.rows[ z ].classList.add( 'ckw-karten-gruppe' ); continue; }
			for ( var k = 0; k < zellen.length; k++ ) {
				var name = kopf[ k ].textContent.replace( /\[\d+\]/g, '' ).replace( /\s+/g, ' ' ).trim();
				zellen[ k ].setAttribute( 'data-ckw-spalte', name );
				if ( !zellen[ k ].textContent.trim() && !zellen[ k ].querySelector( 'img' ) ) { zellen[ k ].classList.add( 'ckw-karten-leer' ); }
			}
		}
		t.classList.add( 'ckw-karten-bereit' );
	}

	function pruefe( t ) {
		var huelle = t.parentElement;
		if ( !huelle ) { return; }
		var platz = huelle.clientWidth;
		if ( !platz ) { return; } // unsichtbar, etwa in einem zugeklappten Abschnitt
		var war = t.classList.contains( 'ckw-karten' );
		if ( war ) { t.classList.remove( 'ckw-karten' ); }
		var zuBreit = t.scrollWidth > platz + 2;
		if ( zuBreit ) { t.classList.add( 'ckw-karten' ); }
		if ( huelle.classList.contains( 'ckw-tabelle' ) ) { huelle.classList.toggle( 'ckw-tabelle-karten', zuBreit ); }
	}

	function einrichten( wurzel ) {
		var tabellen = [];
		var liste = wurzel.querySelectorAll( '.mw-parser-output table.wikitable' );
		for ( var i = 0; i < liste.length; i++ ) {
			if ( geeignet( liste[ i ] ) ) { beschrifte( liste[ i ] ); tabellen.push( liste[ i ] ); }
		}
		if ( !tabellen.length ) { return; }
		var alle = function () { tabellen.forEach( pruefe ); };
		alle();
		if ( window.ResizeObserver ) {
			var breiten = new Map();
			var ro = new ResizeObserver( function ( eintraege ) {
				eintraege.forEach( function ( e ) {
					var b = Math.round( e.contentRect.width );
					if ( breiten.get( e.target ) === b ) { return; }
					breiten.set( e.target, b );
					tabellen.forEach( function ( t ) { if ( t.parentElement === e.target ) { pruefe( t ); } } );
				} );
			} );
			tabellen.forEach( function ( t ) { if ( t.parentElement ) { ro.observe( t.parentElement ); } } );
		} else {
			var warte;
			window.addEventListener( 'resize', function () { clearTimeout( warte ); warte = setTimeout( alle, 200 ); } );
		}
	}

	window.ckwTabellenAlsKarten = einrichten;
	// Immer die ganze Seite prüfen: Andere Skripte melden über denselben Haken auch kleine eingefügte Teile,
	// und ein später angemeldeter Empfänger bekommt nur die letzte Meldung. Bereits erledigte Tabellen werden übersprungen.
	var spaeter = function () { setTimeout( function () { einrichten( document ); }, 0 ); };
	mw.hook( 'wikipage.content' ).add( spaeter );
	$( spaeter );
}() );