MediaWiki:Common.js
MediaWiki interface page
More actions
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
( function () {
'use strict';
var ACTIVE_CLASS = 'citizen-toc-list-item--active';
var ACTIVE_TOP_CLASS = 'citizen-toc-level-1--active';
var LINK_SELECTOR = '.citizen-toc-link:not(.citizen-toc-top)';
var NAVIGATION_KEYS = [
'ArrowDown',
'ArrowUp',
'End',
'Home',
'PageDown',
'PageUp',
' '
];
function decodeFragment( hash ) {
if ( !hash || hash.charAt( 0 ) !== '#' ) {
return '';
}
try {
return decodeURIComponent( hash.slice( 1 ) );
} catch ( error ) {
return hash.slice( 1 );
}
}
function setupSingleActiveToc() {
var toc = document.querySelector( '.citizen-toc' );
var indicator = toc && toc.querySelector( '.citizen-toc-indicator' );
if ( !toc || !indicator || toc.dataset.techdocsSingleActiveToc === '1' ) {
return;
}
var entries = Array.prototype.map.call(
toc.querySelectorAll( LINK_SELECTOR ),
function ( link ) {
var target = document.getElementById( decodeFragment( link.hash ) );
var item = link.closest( '.citizen-toc-list-item' );
return target && item ? {
item: item,
link: link,
target: target
} : null;
}
).filter( Boolean );
if ( entries.length === 0 ) {
return;
}
toc.dataset.techdocsSingleActiveToc = '1';
var applying = false;
var frameId = 0;
var manualEntry = null;
function getActivationTop() {
var value = parseFloat(
window.getComputedStyle( document.documentElement ).scrollPaddingTop
);
return Number.isFinite( value ) ? value : 0;
}
function findEntryForHash() {
var id = decodeFragment( window.location.hash );
return entries.find( function ( entry ) {
return entry.target.id === id;
} ) || null;
}
function findEntryForScroll() {
var activationTop = getActivationTop();
var selected = entries[ 0 ];
entries.some( function ( entry ) {
if ( entry.target.getBoundingClientRect().top <= activationTop + 1 ) {
selected = entry;
return false;
}
return true;
} );
return selected;
}
function updateIndicator( entry ) {
var positioningParent = indicator.offsetParent || toc;
var parentRect = positioningParent.getBoundingClientRect();
var linkRect = entry.link.getBoundingClientRect();
var unitHeight = linkRect.height || entry.link.offsetHeight || 32;
var top = linkRect.top - parentRect.top + positioningParent.scrollTop;
indicator.style.setProperty( '--indicator-unit-height', unitHeight + 'px' );
indicator.style.setProperty( '--indicator-top', top + 'px' );
indicator.style.setProperty( '--indicator-scale', '1' );
}
function applyEntry( entry ) {
if ( !entry ) {
return;
}
applying = true;
var topItem = entry.item.closest( '.citizen-toc-level-1' );
entries.forEach( function ( candidate ) {
var isActive = candidate.item === entry.item;
candidate.item.classList.toggle( ACTIVE_CLASS, isActive );
candidate.item.classList.toggle(
ACTIVE_TOP_CLASS,
candidate.item === topItem
);
if ( isActive ) {
candidate.link.setAttribute( 'aria-current', 'location' );
} else {
candidate.link.removeAttribute( 'aria-current' );
}
} );
if ( topItem && !entries.some( function ( candidate ) {
return candidate.item === topItem;
} ) ) {
topItem.classList.add( ACTIVE_TOP_CLASS );
}
updateIndicator( entry );
applying = false;
}
function update() {
frameId = 0;
applyEntry( manualEntry || findEntryForScroll() );
}
function scheduleUpdate() {
if ( frameId === 0 ) {
frameId = window.requestAnimationFrame( update );
}
}
function releaseManualEntry() {
if ( manualEntry ) {
manualEntry = null;
scheduleUpdate();
}
}
toc.addEventListener( 'click', function ( event ) {
if (
event.defaultPrevented ||
event.button !== 0 ||
event.altKey ||
event.ctrlKey ||
event.metaKey ||
event.shiftKey
) {
return;
}
var link = event.target.closest( LINK_SELECTOR );
if ( !link || !toc.contains( link ) ) {
return;
}
var entry = entries.find( function ( candidate ) {
return candidate.link === link;
} );
if ( !entry ) {
return;
}
event.preventDefault();
event.stopImmediatePropagation();
manualEntry = entry;
applyEntry( entry );
var hash = link.getAttribute( 'href' );
if ( hash ) {
window.history.pushState( null, '', hash );
}
window.requestAnimationFrame( function () {
entry.target.scrollIntoView( {
block: 'start'
} );
} );
}, true );
window.addEventListener( 'scroll', scheduleUpdate, { passive: true } );
window.addEventListener( 'resize', scheduleUpdate );
window.addEventListener( 'wheel', releaseManualEntry, { passive: true } );
window.addEventListener( 'touchstart', releaseManualEntry, { passive: true } );
window.addEventListener( 'pointerdown', function ( event ) {
if ( !toc.contains( event.target ) ) {
releaseManualEntry();
}
}, true );
window.addEventListener( 'keydown', function ( event ) {
if ( NAVIGATION_KEYS.indexOf( event.key ) !== -1 ) {
releaseManualEntry();
}
} );
window.addEventListener( 'hashchange', function () {
manualEntry = findEntryForHash();
scheduleUpdate();
} );
var classObserver = new MutationObserver( function () {
if ( !applying ) {
scheduleUpdate();
}
} );
classObserver.observe( toc, {
attributes: true,
attributeFilter: [ 'class' ],
subtree: true
} );
scheduleUpdate();
}
mw.loader.using( 'skins.citizen.scripts' ).then( function () {
window.requestAnimationFrame( setupSingleActiveToc );
} );
}() );