(() => { const root = document.querySelector(".paper-shell"); if (!root) return; const mapElement = document.getElementById("concept-map"); let destinations = []; if (mapElement) { try { destinations = JSON.parse(mapElement.textContent); } catch (_) { document.documentElement.dataset.conceptLinksReady = "0"; } } const termMap = new Map(); for (const destination of destinations) { if (!document.getElementById(destination.target)) continue; for (const term of destination.terms || []) { termMap.set(term.toLocaleLowerCase(), destination); } } const terms = [...termMap.keys()].sort((a, b) => b.length - a.length); if (terms.length > 0) { const escaped = terms.map((term) => term.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") ); const pattern = new RegExp(escaped.join("|"), "gi"); const excluded = "a, code, pre, script, style, #human-review-inventory, #companion-details-panel, .source-anchor, .equation, .postulate, .resolution, .scope-box, .audit-disclosure, .route-details, .appendix-section, [data-concept-definition], .paper-footer, .toc"; const linkedDestinationsByScope = new WeakMap(); const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, { acceptNode(node) { if (!node.nodeValue.trim() || node.parentElement.closest(excluded)) { return NodeFilter.FILTER_REJECT; } return NodeFilter.FILTER_ACCEPT; } }); const textNodes = []; while (walker.nextNode()) textNodes.push(walker.currentNode); for (const node of textNodes) { const text = node.nodeValue; const fragment = document.createDocumentFragment(); const scope = node.parentElement.closest("section, header") || root; const linkedDestinations = linkedDestinationsByScope.get(scope) || new Set(); linkedDestinationsByScope.set(scope, linkedDestinations); let cursor = 0; let changed = false; pattern.lastIndex = 0; for (const match of text.matchAll(pattern)) { const start = match.index; const end = start + match[0].length; const before = start > 0 ? text[start - 1] : ""; const after = end < text.length ? text[end] : ""; if (/[A-Za-z0-9]/.test(before) || /[A-Za-z0-9]/.test(after)) continue; const destination = termMap.get(match[0].toLocaleLowerCase()); if (linkedDestinations.has(destination.target)) continue; fragment.append(document.createTextNode(text.slice(cursor, start))); const link = document.createElement("a"); link.className = "concept-link"; link.href = `#${destination.target}`; link.textContent = match[0]; link.title = destination.label; fragment.append(link); linkedDestinations.add(destination.target); cursor = end; changed = true; } if (changed) { fragment.append(document.createTextNode(text.slice(cursor))); node.replaceWith(fragment); } } } document.documentElement.dataset.conceptLinksReady = String(document.querySelectorAll("a.concept-link").length); const returnButton = document.querySelector(".return-to-reading"); let readingLink = null; if (returnButton) { root.addEventListener("click", (event) => { const link = event.target.closest("a.concept-link"); if (!link) return; readingLink = link; returnButton.hidden = false; }); returnButton.addEventListener("click", () => { if (!readingLink) return; try { history.replaceState(null, "", window.location.href.split("#")[0]); } catch (_) { /* Some file viewers do not permit history replacement. */ } readingLink.focus({ preventScroll: true }); readingLink.scrollIntoView({ block: "center" }); returnButton.hidden = true; }); } const tocLinks = [...document.querySelectorAll('.toc a[href^="#"]')]; const tocTargets = tocLinks .map((link) => ({ link, section: document.querySelector(link.hash) })) .filter(({ section }) => section); const markCurrentSection = (id) => { for (const { link, section } of tocTargets) { if (section.id === id) link.setAttribute("aria-current", "location"); else link.removeAttribute("aria-current"); } }; if ("IntersectionObserver" in window) { const sectionObserver = new IntersectionObserver((entries) => { const current = entries.find((entry) => entry.isIntersecting); if (current) markCurrentSection(current.target.id); }, { rootMargin: "-18% 0px -72%", threshold: 0 }); for (const { section } of tocTargets) sectionObserver.observe(section); } })();