mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-12-28 09:39:00 +01:00
65 lines
1.7 KiB
JavaScript
65 lines
1.7 KiB
JavaScript
|
const { createSlug } = require('./strings.cjs');
|
||
|
|
||
|
/**
|
||
|
* Turns headings into clickable, deep linkable anchors. The provided doc should be a document object provided by JSDOM.
|
||
|
* The same document will be returned with the appropriate DOM manipulations.
|
||
|
*/
|
||
|
module.exports = function (doc, options) {
|
||
|
options = {
|
||
|
levels: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'], // the headings to convert
|
||
|
className: 'anchor-heading', // the class name to add
|
||
|
within: 'body', // the element containing the target headings
|
||
|
...options
|
||
|
};
|
||
|
|
||
|
const within = doc.querySelector(options.within);
|
||
|
|
||
|
if (!within) {
|
||
|
return doc;
|
||
|
}
|
||
|
|
||
|
within.querySelectorAll('h1, h2, h3, h4, h5, h6').forEach(heading => {
|
||
|
const hasAnchor = heading.querySelector('a');
|
||
|
const anchor = doc.createElement('a');
|
||
|
let id = heading.textContent ?? '';
|
||
|
let suffix = 0;
|
||
|
|
||
|
// Skip heading levels we don't care about
|
||
|
if (!options.levels?.includes(heading.tagName.toLowerCase())) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Convert dots to underscores
|
||
|
id = id.replace(/\./g, '_');
|
||
|
|
||
|
// Turn it into a slug
|
||
|
id = createSlug(id);
|
||
|
|
||
|
// Make sure it starts with a letter
|
||
|
if (!/^[a-z]/i.test(id)) {
|
||
|
id = `id_${id}`;
|
||
|
}
|
||
|
|
||
|
// Make sure the id is unique
|
||
|
const originalId = id;
|
||
|
while (doc.getElementById(id) !== null) {
|
||
|
id = `${originalId}-${++suffix}`;
|
||
|
}
|
||
|
|
||
|
if (hasAnchor || !id) return;
|
||
|
|
||
|
heading.setAttribute('id', id);
|
||
|
anchor.setAttribute('href', `#${encodeURIComponent(id)}`);
|
||
|
anchor.setAttribute('aria-label', `Direct link to "${heading.textContent}"`);
|
||
|
|
||
|
if (options.className) {
|
||
|
heading.classList.add(options.className);
|
||
|
}
|
||
|
|
||
|
// Append the anchor
|
||
|
heading.append(anchor);
|
||
|
});
|
||
|
|
||
|
return doc;
|
||
|
};
|