MediaWiki:Gadget-entry-dom-layout.js

Z Wikisłownika – wolnego słownika wielojęzycznego

Uwaga: aby zobaczyć zmiany po opublikowaniu, może zajść potrzeba wyczyszczenia pamięci podręcznej przeglądarki.

  • Firefox / Safari: Przytrzymaj Shift podczas klikania Odśwież bieżącą stronę, lub naciśnij klawisze Ctrl+F5, lub Ctrl+R (⌘-R na komputerze Mac)
  • Google Chrome: Naciśnij Ctrl-Shift-R (⌘-Shift-R na komputerze Mac)
  • Internet Explorer / Edge: Przytrzymaj Ctrl, jednocześnie klikając Odśwież, lub naciśnij klawisze Ctrl+F5
  • Opera: Naciśnij klawisze Ctrl+F5.
/**
 * Analiza i uporządkowanie drzewa DOM u haseł w przestrzeni głównej.
 * 
 * Serwer generuje poniższy kod HTML, przykładowo dla hasła polskiego (wyrywek):
 * 
 *   <h2> <!-- nagłówek sekcji językowej --> </h2>
 *   <dl>
 *     <dt> <!-- nagłówek pola znaczeń --> </dt>
 *   </dl>
 *   <p> <!-- np. ''rzeczownik, rodzaj męski'' --> </p>
 *   <dl>
 *     <dd> <!-- znaczenie (1.1) --> </dd>
 *     <dd> <!-- znaczenie (1.2) --> </dd>
 *   </dl>
 *   <dl> <!-- następne pole --> </dl>
 * 
 * Skrypt skupia wszystkie elementy dotyczące tego samego pola we wspólnym węźle
 * <dl>, przydzielając mu klasę "fldt-<nazwa_pola>". Całą sekcję językową owija
 * w węzeł <section> z klasą wskazującą na kod języka:
 * 
 *   <section class="lang-section lang-pl">
 *     <h2 class="section-heading"> <!-- nagłówek sekcji językowej --> </h2>
 *     <div class="section-intro"> <!-- np. ilustracje --> </div>
 *     <dl class="field fldt-znaczenia">
 *       <dt> <!-- nagłówek pola znaczeń --> </dt>
 *       <dd><p> <!-- np. ''rzeczownik, rodzaj męski'' --> </p></dd>
 *       <dd> <!-- znaczenie (1.1) --> </dd>
 *       <dd> <!-- znaczenie (1.2) --> </dd>
 *     </dl>
 *     <dl class="field fldt-..."> <!-- następne pole --> </dl>
 *   </section>
 * 
 * Grupowanie podwęzłów najczęściej dotyczy pola znaczeń (<p>), tłumaczeń (<ul>)
 * oraz źródeł (<ol> wewnątrz <div>). Zob. też [[Dyskusja szablonu:pole#Gadżet]].
 * 
 * Dodatkowo skrypt ukrywa niewypełnione pola. Aby złagodzić wizualny efekt
 * przewijania ładującej się strony wskutek ukrycia pustych pól, wykorzystano
 * standardowe API bez użycia jQuery. Kod powinien działać zarówno w wersji
 * standardowej, jak i w wersji mobilnej Wikisłownika.
 * 
 * Zmierzony narzut czasowy (FF 74.0, wersja standardowa):
 * 
 * - [[ratunek]] (jedna sekcja językowa): 1-3 ms
 * - [[kot]] (cztery sekcje): 6-8 ms
 * - [[Uganda]] (najdłuższa strona, 79 sekcji): 160-200 ms.
 */

function tryScroll( fragment ) {
	var el,
		fragment = fragment || location.hash;
	
	if ( fragment && ( el = document.querySelector( fragment ) ) ) {
		// przewiń stronę do wskazanej w URL sekcji językowej
		el.scrollIntoView();
		return true;
	}
	
	return false;
}

function process( el, prevEl, append ) {
	var child, dd;
	
	if ( el.tagName === 'DL' ) {
		child = el.firstElementChild;
		
		if ( child.tagName === 'DT' ) {
			if (
				child.firstElementChild &&
				child.firstElementChild.tagName === 'SPAN' &&
				child.firstElementChild.classList.contains( 'field-title' )
			) {
				// pogrubiony nagłówek "znaczenia:"
				// <span class="field-title" data-field="znaczenia"></span>
				el.className = 'field fldt-' + child.firstElementChild.dataset.field;
			}
			
			if (
				child.nextElementSibling !== null &&
				child.nextElementSibling.tagName === 'DD' &&
				child.nextElementSibling.childNodes.length === 0
			) {
				// pusty <dd> w każdym polu: [[Dyskusja szablonu:pole#Dwukropek]]
				el.removeChild( child.nextElementSibling );
			}
			
			if ( child.nextElementSibling === null ) {
				// pole jest puste i należy je ukryć
				el.classList.add( 'empty-field' );
			}
			
			if ( append ) {
				prevEl.parentNode.appendChild( el );
			}
		} else if ( prevEl.tagName === 'DL' ) {
			// lista definicji w polu znaczeń, które element <p> (np.
			// ''rzeczownik, rodzaj męski') oddziela od nagłówka <dt>
			// (pogrubiony napis '''znaczenia:''')
			while ( el.hasChildNodes() ) {
				prevEl.appendChild( el.firstChild );
			}
			
			el.parentNode.removeChild( el );
		} else if ( append ) {
			// potencjalne błędy w wikiskładni
			prevEl.parentNode.appendChild( el );
		}
	} else if ( prevEl.tagName === 'DL' ) {
		// np. <p> (znaczenia), <ul> (tłumaczenia), <div> (źródła)
		dd = document.createElement( 'dd' );
		prevEl.classList.remove( 'empty-field' );
		prevEl.appendChild( dd );
		dd.className = 'field-no-indent';
		dd.appendChild( el );
	} else if ( prevEl.className === 'section-intro' ) {
		// grupowanie elementów pod nagłówkiem, przed pierwszym polem
		prevEl.appendChild( el );
	} else if ( append ) {
		// potencjalne błędy w wikiskładni
		prevEl.parentNode.appendChild( el );
	}
}

if ( mw.config.get( 'wgNamespaceNumber' ) === 0 ) {
	mw.hook( 'wikipage.content' ).add( function ( $content ) {
		var i, j, el, header, intro, nestedChildren,
			topChildren = $content.find( '.mw-parser-output' ).children().get(),
			langSection = null;
		
		for ( i = 0; i < topChildren.length; i++ ) {
			el = topChildren[ i ];
			
			if ( el.tagName === 'H2' ) {
				// nagłówek sekcji językowej
				header = el.getElementsByClassName( 'primary-lang-code' )[ 0 ];
				
				if ( header !== null ) {
					el.classList.add( 'section-heading' );
					
					intro = document.createElement( 'div' );
					intro.className = 'section-intro';
					
					if (
						el.nextElementSibling !== null &&
						el.nextElementSibling.tagName === 'SECTION'
					) {
						// nagłówek w wersji mobilnej
						langSection = el.nextElementSibling;
						langSection.insertBefore( intro, langSection.firstChild );
					} else {
						// nagłówek w wersji standardowej
						langSection = document.createElement( 'section' );
						el.parentNode.insertBefore( langSection, el );
						langSection.appendChild( el );
						langSection.appendChild( intro );
					}
					
					langSection.classList.add( 'lang-section' );
					langSection.classList.add( 'lang-' + header.id );
				}
			} else if ( el.tagName === 'SECTION' && el.hasChildNodes() ) {
				// zwijana sekcja językowa w wersji mobilnej
				nestedChildren = Array.prototype.slice.call( el.children );
				
				for ( j = 1; j < nestedChildren.length; j++ ) {
					process( nestedChildren[ j ], nestedChildren[ j ].previousElementSibling, false );
				}
			} else if ( langSection !== null ) {
				// sekcja językowa w wersji standardowej
				process( el, langSection.lastElementChild, true );
			}
		}
		
		tryScroll();
	} );
}

module.exports = { tryScroll: tryScroll };