Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | 235x 235x 235x 235x 235x 235x 235x 198x 198x 37x 235x 1x 1x 1x 1x 1x 1x 1x 1x 2x 1x 1x 1x 1x 1x 1x | import cheerio from 'cheerio';
import slugify from '@sindresorhus/slugify';
import _ from 'lodash';
import { getVslotShorthandName } from './vueSlotSyntaxProcessor.js';
import type { NodeProcessorConfig } from './NodeProcessor.js';
import { MbNode, NodeOrText } from '../utils/node.js';
/**
* Increment the counter for same heading text, and set the id of the heading node
*
* If the addHeaderCount is false, the counter for the same heading text will not be incremented.
* The heading id will also not have the count appended to it.
*
* So for, only SiteLinkManager uses this function with addHeaderCount set to false.
* This is for validating intralinks in a side-effect free manner.
*
* Heading text refers to textContent of h1-h6 Nodes.
*/
export function setHeadingId(node: MbNode,
config: NodeProcessorConfig,
addHeaderCount: boolean = true) {
const textContent = cheerio(node).text();
// remove the '<' and '>' symbols that markdown-it uses to escape '<' and '>'
const cleanedContent = textContent.replace(/<|>/g, '');
const slugifiedHeading = slugify(cleanedContent, { decamelize: false });
let headerId = slugifiedHeading;
Iif (!addHeaderCount) {
node.attribs.id = headerId;
return;
}
const { headerIdMap } = config;
if (headerIdMap[slugifiedHeading]) {
headerId = `${slugifiedHeading}-${headerIdMap[slugifiedHeading]}`;
headerIdMap[slugifiedHeading] += 1;
} else {
headerIdMap[slugifiedHeading] = 2;
}
node.attribs.id = headerId;
}
/**
* Traverses the dom breadth-first from the specified element to find a html heading child.
* @param node Root element to search from
* @returns The header element, or undefined if none is found.
*/
function _findHeaderElement(node: MbNode): undefined | NodeOrText {
const elements = node.children;
Iif (!elements || !elements.length) {
return undefined;
}
const elementQueue = elements.slice(0);
while (elementQueue.length) {
const nextEl = elementQueue.shift();
if (nextEl?.name && (/^h[1-6]$/).test(nextEl.name)) {
return nextEl;
}
Iif (nextEl?.children) {
nextEl.children.forEach(child => elementQueue.push(child));
}
}
return undefined;
}
/**
* Assigns an id to the root element of a panel component using the heading specified in the
* panel's header slot or attribute (if any), with the header slot having priority if present.
* This is to ensure anchors still work when panels are in their minimized form.
* @param node The root panel element
*/
export function assignPanelId(node: MbNode) {
const slotChildren = node.children
? node.children.filter(child => getVslotShorthandName(child) !== '')
: [];
const headerSlot = slotChildren.find(child => getVslotShorthandName(child) === 'header');
if (headerSlot) {
const header = _findHeaderElement(headerSlot as MbNode);
Iif (!header) {
return;
}
Iif (!header.attribs || !_.has(header.attribs, 'id')) {
throw new Error('Found a panel heading without an assigned id.\n'
+ 'Please report this to the MarkBind developers. Thank you!');
}
node.attribs.panelId = header.attribs.id;
}
}
|