2022-10-06 17:45:23 +00:00
|
|
|
const ACTIVE_SECTION_CLASS = 'citizen-toc__listItem--active';
|
2022-05-17 19:10:14 +00:00
|
|
|
|
|
|
|
let /** @type {HTMLElement | undefined} */ activeSection;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {string} id
|
|
|
|
*/
|
|
|
|
function changeActiveSection( id ) {
|
2022-10-06 17:45:23 +00:00
|
|
|
const toc = document.getElementById( 'mw-panel-toc' );
|
2022-05-17 19:10:14 +00:00
|
|
|
|
|
|
|
const getLink = ( hash ) => {
|
|
|
|
const
|
|
|
|
prefix = 'a[href="#',
|
|
|
|
suffix = '"]';
|
|
|
|
|
|
|
|
let el = toc.querySelector( prefix + hash + suffix );
|
|
|
|
|
|
|
|
if ( el === null ) {
|
|
|
|
// Sometimes the href attribute is encoded
|
|
|
|
el = toc.querySelector( prefix + encodeURIComponent( hash ) + suffix );
|
|
|
|
}
|
|
|
|
|
|
|
|
return el;
|
|
|
|
};
|
|
|
|
|
|
|
|
const link = getLink( id );
|
|
|
|
|
|
|
|
if ( activeSection ) {
|
|
|
|
activeSection.classList.remove( ACTIVE_SECTION_CLASS );
|
|
|
|
activeSection = undefined;
|
|
|
|
}
|
|
|
|
|
2023-07-30 23:44:34 +00:00
|
|
|
if ( link ) {
|
|
|
|
activeSection = link.parentNode;
|
|
|
|
activeSection.classList.add( ACTIVE_SECTION_CLASS );
|
|
|
|
}
|
2022-05-17 19:10:14 +00:00
|
|
|
}
|
2022-05-13 04:21:08 +00:00
|
|
|
|
2020-06-17 03:16:45 +00:00
|
|
|
/**
|
2021-04-21 19:07:55 +00:00
|
|
|
* Toggle active HTML class to items in table of content based on user viewport.
|
2022-05-17 19:10:14 +00:00
|
|
|
* Based on Vector
|
2020-07-05 21:07:36 +00:00
|
|
|
*
|
2023-04-30 22:01:53 +00:00
|
|
|
* @param {HTMLElement} bodyContent
|
2021-04-21 19:07:55 +00:00
|
|
|
* @return {void}
|
2020-06-17 03:16:45 +00:00
|
|
|
*/
|
2023-04-30 22:01:53 +00:00
|
|
|
function init( bodyContent ) {
|
|
|
|
if ( !document.getElementById( 'mw-panel-toc' ) ) {
|
|
|
|
return;
|
|
|
|
}
|
2022-05-17 19:35:04 +00:00
|
|
|
|
2023-07-12 02:01:04 +00:00
|
|
|
const getElements = () => {
|
|
|
|
/* T13555 */
|
|
|
|
return bodyContent.querySelectorAll( '.mw-headline' ).length > 0 ? bodyContent.querySelectorAll( '.mw-headline' ) :
|
|
|
|
bodyContent.querySelectorAll( '.mw-heading' );
|
|
|
|
};
|
|
|
|
|
2022-05-17 19:35:04 +00:00
|
|
|
// We use scroll-padding-top to handle scrolling with fixed header
|
|
|
|
// It is better to respect that so it is consistent
|
|
|
|
const getTopMargin = () => {
|
|
|
|
return Number(
|
|
|
|
window.getComputedStyle( document.documentElement )
|
|
|
|
.getPropertyValue( 'scroll-padding-top' )
|
|
|
|
.slice( 0, -2 )
|
|
|
|
) + 20;
|
|
|
|
};
|
2022-05-17 19:10:14 +00:00
|
|
|
|
2023-07-12 02:01:04 +00:00
|
|
|
const headlines = getElements();
|
|
|
|
|
|
|
|
// Do not continue if there are no headlines
|
|
|
|
// TODO: Need to revamp the selector so that it works better with MW 1.40,
|
|
|
|
// currently MW 1.40 has ToC on non-content pages as well
|
|
|
|
if ( !headlines ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-05-17 19:10:14 +00:00
|
|
|
const initSectionObserver = require( './sectionObserver.js' ).init;
|
|
|
|
|
|
|
|
const sectionObserver = initSectionObserver( {
|
2023-07-12 02:01:04 +00:00
|
|
|
elements: headlines,
|
2022-05-17 19:35:04 +00:00
|
|
|
topMargin: getTopMargin(),
|
2022-05-17 19:10:14 +00:00
|
|
|
onIntersection: ( section ) => { changeActiveSection( section.id ); }
|
|
|
|
} );
|
|
|
|
|
|
|
|
// TODO: Pause section observer on ToC link click
|
|
|
|
sectionObserver.resume();
|
2020-02-15 22:55:31 +00:00
|
|
|
}
|
2019-12-30 14:53:22 +00:00
|
|
|
|
2022-05-12 21:18:39 +00:00
|
|
|
module.exports = {
|
2023-04-30 22:01:53 +00:00
|
|
|
init: init
|
2022-05-12 21:18:39 +00:00
|
|
|
};
|