(() => { function initOnboarding(dialog) { const slides = Array.from(dialog.querySelectorAll("[data-onboarding-slide]")); const dots = Array.from(dialog.querySelectorAll("[data-onboarding-dot]")); const progress = dialog.querySelector("[data-onboarding-progress]"); const backButton = dialog.querySelector("[data-onboarding-back]"); const nextButton = dialog.querySelector("[data-onboarding-next]"); const completeUrl = dialog.dataset.completeUrl; const progressTemplate = dialog.dataset.progressTemplate || "Step {current} of {total}"; if (!slides.length || !progress || !backButton || !nextButton || !completeUrl) { return; } let currentIndex = 0; let completionSent = false; const markComplete = () => { if (completionSent) { return; } completionSent = true; window.fetch(completeUrl, { method: "POST", credentials: "same-origin", headers: { "X-Requested-With": "XMLHttpRequest" }, keepalive: true, }).catch(() => { completionSent = false; }); }; const finish = () => { markComplete(); dialog.close(); }; const render = () => { slides.forEach((slide, index) => { slide.hidden = index !== currentIndex; }); dots.forEach((dot, index) => { dot.classList.toggle("is-active", index <= currentIndex); }); progress.textContent = progressTemplate .replace("{current}", String(currentIndex + 1)) .replace("{total}", String(slides.length)); backButton.hidden = currentIndex === 0; const isLast = currentIndex === slides.length - 1; nextButton.textContent = isLast ? nextButton.dataset.finishLabel : nextButton.dataset.nextLabel; }; backButton.addEventListener("click", () => { currentIndex = Math.max(0, currentIndex - 1); render(); }); nextButton.addEventListener("click", () => { if (currentIndex === slides.length - 1) { finish(); return; } currentIndex += 1; render(); }); dialog.addEventListener("cancel", (event) => { event.preventDefault(); }); render(); dialog.showModal(); } document.addEventListener("DOMContentLoaded", () => { const dialog = document.querySelector("[data-onboarding-dialog]"); if (dialog) { initOnboarding(dialog); } }); })();