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}`);
}