window.WarpBoxAccountUI = (() => { let toastTimer = null; let activeConfirmResolve = null; function initStickyTaskbar(options = {}) { const taskbar = options.taskbar || document.querySelector(".top-taskbar"); if (!taskbar) return; const update = () => { taskbar.classList.toggle("is-scrolled", window.scrollY > 2); }; update(); window.addEventListener("scroll", update, { passive: true }); } function closeMenus(root = document) { root.querySelectorAll(".menu-item.is-open").forEach((item) => { item.classList.remove("is-open"); item.querySelector(".menu-button")?.setAttribute("aria-expanded", "false"); }); } function openMenu(item) { if (!item) return; closeMenus(item.closest(".menu-bar") || document); item.classList.add("is-open"); item.querySelector(".menu-button")?.setAttribute("aria-expanded", "true"); } function initMenus(options = {}) { const root = options.root || document; root.addEventListener("click", (event) => { const button = event.target.closest(".menu-button"); if (button) { const item = button.closest(".menu-item"); const isOpen = item?.classList.contains("is-open"); closeMenus(root); if (!isOpen) openMenu(item); return; } if (!event.target.closest(".menu-item")) { closeMenus(root); } }); root.querySelectorAll(".menu-item").forEach((item) => { item.addEventListener("mouseenter", () => { if (!root.querySelector(".menu-item.is-open")) return; openMenu(item); }); }); document.addEventListener("keydown", (event) => { if (event.key === "Escape") closeMenus(root); }); } function toast(message, type = "info", options = {}) { if (window.WarpBoxUI?.toast && !options.forceAccountToast) { window.WarpBoxUI.toast(message, type, options); return; } const target = options.target || document.querySelector("#account-toast") || document.querySelector("#toast"); if (!target) return; target.textContent = message; target.classList.remove("toast-info", "toast-success", "toast-warning", "toast-error", "is-visible"); target.classList.add(`toast-${type}`, "is-visible"); clearTimeout(toastTimer); toastTimer = setTimeout(() => target.classList.remove("is-visible"), options.duration || 2600); } function modalElements(options = {}) { return { modal: options.modal || document.querySelector("#account-modal"), title: options.title || document.querySelector("#account-modal-title"), body: options.body || document.querySelector("#account-modal-body"), backdrop: options.backdrop || document.querySelector("#account-modal-backdrop") || document.querySelector("#modal-backdrop"), }; } function openModal(titleText, html, options = {}) { const parts = modalElements(options); if (!parts.modal || !parts.title || !parts.body) { if (window.WarpBoxUI?.openPopup) { window.WarpBoxUI.openPopup(titleText, html, options); } return; } parts.title.textContent = titleText; if (options.text) { parts.body.textContent = html; } else { parts.body.innerHTML = html; } parts.modal.classList.add("is-visible"); parts.backdrop?.classList.add("is-visible"); parts.modal.querySelector("[data-modal-close]")?.focus(); } function closeModal(options = {}) { const parts = modalElements(options); parts.modal?.classList.remove("is-visible"); parts.backdrop?.classList.remove("is-visible"); if (window.WarpBoxUI?.closePopup && !parts.modal) { window.WarpBoxUI.closePopup(options); } } function confirm(message, options = {}) { const title = options.title || "Confirm action"; const confirmLabel = options.confirmLabel || "OK"; const cancelLabel = options.cancelLabel || "Cancel"; const html = `

${htmlEscape(message)}

`; const parts = modalElements(options); if (!parts.modal) { return Promise.resolve(window.confirm(message)); } openModal(title, html, options); return new Promise((resolve) => { activeConfirmResolve = resolve; parts.modal.querySelector("[data-confirm-ok]")?.focus(); }); } function finishConfirm(result) { if (activeConfirmResolve) { activeConfirmResolve(result); activeConfirmResolve = null; } closeModal(); } function setDirtyState(isDirty, options = {}) { const target = options.target || document.querySelector("[data-dirty-chip]"); if (!target) return; target.classList.toggle("is-dirty", Boolean(isDirty)); target.textContent = isDirty ? (options.dirtyText || "unsaved changes") : (options.cleanText || ""); } function bindFormDirtyState(form, options = {}) { const targetForm = typeof form === "string" ? document.querySelector(form) : form; if (!targetForm) return; let baseline = new FormData(targetForm); const serialize = () => new URLSearchParams(new FormData(targetForm)).toString(); let baselineValue = new URLSearchParams(baseline).toString(); const update = () => setDirtyState(serialize() !== baselineValue, options); targetForm.addEventListener("input", update); targetForm.addEventListener("change", update); targetForm.addEventListener("submit", () => { baseline = new FormData(targetForm); baselineValue = new URLSearchParams(baseline).toString(); setDirtyState(false, options); }); update(); } function bindConfirmActions(root = document) { root.addEventListener("click", async (event) => { const ok = event.target.closest("[data-confirm-ok]"); if (ok) { finishConfirm(true); return; } const cancel = event.target.closest("[data-confirm-cancel], [data-modal-close]"); if (cancel) { finishConfirm(false); return; } const action = event.target.closest("[data-confirm]"); if (!action) return; if (action.dataset.confirmAccepted === "true") { delete action.dataset.confirmAccepted; return; } const message = action.getAttribute("data-confirm"); if (!message) return; event.preventDefault(); event.stopPropagation(); const accepted = await confirm(message, { title: action.getAttribute("data-confirm-title") || "Confirm action", confirmLabel: action.getAttribute("data-confirm-label") || "OK", cancelLabel: action.getAttribute("data-cancel-label") || "Cancel", }); if (!accepted) return; if (action instanceof HTMLAnchorElement && action.href) { window.location.href = action.href; return; } const form = action.closest("form"); const type = (action.getAttribute("type") || "").toLowerCase(); if (form && (type === "submit" || type === "")) { form.requestSubmit(action); return; } action.dataset.confirmAccepted = "true"; action.click(); }); } function htmlEscape(value) { return String(value || "") .replaceAll("&", "&") .replaceAll("<", "<") .replaceAll(">", ">") .replaceAll('"', """) .replaceAll("'", "'"); } function init(root = document) { initStickyTaskbar(); initMenus({ root }); bindConfirmActions(root); document.querySelector("#account-modal-backdrop")?.addEventListener("click", () => closeModal()); document.addEventListener("keydown", (event) => { if (event.key === "Escape") closeModal(); }); } return { init, initStickyTaskbar, initMenus, toast, confirm, openModal, closeModal, setDirtyState, bindFormDirtyState, closeMenus, }; })(); document.addEventListener("DOMContentLoaded", () => { window.WarpBoxAccountUI.init(); });