;window.addEventListener('DOMContentLoaded', () => {
const tocEntries = Array.from(document.querySelectorAll('.toc-item'))
.filter(e => e.offsetParent !== null); const activableIds = tocEntries.map(entry => {
const link = entry.querySelector('a');
const href = link.getAttribute('href');
return href.startsWith('#') ? href.substring(1) : null;
}).filter(id => id !== null);
const headings = Array.from(document.querySelectorAll('h1[id],h2[id],h3[id],h4[id]'))
.filter(h => activableIds.includes(h.id));
let activeId = null;
let detection_disabled = false;
const observer = new IntersectionObserver(detectActiveHeading);
document.querySelectorAll('h1,h2,h3,h4,p,pre,table,img').forEach(e => observer.observe(e));
function detectActiveHeading() {
if (detection_disabled) return;
for (const heading of headings) {
const rect = heading.getBoundingClientRect();
if (rect.top > 20 && rect.bottom < window.innerHeight) {
activeId = heading.id;
break;
}
if (rect.bottom > window.innerHeight) {
break;
}
activeId = heading.id;
}
updateActiveTocLink();
}
function updateActiveTocLink() {
tocEntries.forEach(entry => {
const link = entry.querySelector('a');
const href = link.getAttribute('href');
entry.classList.toggle(
'active',
href === `#${activeId}`
);
});
}
document.querySelectorAll('.toc-item a').forEach(link => {
link.addEventListener('click', () => {
let id = link.getAttribute('href').substring(1);
if (id) activeId = id;
detection_disabled = true;
requestAnimationFrame(updateActiveTocLink);
setTimeout(() => { detection_disabled = false; }, 10);
});
});
window.addEventListener('wheel',
(e) => {
if (e.deltaY < 0) { const atTop = window.scrollY <= 1;
if (atTop) {
let idx = headings.findIndex(h => h.id === activeId);
if (idx > 0) {
activeId = headings[idx - 1].id;
updateActiveTocLink();
}
}
} else if (e.deltaY > 0) { const viewportHeight = window.innerHeight;
const docHeight = document.documentElement.scrollHeight;
const atBottom = window.scrollY + viewportHeight >= docHeight - 1;
if (atBottom) {
let idx = headings.findIndex(h => h.id === activeId);
if (idx >= 0 && idx < headings.length - 1) {
activeId = headings[idx + 1].id;
updateActiveTocLink();
}
}
}
},
{ passive: true }
);
detectActiveHeading();
});