mirror of
https://github.com/StarCitizenTools/mediawiki-skins-Citizen.git
synced 2024-11-23 22:13:38 +00:00
fix(core): 🐛 escape double quotes for toc selector
This commit is contained in:
parent
45ee5f897c
commit
ae6d207fb4
|
@ -1,3 +1,3 @@
|
||||||
{
|
{
|
||||||
".": "2.14.1"
|
".": "2.14.1"
|
||||||
}
|
}
|
|
@ -1,13 +1,13 @@
|
||||||
{
|
{
|
||||||
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
|
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
|
||||||
"release-type": "php",
|
"release-type": "php",
|
||||||
"pull-request-title-pattern": "build: release ${version}",
|
"pull-request-title-pattern": "build: release ${version}",
|
||||||
"extra-files": [
|
"extra-files": [
|
||||||
"skin.json",
|
"skin.json",
|
||||||
"package.json",
|
"package.json",
|
||||||
"package-lock.json"
|
"package-lock.json"
|
||||||
],
|
],
|
||||||
"packages": {
|
"packages": {
|
||||||
".": {}
|
".": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,6 +2,30 @@ const ACTIVE_SECTION_CLASS = 'citizen-toc__listItem--active';
|
||||||
|
|
||||||
let /** @type {HTMLElement | undefined} */ activeSection;
|
let /** @type {HTMLElement | undefined} */ activeSection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escapes double quotes in the given HTML attribute ID.
|
||||||
|
*
|
||||||
|
* @param {string} id - The HTML attribute ID to escape double quotes from.
|
||||||
|
* @return {string} The escaped HTML attribute ID with double quotes replaced.
|
||||||
|
*/
|
||||||
|
function escapeHtmlAttributeQuotes( id ) {
|
||||||
|
// Escapes double quotes in the given id
|
||||||
|
return id.replace( /"/g, '\\"' );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds a link element in the table of contents (TOC) based on the provided ID.
|
||||||
|
*
|
||||||
|
* @param {Element} toc - The table of contents element to search within.
|
||||||
|
* @param {string} id - The ID of the section to find the link for.
|
||||||
|
* @return {Element|null} The link element corresponding to the provided ID, or null if not found.
|
||||||
|
*/
|
||||||
|
function findLinkById( toc, id ) {
|
||||||
|
const sanitizedId = escapeHtmlAttributeQuotes( id );
|
||||||
|
const linkElement = toc.querySelector( `a[href="#${ sanitizedId }"]` );
|
||||||
|
return linkElement;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes the active section in the table of contents based on the provided ID.
|
* Changes the active section in the table of contents based on the provided ID.
|
||||||
*
|
*
|
||||||
|
@ -10,12 +34,7 @@ let /** @type {HTMLElement | undefined} */ activeSection;
|
||||||
* @return {void}
|
* @return {void}
|
||||||
*/
|
*/
|
||||||
function changeActiveSection( toc, id ) {
|
function changeActiveSection( toc, id ) {
|
||||||
const getLink = ( hash ) => {
|
const link = findLinkById( toc, id );
|
||||||
const el = toc.querySelector( `a[href="#${ hash }"], a[href="#${ encodeURIComponent( hash ) }"]` );
|
|
||||||
return el;
|
|
||||||
};
|
|
||||||
|
|
||||||
const link = getLink( id );
|
|
||||||
|
|
||||||
if ( activeSection ) {
|
if ( activeSection ) {
|
||||||
activeSection.classList.remove( ACTIVE_SECTION_CLASS );
|
activeSection.classList.remove( ACTIVE_SECTION_CLASS );
|
||||||
|
@ -41,37 +60,26 @@ function init( bodyContent ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getHeadlineElements = () => {
|
const extractIds = () => {
|
||||||
const headlineElements = [];
|
return Array.from( toc.querySelectorAll( '.citizen-toc__listItem' ) )
|
||||||
Array.from( toc.querySelectorAll( '.citizen-toc__listItem' ) ).forEach( ( tocListEl ) => {
|
.map( ( tocListEl ) => tocListEl.id.slice( 4 ) );
|
||||||
// Remove 'toc-' prefix from ID
|
|
||||||
const headlineElement = bodyContent.querySelector( '#' + CSS.escape( tocListEl.id.slice( 4 ) ) );
|
|
||||||
if ( headlineElement ) {
|
|
||||||
headlineElements.push( headlineElement );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
return headlineElements;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// We use scroll-padding-top to handle scrolling with fixed header
|
const queryElements = ( ids ) => {
|
||||||
// It is better to respect that so it is consistent
|
return ids.map( ( id ) => bodyContent.querySelector( '#' + CSS.escape( id ) ) )
|
||||||
|
.filter( ( element ) => element !== null && element !== undefined );
|
||||||
|
};
|
||||||
|
|
||||||
|
const headlines = queryElements( extractIds() );
|
||||||
|
|
||||||
|
const computedStyle = window.getComputedStyle( document.documentElement );
|
||||||
|
const scrollPaddingTop = computedStyle.getPropertyValue( 'scroll-padding-top' );
|
||||||
|
const topMargin = Number( scrollPaddingTop.slice( 0, -2 ) ) + 20;
|
||||||
|
|
||||||
const getTopMargin = () => {
|
const getTopMargin = () => {
|
||||||
const computedStyle = window.getComputedStyle( document.documentElement );
|
return topMargin;
|
||||||
return Number(
|
|
||||||
computedStyle.getPropertyValue( 'scroll-padding-top' )
|
|
||||||
.slice( 0, -2 )
|
|
||||||
) + 20;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const headlines = getHeadlineElements();
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
const initSectionObserver = require( './sectionObserver.js' ).init;
|
const initSectionObserver = require( './sectionObserver.js' ).init;
|
||||||
|
|
||||||
const sectionObserver = initSectionObserver( {
|
const sectionObserver = initSectionObserver( {
|
||||||
|
@ -84,7 +92,6 @@ function init( bodyContent ) {
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
||||||
// TODO: Pause section observer on ToC link click
|
|
||||||
sectionObserver.resume();
|
sectionObserver.resume();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue