Skip to content
(function () {
function initDecorFloatingToc() {
const holder = document.getElementById("decorFloatingToc");
const panel = document.getElementById("decorFloatingTocPanel");
const inner = document.getElementById("decorFloatingTocInner");
const fill = document.getElementById("decorFloatingTocFill");
const thumb = document.getElementById("decorFloatingTocThumb");
const article = document.getElementById("decorArticleMain");
const links = Array.from(document.querySelectorAll(".decor-floating-toc__item"));
if (!holder || !panel || !inner || !fill || !thumb || !article || !links.length) return;
const items = links.map(link => {
const href = link.getAttribute("href");
const target = href ? document.querySelector(href) : null;
return { link, target };
}).filter(item => item.target);
if (!items.length) return;
function syncHolderHeight() {
if (window.innerWidth <= 991) {
holder.style.minHeight = "0px";
return;
}
holder.style.minHeight = Math.max(article.offsetHeight, panel.offsetHeight) + "px";
}
function moveIndicator(link) {
const innerRect = inner.getBoundingClientRect();
const linkRect = link.getBoundingClientRect();
const center = (linkRect.top - innerRect.top) + (linkRect.height / 2);
thumb.style.top = center + "px";
fill.style.height = center + "px";
}
function setActive(item) {
links.forEach(link => link.classList.remove("active"));
item.link.classList.add("active");
moveIndicator(item.link);
}
function getCurrentItem() {
const triggerY = window.innerHeight * 0.42;
let current = items[0];
for (const item of items) {
const rect = item.target.getBoundingClientRect();
if (rect.top <= triggerY) {
current = item;
} else {
break;
}
}
return current;
}
function updateFloatingPosition() {
if (window.innerWidth <= 991) {
panel.style.position = "relative";
panel.style.top = "auto";
panel.style.left = "auto";
panel.style.width = "100%";
return;
}
const holderRect = holder.getBoundingClientRect();
const holderTopDoc = window.scrollY + holderRect.top;
const holderBottomDoc = holderTopDoc + holder.offsetHeight;
const panelHeight = panel.offsetHeight;
const viewportPadding = 28;
const preferredTop = (window.innerHeight - panelHeight) / 2;
const fixedTop = Math.max(
viewportPadding,
Math.min(preferredTop, window.innerHeight - panelHeight - viewportPadding)
);
const desiredTopDoc = window.scrollY + fixedTop;
const maxTopDoc = holderBottomDoc - panelHeight;
if (desiredTopDoc <= holderTopDoc) {
panel.style.position = "absolute";
panel.style.top = "0px";
panel.style.left = "0px";
panel.style.width = "100%";
} else if (desiredTopDoc >= maxTopDoc) {
panel.style.position = "absolute";
panel.style.top = Math.max(0, holder.offsetHeight - panelHeight) + "px";
panel.style.left = "0px";
panel.style.width = "100%";
} else {
panel.style.position = "fixed";
panel.style.top = fixedTop + "px";
panel.style.left = holderRect.left + "px";
panel.style.width = holderRect.width + "px";
}
}
let ticking = false;
function updateAll() {
syncHolderHeight();
updateFloatingPosition();
setActive(getCurrentItem());
ticking = false;
}
function requestUpdate() {
if (!ticking) {
window.requestAnimationFrame(updateAll);
ticking = true;
}
}
if (!holder.dataset.decorInit) {
window.addEventListener("scroll", requestUpdate, { passive: true });
window.addEventListener("resize", requestUpdate);
window.addEventListener("load", requestUpdate);
links.forEach(link => {
link.addEventListener("click", function () {
setTimeout(requestUpdate, 180);
});
});
if (document.fonts && document.fonts.ready) {
document.fonts.ready.then(requestUpdate);
}
holder.dataset.decorInit = "true";
}
requestUpdate();
}
document.addEventListener("DOMContentLoaded", initDecorFloatingToc);
document.body.addEventListener("experimental-flatsome-pjax-request-done", initDecorFloatingToc);
})();