102 lines
2.9 KiB
JavaScript
102 lines
2.9 KiB
JavaScript
function renderTableOfContents() {
|
|
const root = document.querySelector("table-of-contents");
|
|
|
|
if (root === null) {
|
|
console.error("No <table-of-contents> element found, unable to render table of contents!");
|
|
return;
|
|
}
|
|
|
|
const list = document.createElement("ul");
|
|
|
|
const headers = document.querySelectorAll("h2,h3,h4,h5,h6");
|
|
|
|
const ancestors = [];
|
|
for (const header of headers) {
|
|
const depth = parseInt(header.tagName.slice(1)) - 1;
|
|
while (ancestors.length >= depth) {
|
|
ancestors.pop();
|
|
}
|
|
const li = document.createElement("li");
|
|
const a = document.createElement("a");
|
|
if (header.querySelector("code") !== null) {
|
|
const code = document.createElement("code");
|
|
code.textContent = header.textContent;
|
|
a.append(code);
|
|
} else {
|
|
a.textContent = header.textContent;
|
|
}
|
|
a.href = `#${header.id}`;
|
|
const ul = document.createElement("ul");
|
|
li.replaceChildren(a, ul);
|
|
const mostRecent = ancestors[ancestors.length - 1];
|
|
if (mostRecent === undefined) {
|
|
list.appendChild(li);
|
|
} else {
|
|
mostRecent.appendChild(li);
|
|
}
|
|
ancestors.push(ul);
|
|
}
|
|
|
|
const title = document.createElement("h2");
|
|
title.textContent = "Table of contents";
|
|
|
|
root.replaceChildren(title, list);
|
|
}
|
|
|
|
function groupHeadersInDiv(headerTag, index = 0) {
|
|
const elements = document.querySelectorAll(headerTag);
|
|
const firstElement = elements[index];
|
|
if (!firstElement) {
|
|
return;
|
|
}
|
|
let container = document.createElement("div");
|
|
console.log(firstElement);
|
|
firstElement.replaceWith(container);
|
|
container.append(firstElement);
|
|
|
|
while (true) {
|
|
const sib = container.nextSibling;
|
|
if (sib === null) {
|
|
break;
|
|
}
|
|
if (sib.nodeName === headerTag.toUpperCase()) {
|
|
container = document.createElement("div");
|
|
sib.replaceWith(container);
|
|
index++;
|
|
}
|
|
container.append(sib);
|
|
}
|
|
if (index < elements.length) {
|
|
groupHeadersInDiv(headerTag, index + 1);
|
|
}
|
|
}
|
|
|
|
function slugify(elements) {
|
|
return elements
|
|
.map((element, i) => {
|
|
if (i === 0) return null;
|
|
return element.textContent.trim();
|
|
})
|
|
.filter((x) => x !== null)
|
|
.join("-");
|
|
}
|
|
|
|
function attachIdToHeaders() {
|
|
const headers = document.querySelectorAll("h1,h2,h3,h4,h5,h6");
|
|
const ancestors = [];
|
|
for (const header of headers) {
|
|
const depth = parseInt(header.tagName.slice(1));
|
|
while (ancestors.length >= depth) {
|
|
ancestors.pop();
|
|
}
|
|
ancestors.push(header);
|
|
if (depth > 1) header.id = slugify(ancestors);
|
|
}
|
|
}
|
|
|
|
attachIdToHeaders();
|
|
renderTableOfContents();
|
|
for (let i = 2; i <= 6; ++i) {
|
|
groupHeadersInDiv(`h${i}`);
|
|
}
|