(function (global) { "use strict"; const names = {"attendance_daily":"考勤统计","attendance_events":"打卡记录","agent_data":"代理每日数据","agent_retention":"代理留存","yd_members":"YD会员资格名单","yd_bonus":"YD电销赠送彩金","yd_retention":"YD电销留存","yd_messages":"YD电销发送记录","yd_edits":"YD电报编辑记录","yd_assignments":"YD电话分配记录", home: "数据总览", bonus: "彩金统计", tele_bonus: "PK电销赠送彩金", retention: "留存分析", tele_retention: "PK电销留存", tele_sender_stats: "PK电销发送记录", tele_edit_records: "PK电报编辑记录", settings: "系统设置", setup2fa: "安全验证设置", admin_users: "管理员账号", admin_roles: "角色权限", callmon_presence: "坐席状态", callmon_recordings: "录音质检", callmon_agents: "坐席账号", callmon_billing: "话费统计" }; const CALLMON_PAGES = { callmon_presence: "/callmon/admin/presence.html", callmon_recordings: "/callmon/admin/recordings.html", callmon_agents: "/callmon/admin/agents.html", callmon_billing: "/callmon/admin/billing.html" }; function cleanUrl(value, base) { try { const root = new URL(base), url = new URL(value, root); if (url.origin !== root.origin || !["http:", "https:"].includes(url.protocol)) return null; const key = url.searchParams.get("p") || "home"; if (!Object.prototype.hasOwnProperty.call(names, key) || url.searchParams.has("logout")) return null; url.pathname = root.pathname; url.hash = ""; ["_admin_tab_rev", "export"].forEach(k => url.searchParams.delete(k)); url.searchParams.set("p", key); return url; } catch (_) { return null; } } function normalizeTabs(value, base) { const seen = new Set(); return (Array.isArray(value) ? value : []).flatMap(tab => { if (!tab || typeof tab !== "object") return []; const url = cleanUrl(tab.href || "?p=" + tab.key, base); const key = url && url.searchParams.get("p"); if (!key || seen.has(key)) return []; seen.add(key); return [{ key, label: names[key], href: url.pathname + url.search }]; }); } function moveTab(tabs, key, target, after) { const source = tabs.find(tab => tab.key === key); if (!source || key === target) return tabs.slice(); const result = tabs.filter(tab => tab.key !== key); const index = result.findIndex(tab => tab.key === target); result.splice(index < 0 ? result.length : index + (after ? 1 : 0), 0, source); return result; } if (typeof window === "undefined") { module.exports = { cleanUrl, normalizeTabs, moveTab, names }; return; } if (global.__adminTabsV1) return; global.__adminTabsV1 = true; const embedded = window.self !== window.top; if (embedded) document.documentElement.classList.add("admin-tab-frame"); else { const entry = performance.getEntriesByType("navigation")[0]; if (entry && entry.type === "reload") { try { sessionStorage.removeItem("admin-page-tabs"); sessionStorage.setItem("admin-tabs-reset-home", "1"); } catch (_) {} } } const style = document.createElement("style"); style.textContent = ` /* shared card typography and theme scrollbars */ :root{--admin-scroll-track:#f1eff7;--admin-scroll-thumb:#b2a1cc} html[data-theme="dark"]{--admin-scroll-track:#191a23;--admin-scroll-thumb:#65577e} *{scrollbar-width:thin;scrollbar-color:var(--admin-scroll-thumb) var(--admin-scroll-track)} *::-webkit-scrollbar{width:8px;height:8px}*::-webkit-scrollbar-track{background:var(--admin-scroll-track)} *::-webkit-scrollbar-thumb{background:var(--admin-scroll-thumb);border-radius:8px;border:2px solid var(--admin-scroll-track)} *::-webkit-scrollbar-button{display:none;width:0;height:0} .stats .label,.stat .label,.stat-card .label,.gift-kpi-label,.stats .muted:first-child{font-weight:700!important} html.admin-tab-frame .side,html.admin-tab-frame .top,html.admin-tab-frame #page-tabs,html.admin-tab-frame .glow{display:none!important} html.admin-tab-frame,html.admin-tab-frame body,html.admin-tab-frame .app,html.admin-tab-frame .main{min-height:0!important;height:auto!important} html.admin-tab-frame body,html.admin-tab-frame .app{display:block!important;margin:0!important} html.admin-tab-frame .main{margin:0!important;padding:0!important;width:100%!important;max-width:none!important} html.admin-tab-frame main{padding:0!important;min-height:0!important} .admin-tab-view{position:relative;min-width:0}.admin-tab-view[hidden]{display:none!important} .admin-tab-iframe{display:block;width:100%;height:240px;border:0;background:transparent;color-scheme:normal} .admin-tab-loading{position:fixed;top:50%;left:50%;right:auto;transform:translate(-50%,-50%);z-index:50;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;box-sizing:border-box;width:174px;padding:25px 20px;border:1px solid var(--line,#ded9ef);border-radius:16px;background:var(--surface,#fff);color:var(--text,#343044);font:500 13px "Segoe UI","Microsoft YaHei",sans-serif;letter-spacing:.3px;box-shadow:0 12px 36px rgba(79,57,116,.1);pointer-events:none} .admin-tab-loading::before{content:"";display:block;width:30px;height:30px;border:3px solid var(--accent-soft,#eee8f6);border-top-color:var(--accent,#8a74b6);border-right-color:var(--accent,#8a74b6);border-radius:50%;animation:admin-tab-spin .85s linear infinite} html[data-theme=dark] .admin-tab-loading{box-shadow:0 12px 38px rgba(0,0,0,.25)} @keyframes admin-tab-spin{to{transform:rotate(360deg)}} @media(prefers-reduced-motion:reduce){.admin-tab-loading::before{animation:none}} .admin-tab-loading[hidden]{display:none!important} html[data-theme] body.sidebar-collapsed .side .nav-group>a.subnav{display:none!important} html[data-theme] body.sidebar-collapsed .side a.nav::after{display:none!important} .admin-side-flyout{position:fixed;z-index:100510;box-sizing:border-box;width:220px;padding:7px;border:1px solid var(--line,#ded9ef);border-radius:12px;background:var(--surface,#fff);color:var(--text,#302c3b);box-shadow:0 12px 32px rgba(49,34,72,.16);font:13px "Segoe UI","Microsoft YaHei",sans-serif} .admin-side-flyout[hidden]{display:none!important} .admin-side-flyout::before{content:"";position:absolute;top:0;bottom:0;left:-14px;width:14px} .admin-side-flyout-title{padding:6px 10px 9px;font-size:11px;color:var(--muted,#858093);border-bottom:1px solid var(--line,#e9e5ef);margin-bottom:5px} html[data-theme] body .admin-side-flyout a{display:block!important;position:static!important;box-sizing:border-box!important;width:100%!important;margin:0!important;padding:10px 11px!important;border:0!important;border-radius:7px!important;text-decoration:none!important;color:var(--text,#302c3b)!important;font-size:13px!important;box-shadow:none!important;animation:none!important} html[data-theme] body .admin-side-flyout a:hover,html[data-theme] body .admin-side-flyout a.active{background:var(--accent-soft,#eee8f6)!important;color:var(--accent,#8065b1)!important} html[data-theme] #page-tabs .page-tab{cursor:grab;user-select:none} html[data-theme] body #page-tabs .page-tab{position:relative;transition:transform 160ms ease,box-shadow 160ms ease,border-color 160ms ease!important;will-change:auto} @media(hover:hover) and (pointer:fine){ html[data-theme] body #page-tabs .page-tab:not(.is-dragging):hover{transform:translateY(-3px)!important;box-shadow:0 4px 12px rgba(93,70,132,.22)!important;border-color:#b4a2d2!important;z-index:2} html[data-theme=dark] body #page-tabs .page-tab:not(.is-dragging):hover{box-shadow:0 4px 14px rgba(0,0,0,.45)!important;border-color:#9580b7!important} html[data-theme] body #page-tabs .page-tab:not(.is-dragging):active{transform:translateY(-1px)!important} } html[data-theme] body #page-tabs .page-tab.is-dragging{transform:none!important} @media(prefers-reduced-motion:reduce){html[data-theme] body #page-tabs .page-tab{transition:none!important;transform:none!important}} html[data-theme] #page-tabs .page-tab.is-dragging{opacity:.45;cursor:grabbing} html[data-theme] #page-tabs .page-tab.admin-drop-before{box-shadow:inset 3px 0 var(--accent,#9b80f4)!important} html[data-theme] #page-tabs .page-tab.admin-drop-after{box-shadow:inset -3px 0 var(--accent,#9b80f4)!important} .admin-tabs-menu{position:fixed!important;z-index:100500!important;min-width:172px;padding:5px;border:1px solid var(--line,#ded9ef);border-radius:10px;background:var(--surface,#fff)!important;box-shadow:0 8px 28px #00000020} .admin-tabs-menu[hidden]{display:none!important} html[data-theme] body .admin-tabs-menu button:not(.theme-toggle):not(.sidebar-toggle):not(.page-tab-close):not(.date-open){display:block!important;width:100%;min-height:34px;margin:0;padding:8px 11px;border:0;border-radius:6px;background:transparent!important;background-image:none!important;color:var(--text,#272c3a)!important;box-shadow:none!important;font-size:13px;font-weight:400;text-align:left;cursor:pointer} html[data-theme] body .admin-tabs-menu button:not(.theme-toggle):not(.sidebar-toggle):not(.page-tab-close):not(.date-open):hover{background:var(--accent-soft,#eee8f7)!important;color:var(--accent,#7557b6)!important} `; document.head.appendChild(style); function ready(callback) { if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", callback, { once: true }); else callback(); } function indiaToday() { const parts = new Intl.DateTimeFormat("en-CA", { timeZone: "Asia/Kolkata", year: "numeric", month: "2-digit", day: "2-digit" }).formatToParts(new Date()); const date = Object.fromEntries(parts.map(p => [p.type, p.value])); return date.year + "-" + date.month + "-" + date.day; } if (embedded) { ready(function () { let pending = false, previousHeight = 0, previousUrl = ""; function report() { pending = false; const main = document.querySelector("main"), url = cleanUrl(location.href, location.href); let height = main ? main.getBoundingClientRect().bottom + scrollY : document.body.scrollHeight; if (document.querySelector('dialog[open]')) { let viewport = innerHeight; try { viewport = window.top.innerHeight; } catch (_) {} height = Math.max(height, Math.max(420, Math.min(740, viewport - 150))); } document.querySelectorAll(".daterangepicker,.date-popover,.rt-select-menu,.lang-options,.page-size-options").forEach(menu => { if (getComputedStyle(menu).display !== "none" && menu.getClientRects().length) height = Math.max(height, menu.getBoundingClientRect().bottom + scrollY + 12); }); height = Math.max(240, Math.ceil(height + 4)); const href = url ? url.pathname + url.search : ""; if (height !== previousHeight || href !== previousUrl) { previousHeight = height; previousUrl = href; window.parent.postMessage({ type: "admin-tab-state", height, href }, location.origin); } } function schedule() { if (!pending) { pending = true; requestAnimationFrame(report); } } if (global.ResizeObserver) new ResizeObserver(schedule).observe(document.querySelector("main") || document.body); new MutationObserver(schedule).observe(document.body, { childList: true, subtree: true, attributes: true, characterData: true }); addEventListener("load", schedule); addEventListener("resize", schedule); addEventListener("popstate", schedule); addEventListener("message", function (event) { if (event.source !== window.parent || event.origin !== location.origin) return; if (event.data && event.data.type === "admin-tab-theme" && ["light", "dark"].includes(event.data.theme)) { document.documentElement.dataset.theme = event.data.theme; schedule(); } }); if (CALLMON_PAGES[current]) { makeView(current, location.pathname + location.search); views.forEach(function(view, viewKey){ view.element.hidden = viewKey !== current; }); } document.addEventListener("click", function (event) { const link = event.target.closest && event.target.closest("a[href]"); if (!link || event.defaultPrevented || event.ctrlKey || event.metaKey || event.shiftKey || link.target === "_blank" || link.hasAttribute("download")) return; const url = new URL(link.href, location.href); if (url.searchParams.has("export") || !url.searchParams.has("p")) return; const target = cleanUrl(url.href, location.href); if (target && target.searchParams.get("p") !== (new URL(location.href).searchParams.get("p") || "home")) { event.preventDefault(); window.parent.postMessage({ type: "admin-tab-open", href: target.pathname + target.search }, location.origin); } }); schedule(); }); return; } ready(function () { const box = document.getElementById("page-tabs"), main = document.querySelector("main"); if (!box || !main) return; const side=document.querySelector('.side'),sideToggle=document.querySelector('.sidebar-toggle'); const flyout=document.createElement('nav');flyout.className='admin-side-flyout';flyout.hidden=true;flyout.setAttribute('aria-label','侧栏快捷菜单');document.body.appendChild(flyout); let sideHideTimer=0; function hideSideFlyout(){clearTimeout(sideHideTimer);flyout.hidden=true;} function delayHideSideFlyout(){clearTimeout(sideHideTimer);sideHideTimer=setTimeout(hideSideFlyout,160);} function showSideFlyout(item){ if(!document.body.classList.contains('sidebar-collapsed'))return; clearTimeout(sideHideTimer);flyout.replaceChildren(); const title=document.createElement('div');title.className='admin-side-flyout-title';title.textContent=item.textContent.trim();flyout.appendChild(title); const group=item.closest('.nav-group'),links=group?group.querySelectorAll('a.subnav'):[item]; links.forEach(source=>{const link=source.cloneNode(true);link.removeAttribute('style');link.className=source.classList.contains('active')?'active':'';flyout.appendChild(link);}); const rect=item.getBoundingClientRect();flyout.hidden=false; flyout.style.left=Math.max(8,Math.min(innerWidth-228,rect.right+12))+'px'; flyout.style.top=Math.max(8,Math.min(innerHeight-flyout.offsetHeight-8,rect.top))+'px'; } if(side){ side.querySelectorAll('summary.nav,a.nav').forEach(item=>{ item.addEventListener('mouseenter',()=>showSideFlyout(item));item.addEventListener('mouseleave',delayHideSideFlyout); item.addEventListener('focus',()=>showSideFlyout(item)); if(item.matches('summary'))item.addEventListener('click',event=>{if(document.body.classList.contains('sidebar-collapsed')){event.preventDefault();event.stopImmediatePropagation();showSideFlyout(item);}},true); }); } flyout.addEventListener('mouseenter',()=>clearTimeout(sideHideTimer));flyout.addEventListener('mouseleave',delayHideSideFlyout); flyout.addEventListener('focusout',event=>{if(!flyout.contains(event.relatedTarget))delayHideSideFlyout();}); addEventListener('resize',hideSideFlyout);addEventListener('scroll',hideSideFlyout,true); try{const saved=localStorage.getItem('admin-sidebar-collapsed');if(saved!==null)document.body.classList.toggle('sidebar-collapsed',saved==='1');}catch(_){} if(sideToggle){sideToggle.removeAttribute('onclick');sideToggle.addEventListener('click',()=>{ const collapsed=document.body.classList.toggle('sidebar-collapsed');hideSideFlyout(); sideToggle.setAttribute('aria-expanded',String(!collapsed)); try{localStorage.setItem('admin-sidebar-collapsed',collapsed?'1':'0');}catch(_){} });} const allowed = Array.isArray(global.ADMIN_ACCOUNT_CONTEXT?.pages) ? global.ADMIN_ACCOUNT_CONTEXT.pages.slice() : null; if (allowed) ["callmon_presence","callmon_recordings","callmon_agents","callmon_billing"].forEach(function(k){ if (allowed.indexOf(k) < 0) allowed.push(k); }); const landing = allowed && !allowed.includes("home") ? (allowed[0] || "home") : "home"; let resetHome = false; try { resetHome = sessionStorage.getItem("admin-tabs-reset-home") === "1"; } catch (_) {} if (resetHome) { if ((new URL(location.href).searchParams.get("p") || "home") !== landing) { location.replace(location.pathname + "?p=" + landing); return; } try { sessionStorage.removeItem("admin-tabs-reset-home"); sessionStorage.removeItem("admin-page-tabs"); } catch (_) {} } const storageKey = "admin-page-tabs", base = location.href; let current = (new URL(location.href)).searchParams.get("p") || "home"; if (!names[current]) return; let saved = []; try { saved = JSON.parse(sessionStorage.getItem(storageKey) || "[]"); } catch (_) {} const navigation = performance.getEntriesByType("navigation")[0]; if (navigation && navigation.type === "reload") saved = []; let tabs = normalizeTabs(saved, base); if (allowed) tabs = tabs.filter(tab => allowed.includes(tab.key)); if (!tabs.length) tabs.push({ key: landing, label: names[landing], href: location.pathname + "?p=" + landing }); const initialUrl = cleanUrl(location.href, base); const existing = tabs.find(tab => tab.key === current); if (existing) existing.href = initialUrl.pathname + initialUrl.search; else tabs.push({ key: current, label: names[current], href: initialUrl.pathname + initialUrl.search }); const views = new Map(); const initial = document.createElement("div"); initial.className = "admin-tab-view"; while (main.firstChild) initial.appendChild(main.firstChild); main.appendChild(initial); views.set(current, { element: initial, frame: null, href: initialUrl.pathname + initialUrl.search }); const menu = document.createElement("div"); menu.className = "tab-context-menu admin-tabs-menu"; menu.hidden = true; document.body.appendChild(menu); let menuKey = "", dragKey = "", suppressClickUntil = 0; function save() { try { sessionStorage.setItem(storageKey, JSON.stringify(tabs)); } catch (_) {} } function activeSide() { document.querySelectorAll(".side a.nav,.side a.subnav").forEach(link => { const url = cleanUrl(link.href, base), active = url && url.searchParams.get("p") === current; link.classList.toggle("active", Boolean(active)); if (active && !document.body.classList.contains('sidebar-collapsed')) { const group = link.closest("details"); if (group) group.open = true; } }); } function syncTheme() { views.forEach(view => { if (view.frame && view.frame.contentWindow) view.frame.contentWindow.postMessage({ type: "admin-tab-theme", theme: document.documentElement.dataset.theme || "light" }, location.origin); }); } new MutationObserver(syncTheme).observe(document.documentElement, { attributes: true, attributeFilter: ["data-theme"] }); function positionLoading() { const bounds=main.getBoundingClientRect(); const left=Math.max(0,bounds.left),right=Math.min(innerWidth,bounds.right); const top=Math.min(innerHeight,Math.max(0,bounds.top,box.getBoundingClientRect().bottom)); views.forEach(view=>{ if(!view.loading)return; view.loading.hidden=Boolean(view.ready || view.element.hidden); if(view.loading.hidden)return; view.loading.style.left=(left+Math.max(0,right-left)/2)+'px'; view.loading.style.top=(top+(innerHeight-top)/2)+'px'; }); } addEventListener('resize',positionLoading); addEventListener('scroll',positionLoading,true); function makeView(key, href) { const prior = views.get(key); if (prior) {prior.element.remove();if(prior.loading)prior.loading.remove();} const element = document.createElement("div"), frame = document.createElement("iframe"), loading = document.createElement("span"); element.className = "admin-tab-view"; frame.className = "admin-tab-iframe"; frame.title = names[key]; loading.className = "admin-tab-loading"; loading.setAttribute("role", "status"); loading.setAttribute("aria-live", "polite"); loading.textContent = "数据加载中…"; loading.hidden=true;document.body.appendChild(loading); element.append(frame); main.appendChild(element); requestAnimationFrame(positionLoading); const view = { element, frame, href, loading, ready:false }; views.set(key, view); frame.addEventListener("load", function () { view.ready=true; loading.hidden = true; try { const content = frame.contentDocument.querySelector("main") || frame.contentDocument.body; frame.style.height = Math.max(240, Math.ceil(content.getBoundingClientRect().height + 4)) + "px"; } catch (_) {} syncTheme(); }); if (CALLMON_PAGES[key]) { frame.style.minHeight = "calc(100vh - 130px)"; frame.style.height = "calc(100vh - 130px)"; frame.src = CALLMON_PAGES[key] + "?embed=1&_t=" + Date.now(); } else { const target = new URL(href, base); target.searchParams.set("_admin_tab_rev", String(Date.now())); frame.src = target.href; } return view; } function openTab(value, options) { options = options || {}; const url = cleanUrl(value, base); if (!url) return; const key = url.searchParams.get("p"), href = url.pathname + url.search; if (allowed && !allowed.includes(key)) return; let tab = tabs.find(item => item.key === key); if (!tab) { tab = { key, label: names[key], href }; tabs.push(tab); } else { tab.href = href; tab.label = names[key]; } const cached = views.get(key); if (!cached || options.force || cached.href !== href) makeView(key, href); current = key; views.forEach((view, viewKey) => { view.element.hidden = viewKey !== current; }); positionLoading(); menu.hidden = true; save(); render(); activeSide(); document.title = names[key] + " · 管理统计后台"; if (!options.noHistory && location.pathname + location.search !== href) history.pushState({ adminTab: key }, "", href); else if (options.replace) history.replaceState({ adminTab: key }, "", href); try { if (global.jQuery) jQuery(".daterangepicker").hide(); } catch (_) {} } function refreshTab(key) { const tab = tabs.find(item => item.key === key); if (!tab) return; const url = cleanUrl(tab.href, base); if (key === "tele_sender_stats" || key === "tele_edit_records") { const today = indiaToday(); url.searchParams.set("from", today); url.searchParams.set("to", today); url.searchParams.set("page", "1"); url.searchParams.set("from_time", "00:00:00"); url.searchParams.set("to_time", "23:59:59"); if (key === "tele_sender_stats") { url.searchParams.set("date_mode", "auto"); url.searchParams.delete("duplicate_only"); } } else if (key === "tele_retention") { ["from", "to", "page", "date_mode", "include15"].forEach(k => url.searchParams.delete(k)); } openTab(url.href, { force: true, replace: true }); } function edge(key, end) { const tab = tabs.find(item => item.key === key); if (!tab) return; tabs = tabs.filter(item => item.key !== key); if (end) tabs.push(tab); else tabs.unshift(tab); menu.hidden = true; save(); render(); } [["刷新此菜单", () => refreshTab(menuKey)], ["移到最前面", () => edge(menuKey, false)], ["移到最后面", () => edge(menuKey, true)]].forEach(([label, action]) => { const button = document.createElement("button"); button.type = "button"; button.textContent = label; button.addEventListener("click", action); menu.appendChild(button); }); function clearDrop() { box.querySelectorAll(".admin-drop-before,.admin-drop-after").forEach(el => el.classList.remove("admin-drop-before", "admin-drop-after")); } function render() { const scroll = box.scrollLeft; box.replaceChildren(); tabs.forEach(tab => { const wrap = document.createElement("div"), link = document.createElement("a"), close = document.createElement("button"); wrap.className = "page-tab" + (tab.key === current ? " active" : ""); wrap.dataset.key = tab.key; wrap.draggable = true; wrap.tabIndex = 0; wrap.setAttribute("aria-label", tab.label + ",可拖动排序,Alt加左右键移动"); link.className = "page-tab-label"; link.href = tab.href; link.textContent = tab.label; link.draggable = false; link.style.color = "inherit"; link.style.textDecoration = "none"; close.type = "button"; close.className = "page-tab-close"; close.textContent = "×"; close.setAttribute("aria-label", "关闭标签"); close.addEventListener("click", event => { event.stopPropagation(); const active = tab.key === current; tabs = tabs.filter(item => item.key !== tab.key); const view = views.get(tab.key); if (view) { view.element.remove();if(view.loading)view.loading.remove();views.delete(tab.key); } if (active) { const next = tabs[tabs.length - 1]; openTab(next ? next.href : "?p=" + landing, { replace: true }); } else { save(); render(); } }); wrap.addEventListener("contextmenu", event => { event.preventDefault(); event.stopPropagation(); menuKey = tab.key; menu.hidden = false; menu.style.left = Math.max(8, Math.min(event.clientX, innerWidth - 188)) + "px"; menu.style.top = Math.max(8, Math.min(event.clientY, innerHeight - 135)) + "px"; }); wrap.addEventListener("dragstart", event => { if (event.target.closest("button")) { event.preventDefault(); return; } dragKey = tab.key; wrap.classList.add("is-dragging"); menu.hidden = true; event.dataTransfer.effectAllowed = "move"; event.dataTransfer.setData("text/plain", tab.key); }); wrap.addEventListener("dragover", event => { if (!dragKey || dragKey === tab.key) return; event.preventDefault(); clearDrop(); event.dataTransfer.dropEffect = "move"; const after = event.clientX > wrap.getBoundingClientRect().left + wrap.offsetWidth / 2; wrap.classList.add(after ? "admin-drop-after" : "admin-drop-before"); }); wrap.addEventListener("drop", event => { if (!dragKey) return; event.preventDefault(); event.stopPropagation(); const after = event.clientX > wrap.getBoundingClientRect().left + wrap.offsetWidth / 2; tabs = moveTab(tabs, dragKey, tab.key, after); dragKey = ""; suppressClickUntil = Date.now() + 250; save(); render(); }); wrap.addEventListener("dragend", () => { dragKey = ""; suppressClickUntil = Date.now() + 250; clearDrop(); box.querySelectorAll(".is-dragging").forEach(el => el.classList.remove("is-dragging")); }); wrap.addEventListener("keydown", event => { if (!event.altKey || !["ArrowLeft", "ArrowRight"].includes(event.key)) return; event.preventDefault(); const at = tabs.findIndex(item => item.key === tab.key), target = tabs[at + (event.key === "ArrowLeft" ? -1 : 1)]; if (target) { tabs = moveTab(tabs, tab.key, target.key, event.key === "ArrowRight"); save(); render(); box.querySelector('[data-key="' + tab.key + '"]')?.focus(); } }); wrap.append(link, close); box.appendChild(wrap); }); box.scrollLeft = scroll; } if (CALLMON_PAGES[current]) { makeView(current, location.pathname + location.search); views.forEach(function(view, viewKey){ view.element.hidden = viewKey !== current; }); } document.addEventListener("click", function (event) { if (!menu.contains(event.target)) menu.hidden = true; const link = event.target.closest && event.target.closest("a[href]"); if (!link || event.defaultPrevented || event.ctrlKey || event.metaKey || event.shiftKey || link.target === "_blank" || link.hasAttribute("download")) return; const raw = new URL(link.href, location.href); if (raw.searchParams.has("export") || raw.searchParams.has("logout") || !raw.searchParams.has("p")) return; const sidebar = link.matches(".side a.nav,.side a.subnav,.admin-side-flyout a"), label = link.matches("#page-tabs .page-tab-label"), content = link.closest("main"); if (!sidebar && !label && !content) return; if (!cleanUrl(raw.href, base)) return; event.preventDefault(); if (Date.now() < suppressClickUntil) return; if(sidebar)hideSideFlyout(); openTab(raw.href, { force: Boolean(sidebar || content) }); }); document.addEventListener("submit", function (event) { const form = event.target; if (event.defaultPrevented || !(form instanceof HTMLFormElement) || !form.closest("main") || form.method.toLowerCase() !== "get") return; const values = new FormData(form); if (event.submitter && event.submitter.name) values.set(event.submitter.name, event.submitter.value); if (values.has("export")) return; const url = new URL(form.action || location.href, location.href); url.search = ""; values.forEach((value, key) => { if (typeof value === "string") url.searchParams.append(key, value); }); if (!url.searchParams.has("p")) url.searchParams.set("p", current); if (!cleanUrl(url.href, base)) return; event.preventDefault(); openTab(url.href, { force: true }); }); addEventListener("message", function (event) { if (event.origin !== location.origin || !event.data) return; const entry = Array.from(views.entries()).find(([, view]) => view.frame && view.frame.contentWindow === event.source); if (!entry) return; const [key, view] = entry, data = event.data; if (data.type === "admin-tab-open") { openTab(data.href); return; } if (data.type !== "admin-tab-state") return; if (Number.isFinite(data.height) && data.height > 0) view.frame.style.height = Math.min(1000000, Math.max(240, Math.ceil(data.height))) + "px"; view.ready=true;if (view.loading) view.loading.hidden = true; const url = data.href && cleanUrl(data.href, base); if (url && url.searchParams.get("p") === key) { const href = url.pathname + url.search, tab = tabs.find(item => item.key === key); if (view.href !== href || (tab && tab.href !== href)) { view.href = href; if (tab) tab.href = href; save(); render(); } if (key === current && location.pathname + location.search !== href) history.replaceState({ adminTab: key }, "", href); } }); addEventListener("popstate", () => openTab(location.href, { noHistory: true })); document.addEventListener("keydown", event => { if (event.key === "Escape") menu.hidden = true; }); save(); render(); activeSide(); }); })(globalThis); /* === CALLMON_NAV_V2 === */ (function(){if(window.__callmonNavLoader)return;window.__callmonNavLoader=1;var s=document.createElement("script");s.src="/callmon_nav.js?v=20261115c";s.defer=true;document.head.appendChild(s);})();