/** * AKSRT Scroll Behaviors — back-to-top, floating buttons, reading progress, nav scroll */ (function () { "use strict"; function initBackToTop() { var el = document.getElementById("float-back-to-top") || document.getElementById("back-to-top"); if (!el) return; function onScroll() { if (window.scrollY > 400) { el.classList.add("is-visible"); } else { el.classList.remove("is-visible"); } } window.addEventListener("scroll", onScroll, { passive: true }); onScroll(); el.addEventListener("click", function (e) { e.preventDefault(); if (window.aksrt && aksrt.scrollTopTo) { aksrt.scrollTopTo(0); } else { window.scrollTo({ top: 0, behavior: "smooth" }); } }); } function initFloatingButtons() { var wrap = document.getElementById("float-right-btns"); if (!wrap) return; function debugEnabled() { return !!(window.aksrtData && aksrtData.debug); } function debugLog(message, context, level) { if (!debugEnabled()) return; var method = level === "error" ? "error" : (level === "warn" ? "warn" : "log"); console[method]("[AKSRT FloatingButtons] " + message, context || {}); } function reportDebug(eventName, payload) { if (!(window.aksrtData && aksrtData.debugLog && aksrtData.ajaxUrl && aksrtData.nonce)) { return; } var body = new URLSearchParams(); body.set("action", "aksrt_frontend_debug"); body.set("nonce", aksrtData.nonce); body.set("source", "floating_buttons"); body.set("event", eventName); body.set("page", window.location.href); if (payload && payload.text) body.set("text", String(payload.text)); if (payload && payload.size) body.set("size", String(payload.size)); if (payload && payload.error) body.set("error", String(payload.error)); fetch(aksrtData.ajaxUrl, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" }, credentials: "same-origin", body: body.toString() }).catch(function () {}); } var scrollTimer = null; function setHidden(hidden) { wrap.classList.toggle("is-hidden", !!hidden); } function syncFromScrollBus() { var body = document.body; if (wrap.classList.contains("scroll-down-hide")) { setHidden(body.classList.contains("scroll-down")); } if (wrap.classList.contains("scrolling-hide")) { setHidden(true); window.clearTimeout(scrollTimer); scrollTimer = window.setTimeout(function () { if (!body.classList.contains("scroll-down")) setHidden(false); }, 160); } } window.addEventListener("scroll", function () { requestAnimationFrame(syncFromScrollBus); }, { passive: true }); wrap.addEventListener("click", function (event) { var btn = event.target.closest(".float-btn"); if (!btn) return; if (btn.getAttribute("href") === "#") { event.preventDefault(); } if (btn.hasAttribute("data-action") && btn.getAttribute("data-action") === "toggle-theme") { event.preventDefault(); if (window.aksrt && typeof aksrt.toggleTheme === "function") { aksrt.toggleTheme(); } else { document.documentElement.classList.toggle("dark"); document.body.classList.toggle("dark-theme"); } } btn.classList.remove("is-rippling"); window.requestAnimationFrame(function () { btn.classList.add("is-rippling"); window.setTimeout(function () { btn.classList.remove("is-rippling"); }, 600); }); }); } function initReadingProgress() { var bar = document.getElementById("reading-progress-global"); if (!bar) return; function update() { var doc = document.documentElement; var scrollable = doc.scrollHeight - window.innerHeight; var pct = scrollable <= 0 ? 100 : Math.min(100, Math.max(0, (window.scrollY / scrollable) * 100)); bar.style.width = pct + "%"; bar.setAttribute("aria-valuenow", String(Math.round(pct))); } window.addEventListener("scroll", update, { passive: true }); window.addEventListener("resize", update, { passive: true }); update(); } function initNavScrollState() { // Zibll: hide is CSS on body.scroll-down + header.scroll-down-hide. // Keep is-scrolled on .header for optional consumers. var header = document.querySelector(".header"); var ticking = false; function update() { ticking = false; var body = document.body; if (header) { header.classList.toggle("is-scrolled", body.classList.contains("body-scroll")); } } function onScroll() { if (!ticking) { window.requestAnimationFrame(update); ticking = true; } } window.addEventListener("scroll", onScroll, { passive: true }); window.addEventListener("resize", onScroll, { passive: true }); update(); } window.AksrtFrontend.register("aksrt-floating-buttons", function () { initFloatingButtons(); }); window.AksrtFrontend.register("aksrt-back-to-top", function () { initBackToTop(); }); window.AksrtFrontend.register("aksrt-reading-progress", function () { initReadingProgress(); }); window.AksrtFrontend.register("aksrt-nav-scroll-state", function () { initNavScrollState(); }); })();