(function() { "use strict"; (function(global) { const { Component, Util, $ } = global; const UI = {}; Component.register("Modal", (container) => { container.modal = new bootstrap.Modal(container); container.addEventListener("bind", (e) => { e.detail ? container.modal.show() : container.modal.hide(); }); container.addEventListener("hide.bs.modal", () => { var _a; (_a = document.activeElement) == null ? void 0 : _a.blur(); container.dispatchEvent(new CustomEvent("change", { bubbles: false, detail: false })); }); Util.copyFunction(container, container.modal, "show", "hide"); }, Util.makeDom( /*html*/ ` ` )); Component.register("Dialog", Component.getSetupFunction("Modal"), Util.makeDom( /*html*/ ` ` )); let _dialogCount = 0; UI.showDialog = function({ title = "", message = "", buttons = ["{#Close#}"], type = "body" }) { const d = document.body.appendChild(document.createElement("Dialog")); d.style.zIndex = 2e3 + ++_dialogCount; Promise.resolve().then(() => { Object.assign(d.state, { message, title, type, buttons }); d.show(); }); return new Promise((resolve) => { d.addEventListener("change", (e) => { _dialogCount--; resolve(d.result || 0); d.remove(); }); }); }; UI.alert = function(message, options = {}) { return UI.showDialog({ message, ...options }); }; UI.confirm = function(message, options = {}) { return new Promise((resolve) => UI.showDialog({ message, buttons: ["{#Cancel#}", "{#Confirm#}"], ...options }).then((index2) => resolve(index2 >= 2)).catch(() => resolve(false))); }; Component.register("Toast", (container) => { container.toast = new bootstrap.Toast(container, { autohide: container.state.delay > 0 }); Util.copyFunction(container, container.toast, "show", "hide"); container.addEventListener("show.bs.toast", () => { if (container.state.delay > 0) { let timer; const startTimer = () => { container.state.left = container.state.delay / 1e3; timer = setInterval(() => { if (!container.isConnected || --container.state.left <= 0) clearInterval(timer); }, 1e3); }; startTimer(); container.addEventListener("mouseenter", () => { clearInterval(timer); container.state.left = void 0; }); container.addEventListener("mouseleave", startTimer); } }); }, Util.makeDom( /*html*/ `
` ), Util.makeDom( /*html*/ `
` )); UI.toast = function(message, options = {}) { const delay = options.delay ?? 5e3; const t = document.createElement("Toast"); t.state = { delay, left: delay ? delay / 1e3 : void 0, type: options.type || "primary", message, buttons: options.buttons || [] }; $(`[toast-container="${options.container || "default"}"]`).appendChild(t); Promise.resolve().then(() => t.show()); }; UI.toastConfirm = function(message, options = {}) { return new Promise((resolve) => UI.toast(message, { buttons: ["{#Confirm#}"], ...options }).then((index2) => resolve(index2 === 1)).catch(() => resolve(false))); }; global.UI = UI; })(globalThis); (function(global) { const { Component } = global; const HTTP = { get: ({ url, ...opt }) => HTTP.request({ url, method: "GET", ...opt }), post: ({ url, data, ...opt }) => HTTP.request({ url, method: "POST", data, ...opt }), put: ({ url, data, ...opt }) => HTTP.request({ url, method: "PUT", data, ...opt }), delete: ({ url, ...opt }) => HTTP.request({ url, method: "DELETE", ...opt }), head: ({ url, ...opt }) => HTTP.request({ url, method: "HEAD", ...opt }), request: async ({ url, method = "POST", data = void 0, headers = {}, responseType, timeout = 1e4 }) => { method = method.toUpperCase(); const opt = { method, headers: { "Content-Type": "application/json", ...headers } }; if (data !== void 0) opt.body = JSON.stringify(data); const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), timeout); opt.signal = controller.signal; try { const response = await fetch(url, opt); clearTimeout(timer); const result = responseType === "blob" ? await response.blob() : responseType === "text" ? await response.text() : await response.json(); return { ok: response.ok, status: response.status, result }; } catch (err) { clearTimeout(timer); return { ok: false, status: 0, error: err.message }; } } }; Component.register("API", (container) => { if (!container.state.request) container.state.request = { url: "", method: "GET", data: {}, noui: false }; if (!container.state.response) container.state.response = { ok: false, status: 0, result: null, loading: false }; container.do = async (req) => { var _a; const opt = { ...container.state.request, ...req }; container.state.response.loading = true; const resp = await HTTP.request(opt); Object.assign(container.state.response, resp, { loading: false }); if (!resp.ok) { if (!opt.noui && ((_a = global.UI) == null ? void 0 : _a.toast)) global.UI.toast(resp.error || "Request failed", { type: "danger" }); } container.dispatchEvent(new CustomEvent("success", { bubbles: false, detail: resp.result })); return resp; }; let _autoTimer = null; container.state.request.__watch(null, () => { if (!container.hasAttribute("auto") || !container.state.request.url) return; if (_autoTimer) return; _autoTimer = Promise.resolve().then(() => { container.do(); _autoTimer = null; }); }); }); global.HTTP = HTTP; })(globalThis); (function(global) { const { Component, NewState, Util, $ } = global; const AutoForm = { customTypes: [], register: (name, typeName) => { const type = typeName || name; if (!AutoForm.customTypes.includes(type)) AutoForm.customTypes.push(type); } }; Component.register("AutoForm", (container) => { if (!container.state.schema) container.state.schema = []; const ensureProxy = (v) => v && typeof v === "object" && !v.__isProxy ? NewState(v) : v; container.state.__watch("data", (v) => container.data = ensureProxy(v)); container.data = ensureProxy(container.state.data || {}); container.addEventListener("submit", async (event) => { var _a, _b, _c, _d; event.preventDefault(); if (!container.form.reportValidity()) return (_b = (_a = global.UI) == null ? void 0 : _a.toast) == null ? void 0 : _b.call(_a, "{#verify failed#}", { type: "danger" }); container.state.formState = "submitting"; const detail = JSON.parse(JSON.stringify(container.data)); const customEvent = new CustomEvent("submit", { bubbles: false, cancelable: true, detail }); container.dispatchEvent(customEvent); if (customEvent.defaultPrevented) return; try { if (container.state.action) { const resp = await global.HTTP.request({ url: container.state.action, method: "POST", data: detail }); if (!resp.ok) throw new Error(resp.error); } container.state.formState = "success"; if ((_c = global.UI) == null ? void 0 : _c.toast) global.UI.toast("{#submit success#}", { type: "success" }); } catch (err) { container.state.formState = "error"; if ((_d = global.UI) == null ? void 0 : _d.toast) global.UI.toast(err.message, { type: "danger" }); } }); }, Util.makeDom( /*html*/ `
` )); Component.register("TagsInput", (container) => { container._thisObj = container; if (!container.state) container.state = NewState({ tags: [] }); container.addEventListener("bind", (e) => { container.state.tags = Array.isArray(e.detail) ? e.detail : []; }); }, Util.makeDom( /*html*/ `
` ), Util.makeDom( /*html*/ `` )); AutoForm.register("TagsInput"); global.AutoForm = AutoForm; })(globalThis); (function(global) { const { Component, Util } = global; const VirtualScroll = (options = {}) => { const config = { itemHeight: 50, buffer: 5, ...options }; return (container) => { if (!container.state.list) container.state.list = []; if (!container.state._renderedList) container.state._renderedList = []; let _ticking = false; const update = () => { const list = container.state.list || []; const scrollTop = container.scrollTop; const containerHeight = container.clientHeight; const itemHeight = config.itemHeight; const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - config.buffer); const endIndex = Math.min(list.length, Math.ceil((scrollTop + containerHeight) / itemHeight) + config.buffer); const rendered = []; for (let i = startIndex; i < endIndex; i++) { rendered.push({ ...list[i], _top: i * itemHeight, _index: i }); } container.state._renderedList = rendered; const spacerPost = container.querySelector(".dt-spacer-post"); if (spacerPost) { spacerPost.style.height = list.length * itemHeight - endIndex * itemHeight + "px"; spacerPost.style.display = "block"; } const spacerPrev = container.querySelector(".dt-spacer-prev"); if (spacerPrev) { spacerPrev.style.height = startIndex * itemHeight + "px"; spacerPrev.style.display = "block"; } }; container.addEventListener("scroll", () => { if (!_ticking) { window.requestAnimationFrame(() => { update(); _ticking = false; }); _ticking = true; } }); container.state.__watch("list", update); window.addEventListener("resize", update); Promise.resolve().then(update); }; }; Component.register("FastList", (container) => { const itemHeights = /* @__PURE__ */ new Map(); container.state.renderedList = []; Util.newAvg(); const update = () => { const list = container.state.list || []; container.state.groups || []; const scrollTop = container.scrollTop; const viewHeight = container.clientHeight; const itemHeight = container.state.itemHeight || 40; let currentTop = 0; let startIndex = -1; let endIndex = list.length; for (let i = 0; i < list.length; i++) { const h = itemHeights.get(list[i].id) || itemHeight; if (startIndex === -1 && currentTop + h > scrollTop - 200) startIndex = i; if (startIndex !== -1 && currentTop > scrollTop + viewHeight + 200) { endIndex = i; break; } currentTop += h; } if (startIndex === -1) startIndex = 0; container.state.renderedList = list.slice(startIndex, endIndex).map((item, i) => ({ ...item, _index: startIndex + i })); const prevH = list.slice(0, startIndex).reduce((s, item) => s + (itemHeights.get(item.id) || itemHeight), 0); const postH = list.slice(endIndex).reduce((s, item) => s + (itemHeights.get(item.id) || itemHeight), 0); const prev = container.querySelector(".list-spacer-prev"); const post = container.querySelector(".list-spacer-post"); if (prev) prev.style.height = prevH + "px"; if (post) post.style.height = postH + "px"; }; container.addEventListener("scroll", update); container.state.__watch("list", update); Promise.resolve().then(update); }, Util.makeDom( /*html*/ `
` )); global.VirtualScroll = VirtualScroll; })(globalThis); (function(global) { const { Component, Util } = global; const MouseMover = { bind: (handle, target, options = {}) => { let isMoving = false; let startX, startY, startLeft, startTop; const onMouseDown = (e) => { if (options.shouldStart && !options.shouldStart(e)) return; isMoving = true; startX = e.clientX; startY = e.clientY; const rect = target.getBoundingClientRect(); startLeft = rect.left; startTop = rect.top; document.addEventListener("mousemove", onMouseMove); document.addEventListener("mouseup", onMouseUp); if (options.onStart) options.onStart(e); }; const onMouseMove = (e) => { if (!isMoving) return; const dx = e.clientX - startX; const dy = e.clientY - startY; if (options.axis !== "y") target.style.left = startLeft + dx + "px"; if (options.axis !== "x") target.style.top = startTop + dy + "px"; if (options.onMove) options.onStart(e); }; const onMouseUp = (e) => { isMoving = false; document.removeEventListener("mousemove", onMouseMove); document.removeEventListener("mouseup", onMouseUp); if (options.onEnd) options.onEnd(e); }; handle.addEventListener("mousedown", onMouseDown); return () => handle.removeEventListener("mousedown", onMouseDown); } }; Component.register("Resizer", (container) => { container.style.cursor = container.hasAttribute("vertical") ? "col-resize" : "row-resize"; container.addEventListener("mousedown", (e) => { const target = container.parentElement; if (!target) return; const rect = target.getBoundingClientRect(); const startX = e.clientX; const startY = e.clientY; const startW = rect.width; const startH = rect.height; const onMouseMove = (e2) => { const dw = e2.clientX - startX; const dh = e2.clientY - startY; if (container.hasAttribute("vertical")) target.style.width = startW + dw + "px"; else target.style.height = startH + dh + "px"; container.dispatchEvent(new CustomEvent("resize", { bubbles: false, detail: { width: target.offsetWidth, height: target.offsetHeight } })); }; const onMouseUp = () => { document.removeEventListener("mousemove", onMouseMove); document.removeEventListener("mouseup", onMouseUp); }; document.addEventListener("mousemove", onMouseMove); document.addEventListener("mouseup", onMouseUp); }); }, Util.makeDom( /*html*/ `
` )); global.MouseMover = MouseMover; })(globalThis); (function(global) { const { Component, Util } = global; const BOOTSTRAP_ICONS = ["alarm", "archive", "arrow-left", "arrow-right", "bag", "bank", "basket", "bell", "bookmark", "box", "briefcase", "calendar", "camera", "cart", "chat", "check", "chevron-down", "chevron-left", "chevron-right", "chevron-up", "clock", "cloud", "code", "collection", "command", "cpu", "credit-card", "cup", "dash", "database", "display", "door-closed", "download", "droplet", "earbuds", "edit", "egg", "eject", "envelope", "eraser", "eye", "file", "filter", "flag", "folder", "gear", "gem", "gift", "graph-up", "grid", "hammer", "hand-thumbs-up", "heart", "house", "image", "inbox", "info-circle", "journal", "key", "laptop", "layers", "layout-text-sidebar-reverse", "lightbulb", "link", "list", "lock", "map", "mic", "moon", "mouse", "music-note", "newspaper", "palette", "paperclip", "pause", "pencil", "person", "phone", "pie-chart", "play", "plus", "printer", "puzzle", "question-circle", "reception-4", "record", "reply", "rss", "save", "search", "send", "server", "share", "shield", "shop", "shuffle", "skip-end", "skip-start", "slash", "sliders", "smartphone", "speaker", "speedometer", "spellcheck", "square", "star", "stickies", "stop", "stopwatch", "suit-heart", "sun", "table", "tag", "tags", "telephone", "terminal", "text-paragraph", "thermometer", "three-dots", "ticket", "tools", "trash", "trophy", "truck", "tv", "umbrella", "unlock", "upload", "vector-pen", "wallet", "watch", "wifi", "window", "wrench", "x", "zoom-in", "zoom-out", "activity", "at", "award", "backspace", "badge-3d", "badge-4k", "badge-8k", "badge-ad", "badge-ar", "badge-cc", "badge-hd", "badge-tm", "badge-vo", "badge-vr", "badge-wc", "bar-chart", "battery", "bicycle", "binoculars", "blockquote-left", "blockquote-right", "book", "bookshelf", "bootstrap", "border-all", "border-bottom", "border-center", "border-inner", "border-left", "border-middle", "border-outer", "border-right", "border-style", "border-top", "border-width", "bounding-box", "box-arrow-down", "box-arrow-in-down", "box-arrow-in-left", "box-arrow-in-right", "box-arrow-in-up", "box-arrow-left", "box-arrow-right", "box-arrow-up", "box-seam", "brightness-alt-high", "brightness-alt-low", "brightness-high", "brightness-low", "broadcast", "brush", "bucket", "bug", "building", "bullseye", "calculator", "calendar-check", "calendar-date", "calendar-day", "calendar-event", "calendar-minus", "calendar-month", "calendar-plus", "calendar-range", "calendar-week", "calendar-x", "calendar2", "calendar3", "calendar4", "camera-reels", "camera-video", "capslock", "card-checklist", "card-heading", "card-image", "card-list", "card-text", "caret-down", "caret-left", "caret-right", "caret-up", "cart-check", "cart-dash", "cart-plus", "cart-x", "cash", "cash-stack", "cast", "chat-dots", "chat-left", "chat-quote", "chat-right", "chat-square", "chat-text", "check-all", "check-circle", "check-square", "circle", "clipboard", "cloud-arrow-down", "cloud-arrow-up", "cloud-check", "cloud-download", "cloud-fog", "cloud-hail", "cloud-lightning", "cloud-minus", "cloud-moon", "cloud-plus", "cloud-rain", "cloud-slash", "cloud-snow", "cloud-sun", "cloud-upload", "clouds", "cloudy", "code-slash", "code-square", "collection-play", "columns", "columns-gap", "compass", "cone", "cone-striped", "controller", "credit-card-2-back", "credit-card-2-front", "crop", "cup-straw", "cursor", "dash-circle", "dash-square", "diagram-2", "diagram-3", "diamond", "dice-1", "dice-2", "dice-3", "dice-4", "dice-5", "dice-6", "disc", "discord", "distribute-horizontal", "distribute-vertical", "door-open", "dot", "droplet-half", "easel", "egg-fried", "emoji-angry", "emoji-dizzy", "emoji-expressionless", "emoji-frown", "emoji-heart-eyes", "emoji-laughing", "emoji-neutral", "emoji-smile", "emoji-sunglasses", "emoji-wink", "envelope-open", "exclamation", "exclamation-circle", "exclamation-diamond", "exclamation-octagon", "exclamation-square", "exclamation-triangle", "eye-slash", "eyedropper", "facebook", "file-arrow-down", "file-arrow-up", "file-binary", "file-break", "file-check", "file-code", "file-diff", "file-earmark", "file-excel", "file-image", "file-lock", "file-medical", "file-minus", "file-music", "file-pdf", "file-person", "file-play", "file-plus", "file-post", "file-ppt", "file-richtext", "file-slides", "file-spreadsheet", "file-text", "file-word", "file-zip", "files", "film", "filter-circle", "filter-left", "filter-right", "filter-square", "fingerprint", "flower1", "flower2", "flower3", "folder-check", "folder-minus", "folder-plus", "folder-symlink", "folder-x", "folder2-open", "fonts", "forward", "front", "fullscreen", "fullscreen-exit", "funnel", "gear-wide", "gender-female", "gender-male", "gender-trans", "geo", "geo-alt", "github", "globe", "google", "graph-down", "grid-1x2", "grid-3x2", "grid-3x3", "grip-horizontal", "grip-vertical", "hand-index", "hand-thumbs-down", "handbag", "hash", "headphones", "headset", "heart-half", "heptagon", "hourglass", "hourglass-bottom", "hourglass-split", "hourglass-top", "house-door", "hr", "hurricane", "image-alt", "images", "infinity", "input-cursor", "instagram", "intersect", "journal-album", "journal-arrow-down", "journal-arrow-up", "journal-bookmark", "journal-check", "journal-code", "journal-medical", "journal-minus", "journal-plus", "journal-richtext", "journal-text", "journal-x", "journals", "justify", "kanban", "keyboard", "ladder", "lamp", "layers-half", "layout-sidebar", "layout-split", "layout-three-columns", "life-preserver", "lightbulb-off", "lightning", "lightning-charge", "link-45deg", "linkedin", "list-check", "list-nested", "list-ol", "list-stars", "list-task", "list-ul", "mailbox", "markdown", "mask", "mastodon", "megaphone", "menu-app", "menu-button", "messenger", "mic-mute", "minecart", "minecart-loaded", "moisture", "mouse2", "mouse3", "music-note-beamed", "music-note-list", "music-player", "node-minus", "node-plus", "nut", "octagon", "option", "outlet", "paint-bucket", "patch-check", "patch-exclamation", "patch-minus", "patch-plus", "patch-question", "pause-btn", "pause-circle", "peace", "pen", "pencil-square", "pentagon", "person-badge", "person-bounding-box", "person-circle", "person-lines-fill", "phone-landscape", "phone-vibrate", "pie-chart-fill", "pin", "pin-angle", "pin-map", "pip", "play-btn", "play-circle", "plug", "plus-circle", "plus-square", "power", "question", "question-diamond", "question-square", "rainbow", "receipt", "receipt-cutoff", "reception-0", "reception-1", "reception-2", "reception-3", "record-btn", "record-circle", "record2", "recycle", "reddit", "reply-all", "router", "rulers", "safe", "save2", "sd-card", "segmented-nav", "shield-check", "shield-exclamation", "shield-lock", "shield-shaded", "shield-slash", "shift", "signpost", "signpost-2", "signpost-split", "sim", "skip-backward", "skip-forward", "slack", "slash-circle", "slash-square", "smartwatch", "snow", "snow2", "snow3", "sort-alpha-down", "sort-alpha-up", "sort-numeric-down", "sort-numeric-up", "soundwave", "speedometer2", "square-half", "stack", "star-half", "stars", "stop-btn", "stop-circle", "suit-club", "suit-diamond", "suit-spade", "sunglasses", "sunrise", "sunset", "symmetry-horizontal", "symmetry-vertical", "tablet", "tablet-landscape", "telegram", "telephone-forward", "telephone-inbound", "telephone-outbound", "telephone-plus", "telephone-x", "text-center", "text-indent-left", "text-indent-right", "text-left", "text-right", "thermometer-half", "thermometer-high", "thermometer-low", "thermometer-snow", "thermometer-sun", "three-dots-vertical", "toggle-off", "toggle-on", "toggle2-off", "toggle2-on", "tornado", "translate", "trash2", "tree", "truck-flatbed", "tsunami", "type", "type-bold", "type-h1", "type-h2", "type-h3", "type-italic", "type-strikethrough", "type-underline", "ui-checks", "ui-checks-grid", "ui-radios", "ui-radios-grid", "union", "upc", "upc-scan", "view-list", "view-stacked", "vinyl", "voicemail", "volume-down", "volume-mute", "volume-off", "volume-up", "vr", "wallet2", "water", "whatsapp", "wifi-1", "wifi-2", "wifi-off", "wind", "window-dock", "window-sidebar", "x-circle", "x-diamond", "x-octagon", "x-square", "youtube"]; Component.register("Icon", (container) => { container.state.name = container.getAttribute("name"); }, Util.makeDom(``)); Component.register("IconSelector", (container) => { container.state.icons = BOOTSTRAP_ICONS; container.state.search = ""; container.state.selected = ""; container.state.__watch("search", (v) => { const s = v.toLowerCase(); container.state.icons = BOOTSTRAP_ICONS.filter((i) => i.includes(s)); }); container.select = (icon) => { container.state.selected = icon; container.dispatchEvent(new CustomEvent("change", { bubbles: false, detail: icon })); }; }, Util.makeDom( /*html*/ `
` )); global.BOOTSTRAP_ICONS = BOOTSTRAP_ICONS; })(globalThis); (function(global) { const { Component, Hash, Util } = global; Component.register("Nav", (container) => { container.state.items = []; container.state.activeId = Hash.nav; container.state.__watch("activeId", (v) => Hash.nav = v); window.addEventListener("hashchange", () => container.state.activeId = Hash.nav); container.select = (id) => { container.state.activeId = id; container.dispatchEvent(new CustomEvent("change", { bubbles: false, detail: id })); }; }, Util.makeDom( /*html*/ ` ` )); Component.register("Breadcrumb", (container) => { container.state.items = []; }, Util.makeDom( /*html*/ ` ` )); })(globalThis); if (typeof document !== "undefined") { window.addEventListener("beforeunload", (event) => { var _a; if (((_a = globalThis.State) == null ? void 0 : _a.exitBlocks) > 0) { event.preventDefault(); event.returnValue = ""; } }); } })();