',di="forward";function vo(e){let t=new Set;for(let n of e){let r=n.getAttribute("transition");r&&r!=="true"&&t.add(r)}return t.add(di),[...t]}function fi(){return typeof document<"u"&&typeof document.startViewTransition=="function"&&_.router.viewTransition!==!1}function vn(){for(let e of document.querySelectorAll("[route-view]")){for(let t of[...e.children])He(t);e.innerHTML=""}}function pi(){let e=[],t=new Map,n={path:"",params:{},query:{},hash:""},r=new Set,i=new Map,o=[];function s(x){let h=e.find(w=>w.path===x);if(!h){let w=[],A=x.replace(/:(\w+)/g,(E,P)=>(w.push(P),"([^/]+)")),g=new RegExp("^"+A+"$");h={path:x,outlets:{},regex:g,paramNames:w},e.push(h)}return h}function a(x){let h={};return new URLSearchParams(x).forEach((w,A)=>{h[A]=w}),h}function l(x){for(let h of e){let w=x.match(h.regex);if(w){let A={},g=h.paramNames;for(let E=0;E=0?x.slice(A+1):"",E=A>=0?x.slice(0,A):x,[P,c=""]=E.split("?"),f=P.length>1?P.replace(/\/+$/,""):P;if(f===n.path&&g){if(n.hash="#"+g,_.router.useHash){let j="#"+x;h?window.location.replace(j):window.location.hash=x}else{let j=(_.router.base||"/").replace(/\/$/,"")+x;h?window.history.replaceState({},"",j):window.history.pushState({},"",j)}requestAnimationFrame(()=>{let j=document.getElementById(g);j&&j.scrollIntoView({behavior:"smooth"})}),Rn(),r.forEach(j=>j(n));return}n={path:f,params:{},query:a(c),hash:g?"#"+g:""};let p=l(f);if(p){n.matched=!0,n.params=p.params;let j=p.route.outlets?.default,Y=j?.getAttribute("guard"),W=j?.getAttribute("redirect");if(Y){let _e=K({},null);if(_e.__raw.$store=ae,_e.__raw.$route=n,!R(Y,_e)){W&&ui(W)?await u(W,!0):W?(b(`Route guard redirect blocked \u2014 "${W}" is not a relative path.`),vn()):(b(`Route guard failed for "${x}" but no redirect is defined. The route will not render.`),vn());return}}}else{n.matched=!1;let j=t.get("default");if(j){let Y=j.getAttribute("guard"),W=j.getAttribute("redirect");if(Y){let _e=K({},null);if(_e.__raw.$store=ae,_e.__raw.$route=n,!R(Y,_e)){W&&ui(W)?await u(W,!0):W?(b(`Route guard redirect blocked \u2014 "${W}" is not a relative path.`),vn()):(b(`Route guard failed for "${x}" but no redirect is defined. The route will not render.`),vn());return}}}}let I=f+(c?"?"+c:"")+(g?"#"+g:"");if(_.router.useHash){let j="#"+I;h?window.location.replace(j):window.location.hash=I}else{let j=(_.router.base||"/").replace(/\/$/,"")+I;h?window.history.replaceState({},"",j):window.history.pushState({},"",j)}let N=n,J=document.querySelectorAll("[route-view]"),L=[...J].some(j=>j.getAttribute("transition"));if(fi()&&L){bn();for(let W of J)if(W.getAttribute("transition")){let _e=(W.getAttribute("route-view")||"").trim();W.style.viewTransitionName=_e&&_e!=="default"?"route-content-"+_e:"route-content"}let j=vo(J),Y=document.startViewTransition({update:async()=>{await T(p,N)},types:j});Y.finished.catch(W=>{W.name!=="AbortError"&&b("View transition failed:",W)}),Y.ready?.catch(()=>{}),await Y.updateCallbackDone}else await T(p,N);if(await v(),Rn(),r.forEach(j=>j(n)),Te("route:navigate",{path:n.path,params:n.params,query:n.query,hash:n.hash}),n.hash){let j=n.hash.slice(1);requestAnimationFrame(()=>{let Y=document.getElementById(j);Y&&Y.scrollIntoView({behavior:"smooth"})})}}function d(x,h){if(!document.head)return;let w=K({},null);w.__raw.$route=h,w.__raw.$store=ae;let A=x.getAttribute("page-title");if(A){let c=R(A,w);c!=null&&(document.title=String(c))}let g=x.getAttribute("page-description");if(g){let c=R(g,w);if(c!=null){let f=document.querySelector('meta[name="description"]');f||(f=document.createElement("meta"),f.name="description",document.head.appendChild(f)),f.content=String(c)}}let E=x.getAttribute("page-canonical");if(E){let c=R(E,w);if(c!=null){let f=document.querySelector('link[rel="canonical"]');f||(f=document.createElement("link"),f.rel="canonical",document.head.appendChild(f)),f.href=String(c)}}let P=x.getAttribute("page-jsonld");if(P){let c=document.querySelector('script[type="application/ld+json"][data-nojs]');c||(c=document.createElement("script"),c.type="application/ld+json",c.setAttribute("data-nojs",""),document.head.appendChild(c)),c.textContent=wo(P,w).replace(/<\//g,"<\\/")}}function m(x,h,w,A,g){let E=x+h+w,P=A+":"+E;if(i.has(P))return i.get(P);let c=document.createElement("template");return c.setAttribute("src",E),c.setAttribute("route",g),document.body.appendChild(c),i.set(P,c),c}async function y(x,h){let w=_.router.templates||"";if(!x.hasAttribute("src")&&!w)return null;let g=(x.getAttribute("src")||w).replace(/\/?$/,"/"),E=x.getAttribute("ext")||_.router.ext||".tpl",P=x.getAttribute("route-index")||"index";if(n.path==="/"){let L=P,j=m(g,L,E,h,n.path);return q("[ROUTER] File-based route:",n.path,"\u2192",g+L+E),x.hasAttribute("i18n-ns")&&!j.getAttribute("i18n-ns")&&j.setAttribute("i18n-ns",L),{tpl:j,remainingSegments:[],baseSrc:g,ext:E,indexName:P}}let c=n.path.replace(/^\//,"").split("/").filter(Boolean);if(c.length<=1){let L=c[0]||P,j=m(g,L,E,h,n.path);return q("[ROUTER] File-based route:",n.path,"\u2192",g+L+E),x.hasAttribute("i18n-ns")&&!j.getAttribute("i18n-ns")&&j.setAttribute("i18n-ns",L),{tpl:j,remainingSegments:[],baseSrc:g,ext:E,indexName:P}}let f=c[0],p=h+":layout:"+g+f+E,I;if(i.has(p)){let L=i.get(p);I=L&&!L.__loadFailed}else{let L=m(g,f,E,h,"/"+f);i.set(p,L),L.__srcLoaded||await Me(L),I=!L.__loadFailed}if(I){let L=i.get(p)||m(g,f,E,h,"/"+f);return q("[ROUTER] Hierarchical layout:",n.path,"\u2192 layout",g+f+E,"remaining:",c.slice(1)),x.hasAttribute("i18n-ns")&&!L.getAttribute("i18n-ns")&&L.setAttribute("i18n-ns",f),{tpl:L,remainingSegments:c.slice(1),baseSrc:g,ext:E,indexName:P}}let N=c.join("/"),J=m(g,N,E,h,n.path);return q("[ROUTER] File-based route (flat fallback):",n.path,"\u2192",g+N+E),x.hasAttribute("i18n-ns")&&!J.getAttribute("i18n-ns")&&J.setAttribute("i18n-ns",N),{tpl:J,remainingSegments:[],baseSrc:g,ext:E,indexName:P}}async function v(){let x=document.querySelectorAll("[route-view][route-index]");for(let h of x){if(h.children.length>0)continue;let w=h.getAttribute("route-index");if(!w)continue;let A=(h.getAttribute("route-view")||"").trim()||"default",g=h.getAttribute("ext")||_.router.ext||".tpl",E=h.getAttribute("src");if(E&&E.startsWith("./")){let L=h.parentNode;for(;L;){if(L.__srcBase){E=L.__srcBase+E.slice(2);break}L=L.parentNode}E.startsWith("./")&&(E=E.slice(2))}let P=E?E.replace(/\/?$/,"/"):"",c=m(P,w,g,A,"/"+w);if(c.getAttribute("src")&&!c.__srcLoaded&&await Me(c),c.__loadFailed)continue;c.removeAttribute("route"),q("[ROUTER] Nested index route:",P+w+g),h.hasAttribute("i18n-ns")&&!c.getAttribute("i18n-ns")&&c.setAttribute("i18n-ns",w),He(h),h.innerHTML="";let f=c.getAttribute("i18n-ns");if(f){let{_loadI18nNamespace:L}=await Promise.resolve().then(()=>(st(),rn));await L(f)}let p=c.content.cloneNode(!0),I=K({$route:n},H(h)),N=document.createElement("div");N.style.display="contents",N.__ctx=I,c.content.__srcBase&&(N.__srcBase=c.content.__srcBase),N.appendChild(p),h.appendChild(N),Mt(N);let J=[...N.querySelectorAll("template[src]")];await Promise.all(J.map(Me)),We(N),G(N)}}async function $(x,h,w,A,g,E){if(!h.length)return;let P=[...x.querySelectorAll("[route-view]")];if(!P.length)return;let c=h[0],f=h.slice(1);for(let p of P){let I=(p.getAttribute("route-view")||"").trim()||"default",N=p.getAttribute("src");if(N&&N.startsWith("./")){let Z=p.parentNode;for(;Z;){if(Z.__srcBase){N=Z.__srcBase+N.slice(2);break}Z=Z.parentNode}N.startsWith("./")&&(N=N.slice(2))}let J=N?N.replace(/\/?$/,"/"):w,L=p.getAttribute("ext")||A,j=I+":"+J+c;if(E.has(j)){b("[ROUTER] Infinite loop detected for nested outlet:",j);continue}E.add(j);let Y,W=f;if(f.length>0){let Z=I+":layout:"+J+c+L;if(!i.has(Z)){let Re=m(J,c,L,I,"/"+c);i.set(Z,Re),Re.__srcLoaded||await Me(Re)}let we=i.get(Z);if(we&&!we.__loadFailed)Y=we,q("[ROUTER] Nested layout:",c,"\u2192",J+c+L);else{let Re=[c,...f].join("/");Y=m(J,Re,L,I,n.path),W=[],q("[ROUTER] Nested flat fallback:",c,"\u2192",J+Re+L)}}else Y=m(J,c,L,I,n.path),q("[ROUTER] Nested leaf:",c,"\u2192",J+c+L);if(Y.getAttribute("src")&&!Y.__srcLoaded&&await Me(Y),Y.__loadFailed){let Z=t.get(I)||(I!=="default"?t.get("default"):null);if(Z&&!Z.__loadFailed&&(Y=Z,Y.getAttribute("src")&&!Y.__srcLoaded&&await Me(Y)),!Y||Y.__loadFailed){He(p),p.innerHTML=nr;continue}}He(p),p.innerHTML="";let _e=Y.getAttribute("i18n-ns");if(_e){let{_loadI18nNamespace:Z}=await Promise.resolve().then(()=>(st(),rn));await Z(_e)}let Oe=Y.content.cloneNode(!0),U=K({$route:n},H(p)),be=document.createElement("div");be.style.display="contents",be.__ctx=U,Y.content.__srcBase&&(be.__srcBase=Y.content.__srcBase),be.appendChild(Oe),p.appendChild(be),Mt(be);let O=[...be.querySelectorAll("template[src]")];await Promise.all(O.map(Me)),We(be),G(be),W.length>0&&await $(be,W,J,L,g,E)}}async function T(x,h=n){let w=document.querySelectorAll("[route-view]");for(let g of w){if(!g.isConnected||g.parentElement&&g.parentElement.closest("[route-view]"))continue;let E=g.getAttribute("route-view"),P=E&&E.trim()!==""?E.trim():"default",c=x?.route?.outlets?.[P],f=[],p="",I="",N="index",J=_.router.templates||"";if(!c&&(g.hasAttribute("src")||J)){let L=await y(g,P);L&&(c=L.tpl,f=L.remainingSegments,p=L.baseSrc,I=L.ext,N=L.indexName)}if((!c||c.__loadFailed)&&(!x||c?.__loadFailed)){let L=t.get(P)||(P!=="default"?t.get("default"):null);L&&(c=L)}for(let L of[...g.children])He(L);if(g.innerHTML="",c&&!c.__loadFailed){if(c.getAttribute("src")&&!c.__srcLoaded&&(q("Loading route template on demand:",c.getAttribute("src")),await Me(c)),c.__loadFailed){let U=t.get(P)||(P!=="default"?t.get("default"):null);if(U&&!U.__loadFailed&&(c=U,c.getAttribute("src")&&!c.__srcLoaded&&await Me(c)),!c||c.__loadFailed){g.innerHTML=nr;continue}}let L=c.getAttribute("i18n-ns");if(L){let{_loadI18nNamespace:U}=await Promise.resolve().then(()=>(st(),rn));await U(L)}let j=c.content.cloneNode(!0),Y=K({$route:h},H(g)),W=document.createElement("div");W.style.display="contents",W.__ctx=Y,c.content.__srcBase&&(W.__srcBase=c.content.__srcBase),W.appendChild(j),g.appendChild(W),Mt(W);let _e=[...W.querySelectorAll("template[src]")];q("[ROUTER] nested templates found in wrapper:",_e.length,_e.map(U=>U.getAttribute("src")+(U.__srcLoaded?"[LOADED]":"[NEW]"))),await Promise.all(_e.map(Me)),q("[ROUTER] all nested loads done for route:",h.path);let Oe=g.getAttribute("transition");if(Oe&&!fi()&&(Ct(W,null,Oe),b("Class-based route transitions are deprecated. The View Transition API is now used by default. Set router.viewTransition to false to keep legacy behavior.")),We(W),G(W),f.length>0&&await $(W,f,p,I,N,new Set),P==="default"){let U=c.getAttribute("page-title");if(U){let be=K({},null);be.__raw.$route=h,be.__raw.$store=ae;let O=R(U,be);O!=null&&(document.title=String(O))}}P==="default"&&d(c,h),P==="default"&&_.router.focusBehavior==="auto"&&requestAnimationFrame(()=>{let U=g.querySelector("[autofocus]")||g.querySelector('[tabindex="-1"]')||g.querySelector("h1")||g;U.hasAttribute("tabindex")||U.setAttribute("tabindex","-1"),U.focus({preventScroll:!0})})}else(!x||c?.__loadFailed)&&(g.innerHTML=nr)}document.querySelectorAll("[route]").forEach(g=>{let E=g.getAttribute("route"),P=g.getAttribute("route-active")||"active",c=g.getAttribute("route-active-exact");if(c)c.split(/\s+/).filter(Boolean).forEach(f=>g.classList.toggle(f,h.path===E));else if(P&&!g.hasAttribute("route-active-exact")){let f=h.path,p=E==="/"?f==="/":f===E||f.startsWith(E.replace(/\/$/,"")+"/");P.split(/\s+/).filter(Boolean).forEach(I=>g.classList.toggle(I,p))}});let A=_.router.scrollBehavior;A==="top"?window.scrollTo(0,0):A==="smooth"&&window.scrollTo({top:0,behavior:"smooth"})}function C(x,h){h.scrollIntoView({behavior:"smooth"});let w='a[href="#'+x+'"]';document.querySelectorAll('a[href^="#"]').forEach(A=>{A.hasAttribute("route")||A.classList.toggle("active",A.matches(w))})}function z(){let x=document.querySelectorAll("[route-view]");for(let h of x){if(h.parentElement&&h.parentElement.closest("[route-view]"))continue;let w=h.getAttribute("src")||_.router.templates||"";if(!w)continue;let A=w.replace(/\/?$/,"/"),g=h.getAttribute("ext")||_.router.ext||".tpl",E=h.getAttribute("route-index")||"index",P=(h.getAttribute("route-view")||"").trim()||"default",c=new Map;document.querySelectorAll("[route]:not([route-view])").forEach(I=>{let N=I.getAttribute("route");if(!N)return;let J=N.split("?")[0].split("#")[0],L=I.getAttribute("lazy"),j=c.get(J);(!c.has(J)||L==="priority"||j==="ondemand"&&L!=="ondemand")&&c.set(J,L)});let f=[],p=[];for(let[I,N]of c){if(N==="ondemand"||I===n.path||I==="*")continue;let J=I==="/"?E:I.replace(/^\//,""),L=I==="/"?[]:I.replace(/^\//,"").split("/").filter(Boolean);if(L.length>1){let _e=L[0],Oe=P+":layout:"+A+_e+g,U=i.get(Oe);if(U&&!U.__loadFailed){let be=L.join("/"),O=m(A,be,g,P,I);O.__srcLoaded||(q("[ROUTER] Prefetch (layout child):",I,"\u2192",A+be+g,N==="priority"?"(priority)":"(background)"),h.hasAttribute("i18n-ns")&&O.setAttribute("i18n-ns",be),N==="priority"?f.push(O):p.push(O));continue}}let j=A+J+g,Y=P+":"+j;if(i.has(Y))continue;let W=m(A,J,g,P,I);q("[ROUTER] Prefetch:",I,"\u2192",j,N==="priority"?"(priority)":"(background)"),h.hasAttribute("i18n-ns")&&W.setAttribute("i18n-ns",J),N==="priority"?f.push(W):p.push(W)}(f.length||p.length)&&Promise.all(f.map(Me)).then(()=>{p.forEach(Me)})}}return{get current(){return n},push(x){return u(x)},replace(x){return u(x,!0)},back(){window.history.back()},forward(){window.history.forward()},on(x){return r.add(x),()=>r.delete(x)},register(x,h,w="default"){if(x==="*"){t.set(w,h);return}let A=s(x);A.outlets[w]=h},async init(){_.router.useHash&&!_.router.suppressHashWarning&&b("Router is running in hash mode (useHash: true). URLs like /#/about are not indexed as separate pages by search engines. Use useHash: false with a server-side SPA fallback (try_files) for SEO-friendly routing. See: https://github.com/no-js-dev/nojs/blob/main/docs/md/routing.md#deployment"),document.querySelectorAll("template[route]").forEach(h=>{let w=h.getAttribute("route"),A=h.getAttribute("outlet")||"default";if(w==="*"){t.set(A,h);return}let g=s(w);g.outlets[A]=h});let x=h=>{let w=h.target.closest("[route]");if(w&&!w.hasAttribute("route-view")){h.preventDefault();let g=w.getAttribute("route");u(g);return}let A=h.target.closest('a[href^="#"]');if(A&&!A.hasAttribute("route")){let E=A.getAttribute("href").slice(1);if(E&&!E.startsWith("/")){let P=document.getElementById(E);P&&(h.preventDefault(),C(E,P),_.router.useHash||window.history.replaceState(null,"","#"+E))}}};if(document.addEventListener("click",x),o.push(()=>document.removeEventListener("click",x)),_.router.useHash){let h=()=>{let A=window.location.hash.slice(1)||"/";if(!A.startsWith("/")){let E=document.getElementById(A);E&&(C(A,E),window.history.replaceState(null,"","#"+n.path));return}let[g]=A.split("?");g!==n.path&&u(A,!0,!0)};window.addEventListener("hashchange",h),o.push(()=>window.removeEventListener("hashchange",h));let w=window.location.hash.slice(1)||"/";await u(w,!0)}else{let h=()=>{let A=Ht(window.location.pathname);if(A===n.path){let g=window.location.hash.slice(1);if(g){let E=document.getElementById(g);E&&C(g,E)}return}u(A,!0,!0)};window.addEventListener("popstate",h),o.push(()=>window.removeEventListener("popstate",h));let w=Ht(window.location.pathname);await u(w,!0)}z()},destroy(){o.forEach(x=>x()),o.length=0,r.clear()}}}ge();te.uppercase=e=>String(e??"").toUpperCase();te.lowercase=e=>String(e??"").toLowerCase();te.capitalize=e=>String(e??"").replace(/\b\w/g,t=>t.toUpperCase());te.truncate=(e,t=100)=>{let n=String(e??"");return n.length>t?n.slice(0,t)+"...":n};te.trim=e=>String(e??"").trim();te.stripHtml=e=>String(e??"").replace(/<[^>]*>/g,"");te.slugify=e=>String(e??"").toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"");te.nl2br=e=>String(e??"").replace(/&/g,"&").replace(//g,">").replace(/\n/g," ");te.encodeUri=e=>encodeURIComponent(String(e??""));var mi=(e,t=0)=>{let n=Math.floor(Number(e));return isNaN(n)?t:n<0?0:n>100?100:n};te.number=(e,t=0)=>{let n=Number(e);if(isNaN(n))return e;let r=mi(t);return n.toLocaleString(void 0,{minimumFractionDigits:r,maximumFractionDigits:r})};te.currency=(e,t="USD")=>{let n=Number(e);if(isNaN(n))return e;try{return n.toLocaleString(void 0,{style:"currency",currency:t})}catch{return`${t} ${n.toFixed(2)}`}};te.percent=(e,t=0)=>{let n=Number(e);return isNaN(n)?e:(n*100).toFixed(mi(t))+"%"};te.filesize=e=>{let t=Number(e);if(isNaN(t))return e;let n=["B","KB","MB","GB","TB"],r=t<0?"-":"",i=0,o=Math.abs(t);for(;o>=1024&&i0?1:0)+" "+n[i]};te.ordinal=e=>{let t=Number(e);if(isNaN(t))return e;let n=["th","st","nd","rd"],r=t%100;return t+(n[(r-20)%10]||n[r]||n[0])};te.count=e=>Array.isArray(e)?e.length:0;te.first=e=>Array.isArray(e)?e[0]:e;te.last=e=>Array.isArray(e)?e[e.length-1]:e;te.join=(e,t=", ")=>Array.isArray(e)?e.join(t):e;te.reverse=e=>Array.isArray(e)?[...e].reverse():e;te.unique=e=>Array.isArray(e)?[...new Set(e)]:e;te.pluck=(e,t)=>Array.isArray(e)?e.map(n=>n?.[t]):e;te.sortBy=(e,t)=>{if(!Array.isArray(e))return e;let n=t?.startsWith("-"),r=n?t.slice(1):t,i=o=>o==null||typeof o=="number"&&isNaN(o);return[...e].sort((o,s)=>{let a=o?.[r],l=s?.[r],u=i(a),d=i(l);if(u&&d)return 0;if(u)return 1;if(d)return-1;let m=al?1:0;return n?-m:m})};te.where=(e,t,n)=>Array.isArray(e)?e.filter(r=>r?.[t]===n):e;var Eo=/^\d{4}-\d{2}-\d{2}$/,En=e=>typeof e=="string"&&Eo.test(e)?new Date(e+"T00:00:00"):new Date(e);te.date=(e,t="short")=>{let n=En(e);if(isNaN(n))return e;let r=t==="long"?{dateStyle:"long"}:t==="full"?{dateStyle:"full"}:{dateStyle:"short"};return n.toLocaleDateString(void 0,r)};te.datetime=e=>{let t=En(e);return isNaN(t)?e:t.toLocaleString()};te.relative=e=>{let t=En(e);if(isNaN(t))return e;let n=(Date.now()-t.getTime())/1e3;return n<0?te.fromNow(e):n<60?"just now":n<3600?Math.floor(n/60)+"m ago":n<86400?Math.floor(n/3600)+"h ago":n<2592e3?Math.floor(n/86400)+"d ago":t.toLocaleDateString()};te.fromNow=e=>{let t=En(e);if(isNaN(t))return e;let n=(t.getTime()-Date.now())/1e3;return n<0?te.relative(e):n<60?"in a moment":n<3600?"in "+Math.floor(n/60)+"m":n<86400?"in "+Math.floor(n/3600)+"h":"in "+Math.floor(n/86400)+"d"};te.default=(e,t="")=>e==null||e===""?t:e;te.json=(e,t=2)=>JSON.stringify(e,null,t);te.debug=e=>(console.log("[No.JS debug]",e),e);te.keys=e=>e&&typeof e=="object"?Object.keys(e):[];te.values=e=>e&&typeof e=="object"?Object.values(e):[];ge();ge();var hi=["foreach","each","for","from","filter","sort","limit","offset","key","index","template","animate-enter","animate-leave","animate-stagger","animate-duration","animate","get","post","put","patch","delete","query","sse","page-title","page-description","page-canonical","page-jsonld","else-if"];function So(e,t){for(let n=0;n>1;e[t[u]]0&&(n[o]=t[a-1]),t[a]=o}let r=new Set,i=t.length?t[t.length-1]:-1;for(;i!==-1;)r.add(i),i=n[i];return r}function rr(e,t,n,r){t&&(e.classList.add(t),e.addEventListener("animationend",()=>e.classList.remove(t),{once:!0}),n&&(e.style.animationDelay=r*n+"ms"))}var ir={priority:10,init(e,t,n){let r=H(e),i,o,s=n.match(/^(\w+)\s+in\s+(\S+)$/);if(s)[,i,o]=s;else{let O=e.getAttribute("from");if(!O||!/^\w+$/.test(n))return;i=n,o=O,console.warn(`[NoJS] "${t}" with "from" is deprecated. Use ${t}="${i} in ${o}" instead.`)}let a=e.getAttribute("index")||"$index",l=e.hasAttribute("if")||e.hasAttribute("else-if"),u=l?null:e.getAttribute("else"),d=e.getAttribute("filter"),m=e.getAttribute("sort"),y=parseInt(e.getAttribute("limit"))||1/0,v=parseInt(e.getAttribute("offset"))||0,$=e.getAttribute("template"),T=e.getAttribute("key"),C=e.getAttribute("animate-enter")||e.getAttribute("animate"),z=e.getAttribute("animate-leave"),re=parseInt(e.getAttribute("animate-stagger"))||0,x=parseInt(e.getAttribute("animate-duration"))||0,h=null,w=null,A=!1,g=!1,E=!!(d||m||v),P=e.parentNode,c=document.createComment("foreach"),f=document.createComment("/foreach");P.insertBefore(c,e),P.insertBefore(f,e.nextSibling),P.removeChild(e);let p=new Map,I=null,N=null,J=0,L=-1;function j(){if(I&&(N.registryVersion!==Gt&&(N=tr(I)),!$||L===J))return;L=J;let O=$?document.getElementById($):null;I=e.cloneNode(!0),So(I,!l),O&&(I.innerHTML="",I.appendChild(O.content.cloneNode(!0))),N=tr(I)}function Y(O){j();let Z=I.cloneNode(!0);return Z.__ctx=K(O,r),Z}function W(O){si(O,N)}function _e(){J++;let O=/[\[\]()\s+\-*\/!?:&|]/.test(o)?R(o,r):jt(o,r);Array.isArray(O)||(O=[]);let Z=Sn(c,f);if(!A&&O===h&&O.length>0&&Z.length>0){for(let X of Z)X.__ctx&&X.__ctx.$notify&&X.__ctx.$notify();return}if(h=O,d){let X={[i]:void 0,[a]:0},ie=K(X,r),Ae=ie.__raw;O=O.filter((Q,k)=>(Ae[i]=Q,Ae[a]=k,delete Ae.__collectKeysCache,!!R(d,ie)))}if(m){let X=m.startsWith("-"),ie=X?m.slice(1):m;O=[...O].sort((Ae,Q)=>{let k=jt(ie,Ae)??Ae?.[ie],Pe=jt(ie,Q)??Q?.[ie],V=k==null||typeof k=="number"&&Number.isNaN(k),B=Pe==null||typeof Pe=="number"&&Number.isNaN(Pe);if(V||B)return V&&B?0:V?1:-1;let le=kPe?1:0;return X?-le:le})}if(O=O.slice(v,v+y),O.length===0&&u){if(A){w=null;return}Xt(c,f),p.clear();let X=ye(u);if(X){P.insertBefore(X,f);let ie=c.nextSibling;for(;ie&&ie!==f;)ie.nodeType===1&&G(ie),ie=ie.nextSibling;A=!0}else g||(g=!0,b(`${t}: else template "${u}" not found`,e));w=null;return}if(O.length===0){Xt(c,f),p.clear(),A=!1,w=null;return}if(A=!1,T){U(O,O.length),w=null;return}function we(){if(E||!w)return!1;let X=w.length,ie=O.length;if(ie<=X)return!1;let Ae=Sn(c,f);if(Ae.length!==X)return!1;for(let V=0;V0){let X=Ne.length;Ne.forEach(ie=>{ie.classList.add(z);let Ae=()=>{ie.classList.remove(z),X--,X<=0&&Re()};ie.addEventListener("animationend",Ae,{once:!0}),setTimeout(Ae,x||0)})}else we()||Re()}let Oe=null;if(T){let O=T.match(/^(\w+)\.(\w+)$/);O&&O[1]===i&&!["__proto__","constructor","prototype"].includes(O[2])&&(Oe=O[2])}function U(O,Z){p.size===0&&Xt(c,f);let we;if(Oe){we=new Array(O.length);for(let B=0;B(ve[i]=tt,ve[a]=xe,delete ve.__collectKeysCache,{key:R(T,le),item:tt,i:xe}))}let Re=new Set(we.map(B=>B.key)),Ne=!1;for(let B of p.keys())if(Re.has(B)){Ne=!0;break}if(!Ne&&p.size>0)Xt(c,f),p.clear();else for(let[B,le]of p)Re.has(B)||(He(le,!0),le.remove(),p.delete(B));let X=null,ie=null;if(we.forEach(({key:B,item:le,i:ve})=>{let tt=p.get(B);if(tt){let xe=tt.__ctx.__raw;(xe[i]!==le||xe[a]!==ve||xe.$count!==Z)&&(xe[i]=le,xe[a]=ve,xe.$index=ve,xe.$count=Z,xe.$first=ve===0,xe.$last=ve===Z-1,xe.$even=ve%2===0,xe.$odd=ve%2!==0,delete xe.__collectKeysCache),tt.__ctx.$notify()}else{let xe=Y({[i]:le,[a]:ve,$index:ve,$count:Z,$first:ve===0,$last:ve===Z-1,$even:ve%2===0,$odd:ve%2!==0});p.set(B,xe),X||(X=document.createDocumentFragment(),ie=[]),X.appendChild(xe),ie.push({clone:xe,i:ve})}}),X){P.insertBefore(X,f);for(let B=0;B=0;B--){let le=p.get(we[B].key);!Pe.has(B)&&le.nextSibling!==V&&P.insertBefore(le,V),V=le}}let be=fe;lt(P),de(o,r,_e),_e(),lt(be)}};F("foreach",ir);F("each",ir);F("for",ir);F("state",{priority:0,init(e,t,n){let r=R(n,K())||{},o=e.__ctx||(e.parentElement?H(e.parentElement):null),s=K(r,o);e.__ctx=s;let a=e.getAttribute("persist"),l=e.getAttribute("persist-key");if(a&&!l){b(`persist="${a}" requires a persist-key attribute. State will not be persisted.`);return}if(a&&l){let u=a==="localStorage"?localStorage:a==="sessionStorage"?sessionStorage:null;if(u){let d=e.getAttribute("persist-fields"),m=d?new Set(d.split(",").map(z=>z.trim())):null,y="nojs_"+(_.appId||"")+"state_"+l;try{let z=u.getItem(y);if(z){let re=JSON.parse(z),x=e.hasAttribute("persist-schema"),h=new Set(["__proto__","constructor","prototype"]);for(let[w,A]of Object.entries(re))if(!h.has(w)&&!w.startsWith("__")&&(!m||m.has(w))){if(x){if(!(w in r)){b('persist-schema: ignoring unknown key "'+w+'"');continue}if(r[w]!==null&&A!==null&&typeof A!=typeof r[w]){b('persist-schema: type mismatch for "'+w+'" (expected '+typeof r[w]+", got "+typeof A+")");continue}}s.$set(w,A)}}}catch{}let v=["token","password","secret","key","auth","credential","session"],T=Object.keys(r).filter(z=>v.some(re=>z.toLowerCase().includes(re)));T.length>0&&b("State key(s) "+T.map(z=>'"'+z+'"').join(", ")+" may contain sensitive data. Consider using persist-fields to exclude them.");let C=s.$watch(()=>{try{let z=s.__raw,re=Object.fromEntries(Object.entries(z).filter(([x])=>!x.startsWith("__")&&(!m||m.has(x))));u.setItem(y,JSON.stringify(re))}catch{}});D(()=>{C&&C()})}}q("state",r)}});F("store",{priority:0,init(e,t,n){let r=e.getAttribute("value");if(n){if(!ae[n]){let i=r?R(r,K())||{}:{};ae[n]=K(i),Te("store:created",{name:n,keys:Object.keys(i)})}q("store",n)}}});F("computed",{priority:2,gated:!0,init(e,t,n){if(Be(e))return;let r=e.getAttribute("expr");if(!n||!r)return;let i=H(e);function o(){let s=R(r,i);i.$set(n,s)}de(r,i,o),o()}});F("watch",{priority:2,gated:!0,init(e,t,n){if(Be(e))return;let r=H(e),i=e.getAttribute("on:change"),o=R(n,r);de(n,r,()=>{let s=R(n,r);if(s!==o){let a=o;o=s,i&&Le(i,r,{$old:a,$new:s})}})}});ge();var xo=["get","post","put","patch","delete","query"];function gi(){let e=document.createElement("div");return e.setAttribute("data-nojs-sentinel",""),e.setAttribute("aria-hidden","true"),e.style.height="0",e.style.overflow="hidden",e.style.pointerEvents="none",e}function Co(e){let t=e.parentElement;for(;t&&t!==document.documentElement;){let n=getComputedStyle(t).overflowY;if(n==="scroll"||n==="auto")return t;t=t.parentElement}return document.documentElement}for(let e of xo)F(e,{priority:1,gated:!0,init(t,n,r){if(Be(t)){b(`${n}: HTTP verb directive on a loop element is not supported \u2014 move the fetch to a parent or child element`,t);return}let i=t.getAttribute("as")||"data",o=t.getAttribute("loading"),s=t.getAttribute("error"),a=t.getAttribute("empty"),l=t.getAttribute("success"),u=t.getAttribute("then"),d=t.getAttribute("redirect"),m=t.getAttribute("confirm"),y=250,v=parseInt(t.getAttribute("refresh"))||0,$=v>0?Math.max(v,y):0,T=t.getAttribute("cached"),C=t.hasAttribute("cached")?T||"memory":"none",z=t.getAttribute("body"),re=t.getAttribute("headers"),x=t.getAttribute("var"),h=t.getAttribute("into"),w=parseInt(t.getAttribute("retry"))||_.retries,A=parseInt(t.getAttribute("retry-delay"))||_.retryDelay||1e3,g=t.getAttribute("params"),E=t.getAttribute("skeleton"),P=e==="query"?"query":"get",c=t.getAttribute(`${P}-trigger`),f=t.getAttribute(`${P}-trigger-label`)||"Load More",p=e==="get"||e==="query"&&t.tagName!=="FORM",I=e==="get"||e==="query",N=e==="get"&&t.hasAttribute("get-cursor"),J=N&&t.getAttribute("get-cursor-field")||null,L=e==="get"&&t.hasAttribute("get-page"),j=L?parseInt(t.getAttribute("get-page"),10)||1:null;N&&L&&b("get-cursor and get-page are mutually exclusive; using cursor-based pagination");let W=t.getAttribute(`${P}-threshold`)||(c==="scroll"?"200px":"0px"),_e=e==="get"?t.getAttribute("get-insert"):null,Oe=e==="get"&&t.hasAttribute("get-insert")?_e==="prepend"?"prepend":"append":"replace",U=Oe!=="replace",be=t.parentElement?H(t.parentElement):K(),O=t.__ctx||K({},be);t.__ctx=O,N?O.$set("cursor",""):L&&O.$set("page",j);let Z=c==="scroll"||c==="button",we=!1,Re=!1,Ne=null,X=null;Z&&!U&&b(`get-trigger="${c}" requires get-insert to be set. Falling back to "${c==="scroll"?"visible":"immediate"}" behavior.`);let ie=Z&&U&&$>0;ie&&b(`get-trigger="${c}" is mutually exclusive with refresh. The refresh interval is being ignored.`);let Ae=[...t.childNodes].map(S=>S.cloneNode(!0)),Q=null;D(()=>{Q&&Q.abort(),Q=null});let k=null,Pe=0,V=!0;function B(){k&&k.parentNode&&k.parentNode.removeChild(k);let S=++Pe;k=gi(),Oe==="append"?t.appendChild(k):t.insertBefore(k,t.firstChild),D(()=>{S===Pe&&(k&&k.parentNode&&k.parentNode.removeChild(k),k=null)})}U&&B();function le(){if(!E)return;let S=document.getElementById(E);S&&S.style.removeProperty("display")}function ve(){if(!E)return;let S=document.getElementById(E);S&&(S.style.display="none")}function tt(){if(t.tagName!=="FORM")return;if(t.__nojsResetSubmitting){t.__nojsResetSubmitting();return}let S=H(t),M=S?.$form;M&&(M.submitting=!1,S.$set("$form",{...M}))}function xe(){for(let S of[...t.children])He(S);k&&k.parentNode&&k.parentNode.removeChild(k),k=null,t.innerHTML="",V=!0,B()}function vi(S,M){let ce=M.split("."),Fe=S;for(let ee of ce){if(Fe==null||typeof Fe!="object")return;if(je[ee]){b("Blocked access to forbidden property '"+ee+"' in get-cursor-field");return}Fe=Fe[ee]}return Fe}let Ei=["cursor","next_cursor","nextCursor","next"];function Si(S,M){if(M&&M.headers){let ce=M.headers.get("X-NoJS-Cursor");if(ce!=null)return ce||null}if(S!=null&&typeof S=="object"&&!Array.isArray(S)){if(J){let ce=vi(S,J);return ce!==void 0&&ce||null}for(let ce of Ei){let Fe=S[ce];if(Fe!=null&&Fe!=="")return Fe}}return null}function Ai(S){if(Array.isArray(S))return S;if(S!=null&&typeof S=="object"){for(let M of Object.keys(S))if(Array.isArray(S[M]))return S[M]}return S}async function Ee(){if(Z&&U&&we)return;Q&&Q.abort(),Q=new AbortController;let S=Q;if(m&&!window.confirm(m)){tt();return}Z&&U&&(we=!0),c==="button"&&U&&!V&&Zt(),le();let M=ze(r,O);if(g){let ee=R(g,O);if(ee&&typeof ee=="object"){let Ce=new URLSearchParams;for(let[Ie,$e]of Object.entries(ee))if($e!=null)if(Array.isArray($e))for(let ke of $e)ke!=null&&Ce.append(Ie,typeof ke=="object"?JSON.stringify(ke):String(ke));else typeof $e=="object"?Ce.append(Ie,JSON.stringify($e)):Ce.append(Ie,String($e));let Se=Ce.toString();if(Se){let Ie=M.includes("?")?"&":"?";M+=Ie+Se}}}let ce="";e==="query"&&(t.tagName==="FORM"?ce=JSON.stringify(Object.fromEntries(new FormData(t).entries())):z&&(ce=ze(z,O)));let Fe=e+":"+M+(ce?":"+ce:"");if(I){let ee=Vr(Fe,C);if(ee!=null){ve(),O.$set(i,ee),We(t),G(t);return}}if(o){let ee=ye(o);ee&&(U&&!V?xi(ee):(oe(t),t.innerHTML="",t.appendChild(ee),G(t)))}try{let ee=null;if(z){let se=ze(z,O);try{ee=JSON.parse(se)}catch{ee=se}}if(t.tagName==="FORM"){let se=new FormData(t);ee=Object.fromEntries(se.entries())}let Ce=re?JSON.parse(re):{};if(re)for(let se of Object.keys(Ce)){let mt=se.toLowerCase();(Tt.has(mt)||/^x-(auth|api)-/.test(mt))&&b(`Sensitive header "${se}" is set inline on a headers attribute. Use NoJS.config({ headers }) or an interceptor to avoid exposing credentials in HTML source.`)}let Se={},Ie=await gn(M,e,ee,Ce,t,Q.signal,w,A,Se),$e=null,ke=Ie;N&&($e=Si(Ie,Se),(Se.headers&&Se.headers.get("X-NoJS-Cursor"))==null&&(ke=Ai(Ie))),I&&Gr(Fe,N?ke:Ie,C);let Ue=N?ke:Ie,ur=Ue==null||Array.isArray(Ue)&&Ue.length===0||Ue==="",fr=Se.headers&&Se.headers.get("X-NoJS-Last-Page")==="true",dr=N&&$e==null,qo=ur||fr||dr;if(ur){if(ve(),N&&O.$set("cursor",""),Z&&U&&ar(),a){let se=ye(a);se&&(U&&!V?(Cn(),Yt(se),G(se)):(oe(t),t.innerHTML="",t.appendChild(se),G(t)))}return}if(ve(),N&&O.$set("cursor",$e||""),U&&!V){let se=O[i];if(Array.isArray(se)&&Array.isArray(Ue)){let mt=Oe==="append"?[...se,...Ue]:[...Ue,...se];O.$set(i,mt)}else O.$set(i,Ue)}else O.$set(i,Ue);if(h&&(ae[h]||(ae[h]=K({})),ae[h].$set(i,Ue),Ve(h)),U&&!V){Cn();let se=document.createElement("div");se.style.display="contents";let mt=K({[i]:Ue},O);se.__ctx=mt;for(let ct of Ae)se.appendChild(ct.cloneNode(!0));if(Oe==="prepend"){let ct=Co(t),Ln=ct.scrollTop,ht=ct.scrollHeight;k&&k.nextSibling?t.insertBefore(se,k.nextSibling):t.appendChild(se),G(se);let Li=ct.scrollHeight;ct.scrollTop=Ln+(Li-ht)}else k?t.insertBefore(se,k):t.appendChild(se),G(se);V=!1}else{if(l){let se=ye(l);if(se){oe(t),t.innerHTML="";let ct=document.getElementById(l.replace("#",""))?.getAttribute("var")||x||"result",Ln=K({[ct]:Ue},O),ht=document.createElement("div");ht.style.display="contents",ht.__ctx=Ln,ht.appendChild(se),t.appendChild(ht),G(ht)}}else{oe(t),t.innerHTML="";for(let se of Ae)t.appendChild(se.cloneNode(!0));We(t),G(t)}if(U){V=!1,k&&k.parentNode&&k.parentNode.removeChild(k);let se=++Pe;k=gi(),Oe==="append"?t.appendChild(k):t.insertBefore(k,t.firstChild),D(()=>{se===Pe&&(k&&k.parentNode&&k.parentNode.removeChild(k),k=null)})}}L&&!N&&Z&&U&&O.$set("page",O.page+1),(fr||dr)&&Z&&U?ar():Z&&U&&!Re&&Ti(),u&&Le(u,O,{result:Ue}),d&&me&&me.push(d),Ge("fetch:success",{url:M,data:Ue}),Te("fetch:success",{method:e,url:M})}catch(ee){if(ee.name==="AbortError")return;if(ve(),b(`${e.toUpperCase()} ${M} failed:`,ee.message),Ge("fetch:error",{url:M,error:ee}),Ge("error",{url:M,error:ee}),Te("fetch:error",{method:e,url:M,error:ee.message}),s){let Ce=ye(s);if(Ce){let Ie=document.getElementById(s.replace("#",""))?.getAttribute("var")||"err",$e=K({[Ie]:{message:ee.message,status:ee.status,body:ee.body}},O),ke=document.createElement("div");ke.style.display="contents",ke.__ctx=$e,ke.appendChild(Ce),U&&!V?(Cn(),Ci(),ke.setAttribute("data-nojs-inline-error",""),Yt(ke)):(oe(t),t.innerHTML="",t.appendChild(ke)),G(ke)}}}finally{Z&&U&&(we=!1),t.tagName==="FORM"&&e!=="get"&&!S.signal.aborted&&Q===S&&tt()}}function xi(S){let M=document.createElement("div");M.style.display="contents",M.setAttribute("data-nojs-inline-loading",""),M.appendChild(S),Yt(M),G(M)}function Cn(){let S=t.querySelector("[data-nojs-inline-loading]");S&&(He(S),S.parentNode.removeChild(S))}function Ci(){let S=t.querySelector("[data-nojs-inline-error]");S&&(He(S),S.parentNode.removeChild(S))}function Yt(S){Oe==="append"&&k?t.insertBefore(S,k):Oe==="prepend"&&k&&k.nextSibling?t.insertBefore(S,k.nextSibling):t.appendChild(S)}function ar(){Re=!0,Ne&&(Ne.disconnect(),Ne=null),Zt(),k&&k.parentNode&&k.parentNode.removeChild(k),k=null,Ge("fetch:end",{url:r})}function Ti(){c==="button"&&cr(),c==="scroll"&&(Ne?k&&Ne.observe(k):lr())}function cr(){Zt();let S=document.createElement("button");S.setAttribute("data-nojs-load-more",""),S.setAttribute("type","button"),S.setAttribute("aria-label",f),S.textContent=f,X=S;let M=()=>{Re||we||Ee()};S.addEventListener("click",M),D(()=>{S.removeEventListener("click",M)}),Yt(S)}function Zt(){X&&X.parentNode&&X.parentNode.removeChild(X),X=null;let S=t.querySelector("[data-nojs-load-more]");S&&S.parentNode&&S.parentNode.removeChild(S)}function lr(){if(typeof IntersectionObserver>"u"){b('IntersectionObserver not available, get-trigger="scroll" falling back to button trigger'),cr();return}Ne=new IntersectionObserver(S=>{for(let M of S)if(M.isIntersecting&&t.isConnected&&!Re&&!we){Ee();break}},{rootMargin:W}),k&&Ne.observe(k),D(()=>{Ne&&(Ne.disconnect(),Ne=null)})}function Tn(){Re=!1,we=!1,N?O.$set("cursor",""):L&&O.$set("page",j),Zt(),xe(),c==="scroll"&&k&&(Ne&&Ne.disconnect(),Ne=null,lr())}if(t.tagName==="FORM"&&e!=="get"){let S=M=>{M.preventDefault(),Ee()};t.addEventListener("submit",S),D(()=>t.removeEventListener("submit",S))}else if(p){if(c!=="none")if(c==="visible")if(typeof IntersectionObserver<"u"){let S=new IntersectionObserver(M=>{for(let ce of M)if(ce.isIntersecting&&t.isConnected){S.disconnect(),Ee();break}},{rootMargin:W});S.observe(t),D(()=>S.disconnect())}else b('IntersectionObserver not available, get-trigger="visible" falling back to immediate fetch'),queueMicrotask(()=>{t.isConnected&&Ee()});else if(c==="scroll"&&U)queueMicrotask(()=>{t.isConnected&&Ee()});else if(c==="scroll"&&!U)if(typeof IntersectionObserver<"u"){let S=new IntersectionObserver(M=>{for(let ce of M)if(ce.isIntersecting&&t.isConnected){S.disconnect(),Ee();break}},{rootMargin:W});S.observe(t),D(()=>S.disconnect())}else queueMicrotask(()=>{t.isConnected&&Ee()});else if(c==="button"&&U)queueMicrotask(()=>{t.isConnected&&Ee()});else if(c==="button"&&!U)queueMicrotask(()=>{t.isConnected&&Ee()});else if(c==="hover"){let S=$<=0,M=()=>{Ee()};S?t.addEventListener("mouseenter",M,{once:!0}):t.addEventListener("mouseenter",M),D(()=>t.removeEventListener("mouseenter",M))}else queueMicrotask(()=>{t.isConnected&&Ee()})}else{let S=M=>{M.preventDefault(),Ee()};t.addEventListener("click",S),D(()=>t.removeEventListener("click",S))}if(/\{[^}]+\}/.test(r)&&c!=="none"){let Se=function(){let $e=ze(r,O);if($e!==M){if(M=$e,Z&&U&&ee){let ke=ze(ee,O);if(ke!==Ce){Ce=ke,ce&&clearTimeout(ce),S>0?ce=setTimeout(()=>{Tn(),Ee()},S):(Tn(),Ee());return}}ce&&clearTimeout(ce),S>0?ce=setTimeout(Ee,S):Ee()}},S=parseInt(t.getAttribute("debounce"))||0,M=ze(r,O),ce=null,Fe=L||N,ee=N?r.replace(/\{cursor\}/g,"__CURSOR__"):L?r.replace(/\{page\}/g,"__PAGE__"):null,Ce=ee?ze(ee,O):null;D(()=>{ce&&clearTimeout(ce)});let Ie=be;for(;Ie&&Ie.__isProxy;){let $e=Ie.$watch(Se);D($e),Ie=Ie.$parent}if(r.includes("$store")){let $e=yt(r)||"*";Bt(Se,$e),Se._el=fe,D(()=>ut(Se))}r.includes("$route")&&(Ut(Se),Se._el=fe,D(()=>bt(Se))),r.includes("$i18n")&&(ot(Se),Se._el=fe,D(()=>qe.delete(Se)))}if(g&&c!=="none"){let Fe=function(){let Ce="";try{let Se=R(g,O);Ce=JSON.stringify(Se)||""}catch{return}Ce!==M&&(M=Ce,ce&&clearTimeout(ce),S>0?ce=setTimeout(Ee,S):Ee())},S=parseInt(t.getAttribute("debounce"))||0,M="";try{let Ce=R(g,O);M=JSON.stringify(Ce)||""}catch{}let ce=null;D(()=>{ce&&clearTimeout(ce)});let ee=be;for(;ee&&ee.__isProxy;){let Ce=ee.$watch(Fe);D(Ce),ee=ee.$parent}}if(Z&&U?t.refresh=function(){Tn(),Ee()}:U?t.refresh=function(){xe(),Ee()}:t.refresh=Ee,D(()=>{delete t.refresh}),$>0&&!ie){let S=setInterval(()=>{if(!t.isConnected){clearInterval(S);return}Ee()},$);D(()=>clearInterval(S))}}});ge();var An=new Map;function To(e,t){let n=An.get(e);n||(n=new Set,An.set(e,n)),n.add(t),n.size>=6&&b("SSE: "+n.size+" connections to "+e+". Browsers limit HTTP/1.1 to 6 concurrent connections per origin. Consider HTTP/2 or reducing open streams.")}function _i(e,t){let n=An.get(e);n&&(n.delete(t),n.size===0&&An.delete(e))}F("sse",{priority:1,gated:!0,init(e,t,n){if(Be(e)){b("sse: SSE directive on a loop element is not supported \u2014 move it to a parent or child element",e);return}let r=e.getAttribute("as")||"data",i=e.getAttribute("sse-event")||null,o=e.getAttribute("sse-insert"),s=o==="append"||o==="prepend"?o:"replace",a=e.getAttribute("sse-limit"),l=e.hasAttribute("sse-limit"),u=l?parseInt(a,10):0;l&&(isNaN(u)||u<=0)&&b('sse-limit="'+a+'" is not a valid positive integer; ignoring.');let d=isNaN(u)||u<=0?0:u,m=e.hasAttribute("sse-credentials"),y=e.getAttribute("into"),v=e.getAttribute("error"),$=e.getAttribute("then");d>0&&s==="replace"&&b("sse-limit has no effect without sse-insert (append or prepend)."),s!=="replace"&&!d&&b('sse-insert="'+s+'" without sse-limit may cause unbounded memory growth on long-lived streams. Set sse-limit to cap the array.');let T=e.parentElement?H(e.parentElement):K(),C=e.__ctx||K({},T);e.__ctx=C,s!=="replace"&&C.$set(r,[]),C.$set("$sse",{connecting:!0,open:!1,error:!1});let z=null,re=null,x=null;function h(c,f,p){C.$set("$sse",{connecting:c,open:f,error:p})}function w(){z&&(q("SSE: closing connection"),z.close(),re&&_i(re,z),z=null,re=null)}function A(){if(!v)return;let c=ye(v);if(!c)return;let p=document.getElementById(v.replace("#",""))?.getAttribute("var")||"err",I=K({[p]:{message:"SSE connection closed"}},C),N=document.createElement("div");N.style.display="contents",N.__ctx=I,N.appendChild(c),oe(e),e.innerHTML="",e.appendChild(N),G(N)}function g(c){w(),s!=="replace"&&C.$set(r,[]),h(!0,!1,!1),x=c,q("SSE: connecting to",c);try{re=new URL(c,window.location.origin).origin}catch{re=window.location.origin}let f=new EventSource(c,{withCredentials:m});z=f,To(re,f),f.onopen=function(){if(!e.isConnected){f.close();return}q("SSE: connection opened",c),h(!1,!0,!1)},f.onerror=function(){if(!e.isConnected){f.close();return}f.readyState===EventSource.CLOSED?(q("SSE: connection closed by server",c),h(!1,!1,!0),re&&_i(re,f),z===f&&(z=null,re=null),A()):(q("SSE: auto-reconnecting",c),h(!0,!1,!1))};let p=i||"message";function I(N){if(!e.isConnected){f.close();return}q("SSE: message received on",p);let J;try{J=JSON.parse(N.data)}catch{J=N.data}if(s==="append"){let L=Array.isArray(C[r])?[...C[r]]:[];L.push(J),d>0&&L.length>d&&L.shift(),C.$set(r,L)}else if(s==="prepend"){let L=Array.isArray(C[r])?[...C[r]]:[];L.unshift(J),d>0&&L.length>d&&L.pop(),C.$set(r,L)}else C.$set(r,J);y&&(ae[y]||(ae[y]=K({})),ae[y].$set(r,C[r]),Ve(y)),$&&Le($,C,{$event:J})}f.addEventListener(p,I)}D(w);let E=ze(n,C);if(g(E),/\{[^}]+\}/.test(n)){let c=function(){let p=ze(n,C);p!==x&&(q("SSE: URL changed, reconnecting",x,"\u2192",p),g(p))},f=T;for(;f&&f.__isProxy;){let p=f.$watch(c);D(p),f=f.$parent}if(n.includes("$store")){let p=yt(n)||"*";Bt(c,p),c._el=fe,D(()=>ut(c))}n.includes("$route")&&(Ut(c),c._el=fe,D(()=>bt(c))),n.includes("$i18n")&&(ot(c),c._el=fe,D(()=>qe.delete(c)))}}});ge();F("bind",{priority:20,gated:!0,init(e,t,n){let r=H(e),i;function o(){let s=R(n,r),a=s!=null?String(s):"";a!==i&&(i=a,e.textContent=a)}de(n,r,o),o()}});F("bind-html",{priority:20,gated:!0,init(e,t,n){let r=H(e);(_.debug||_.devtools)&&!/^['"`]/.test(n.trim())&&b(`[Security] bind-html used with dynamic expression: "${n}". Ensure the value is trusted or sanitized \u2014 use bind for plain text.`,e);let i;function o(){let s=R(n,r);if(s!=null){let a=String(s);if(a===i)return;i=a,oe(e),e.innerHTML=_n(a)}}de(n,r,o),o()}});var Lo=new Set(["href","src","action","formaction","poster","data"]);function yi(e){let n=new DOMParser().parseFromString(e,"image/svg+xml").documentElement;if(n.querySelector("parsererror")||n.nodeName!=="svg"||n.getElementsByTagNameNS("http://www.mozilla.org/newlayout/xml/parsererror.xml","parsererror").length)return"";function r(i){for(let o of[...i.attributes]){let s=o.name.toLowerCase();if(s.startsWith("on")){i.removeAttribute(o.name);continue}if(s==="href"||s==="xlink:href"){let a=o.value.toLowerCase().replace(/[\u0000-\u0020]/g,"");/^(javascript|vbscript):/.test(a)&&i.removeAttribute(o.name)}}}for(let i of[...n.querySelectorAll("script")])i.remove();r(n);for(let i of n.querySelectorAll("*"))r(i);return new XMLSerializer().serializeToString(n)}function Oo(e){try{let t=e.match(/^data:image\/svg\+xml;base64,(.+)$/i);if(t){let o=yi(atob(t[1]));return"data:image/svg+xml;base64,"+btoa(o)}let n=e.indexOf(",");if(n===-1)return"#";let r=e.slice(0,n+1),i=yi(decodeURIComponent(e.slice(n+1)));return r+encodeURIComponent(i)}catch{return"#"}}function No(e,t){if(Lo.has(e)){let n=String(t).trimStart(),r=n.toLowerCase().replace(/[\u0000-\u0020]/g,"");if(/^(javascript|vbscript):/.test(r))return"#";if(/^data:/.test(r)){if(/^data:image\/svg\+xml/.test(r))return Oo(n);if(!/^data:image\//.test(r))return"#"}}return t}var Po=new Set(["disabled","readonly","checked","selected","hidden","required"]);F("bind-*",{priority:20,init(e,t,n){let r=t.replace("bind-",""),i=H(e);if(r==="value"&&(e.tagName==="INPUT"||e.tagName==="TEXTAREA"||e.tagName==="SELECT")){let a=()=>{let l;if(e.type==="number"){if(e.value===""||isNaN(e.valueAsNumber))return;l=e.valueAsNumber}else l=e.value;Le(`${n} = __val`,i,{__val:l})};e.addEventListener("input",a),D(()=>e.removeEventListener("input",a))}let o;function s(){let a=R(n,i);if(Po.has(r)){let u=!!a,d=r in e?!!e[r]:e.hasAttribute(r);if(u===o&&d===u)return;o=u,u?e.setAttribute(r,""):e.removeAttribute(r),r in e&&(e[r]=u);return}let l=a!=null?String(No(r,a)):null;l===o&&e.getAttribute(r)===l||(o=l,l!=null?e.setAttribute(r,l):e.removeAttribute(r))}de(n,i,s),s()}});F("model",{priority:20,gated:!0,init(e,t,n){let r=H(e),i=e.tagName,o=e.type;function s(){let u=R(n,r);if(i==="INPUT"&&o==="checkbox")e.checked=!!u;else if(i==="INPUT"&&o==="radio")e.checked=e.value===String(u);else if(i==="SELECT")e.value=u!=null?String(u):"";else{let d=u!=null?String(u):"";if(document.activeElement===e){if(o==="number"||o==="range"){let m=e.valueAsNumber;if(u!=null&&!isNaN(m)&&m===Number(u))return}else if(e.value===d)return}e.value=d}}let a=i==="SELECT"||o==="checkbox"||o==="radio"?"change":"input",l=()=>{let u;o==="checkbox"?u=e.checked:o==="number"||o==="range"?u=Number(e.value):u=e.value,Le(`${n} = __val`,r,{__val:u})};e.addEventListener(a,l),D(()=>e.removeEventListener(a,l)),de(n,r,s),s()}});ge();function bi(e){let t=[],n=e.previousElementSibling;for(;n;){let r=n.getAttribute("if")||n.getAttribute("else-if");if(r)t.push(r);else break;n=n.previousElementSibling}return t}F("if",{priority:10,init(e,t,n){let r=H(e),i=e.getAttribute("then"),o=e.getAttribute("else"),s=e.getAttribute("animate-enter")||e.getAttribute("animate"),a=e.getAttribute("animate-leave"),l=e.getAttribute("transition"),u=parseInt(e.getAttribute("animate-duration"))||0,d=(e.__i18nSavedChildren||[...e.childNodes]).map(T=>T.cloneNode(!0)),m,y=null;D(()=>{y&&(y(),y=null)});function v(){let T=!!R(n,r);T!==m&&(m=T,a||l?(y&&(y(),y=null),y=wn(e,a,l,()=>{y=null,$(T)},u)):$(T))}function $(T){if(e.__ifState=T,T||ci(e),oe(e),T)if(i){let C=ye(i);C&&(e.innerHTML="",e.appendChild(C))}else{e.innerHTML="";for(let C of d)e.appendChild(C.cloneNode(!0))}else if(o){let C=ye(o);C&&(e.innerHTML="",e.appendChild(C))}else e.innerHTML="";We(e),G(e),T&&ai(e),(s||l)&&Ct(e,s,l,u)}de(n,r,v),v()}});F("else-if",{priority:10,init(e,t,n){if(Be(e))return;let r=H(e),i=e.getAttribute("then"),o=[...e.childNodes].map(u=>u.cloneNode(!0)),s;function a(){let u=e.previousElementSibling;for(;u;){let y=u.getAttribute("if")||u.getAttribute("else-if");if(y){if(R(y,r)){if(s==="hidden")return;s="hidden",oe(e),e.innerHTML="",e.style.display="none";return}}else break;u=u.previousElementSibling}let d=!!R(n,r),m=d?"shown":"hidden";if(m!==s)if(s=m,e.style.display="",d){if(i){let y=ye(i);y&&(oe(e),e.innerHTML="",e.appendChild(y))}else{oe(e),e.innerHTML="";for(let y of o)e.appendChild(y.cloneNode(!0))}We(e),G(e)}else oe(e),e.innerHTML=""}let l=bi(e);l.push(n),de(l.join(" "),r,a),a()}});F("else",{priority:10,init(e){if(e.hasAttribute("if")||Be(e))return;let t=H(e),n=e.getAttribute("then"),r=[...e.childNodes].map(a=>a.cloneNode(!0)),i,o=!1;function s(){let a=e.previousElementSibling,l=!1;for(;a;){let u=a.getAttribute("if")||a.getAttribute("else-if");if(u){if(l=!0,R(u,t)){if(i==="hidden")return;i="hidden",oe(e),e.innerHTML="",e.style.display="none";return}}else break;a=a.previousElementSibling}if(!l&&!o&&(o=!0,b('else: no preceding if/else-if sibling found \u2014 note the sibling else pattern for loops was removed in v1.15; use else="templateId" on the loop element',e)),i!=="shown"){if(i="shown",e.style.display="",n){let u=ye(n);u&&(oe(e),e.innerHTML="",e.appendChild(u))}else{oe(e),e.innerHTML="";for(let u of r)e.appendChild(u.cloneNode(!0))}We(e),G(e)}}de(bi(e).join(" "),t,s),s()}});F("show",{priority:20,init(e,t,n){let r=H(e),i=e.getAttribute("animate-enter")||e.getAttribute("animate"),o=e.getAttribute("animate-leave"),s=e.getAttribute("transition"),a=parseInt(e.getAttribute("animate-duration"))||0,l;function u(){let d=!!R(n,r);d!==l&&(l=d,d?(e.style.display="",(i||s)&&Ct(e,i,s,a)):o||s?wn(e,o,s,()=>{e.style.display="none"},a):e.style.display="none")}de(n,r,u),u()}});F("hide",{priority:20,init(e,t,n){let r=H(e),i=e.getAttribute("animate-enter")||e.getAttribute("animate"),o=e.getAttribute("animate-leave"),s=e.getAttribute("transition"),a=parseInt(e.getAttribute("animate-duration"))||0,l;function u(){let d=!R(n,r);d!==l&&(l=d,d?(e.style.display="",(i||s)&&Ct(e,i,s,a)):o||s?wn(e,o,s,()=>{e.style.display="none"},a):e.style.display="none")}de(n,r,u),u()}});F("animate",{priority:15,init(e,t,n){if(e.hasAttribute("if")||e.hasAttribute("show")||e.hasAttribute("hide"))return;let r=e.getAttribute("animate-enter")||n,i=e.getAttribute("transition"),o=parseInt(e.getAttribute("animate-duration"))||0;(r||i)&&Ct(e,r,i,o,!0)}});function Io(e){let t=[],n=0,r=null,i=0;for(let o=0;o=0&&e[l]==="\\";l--)a++;a%2===0&&(r=null)}}else s==="'"||s==='"'||s==="`"?r=s:s==="("||s==="["||s==="{"?n++:s===")"||s==="]"||s==="}"?n--:s===","&&n===0&&(t.push(e.slice(i,o)),i=o+1)}return t.push(e.slice(i)),t}F("switch",{priority:10,init(e,t,n){let r=H(e);function i(){let o=R(n,r),s=!1;for(let a of[...e.children]){let l=a.getAttribute("case"),u=a.hasAttribute("default"),d=a.getAttribute("then");if(l){let m=Io(l).map(y=>R(y.trim(),r));if(!s&&m.includes(o))if(s=!0,a.style.display="",d){let y=ye(d);y&&(oe(a),a.innerHTML="",a.appendChild(y)),a.__declared=!1,G(a)}else a.__switchDisposed&&(a.__switchDisposed=!1,a.__declared=!1,G(a));else oe(a),a.__switchDisposed=!0,a.style.display="none"}else if(u)if(s&&(oe(a),a.__switchDisposed=!0),a.style.display=s?"none":"",!s&&d){let m=ye(d);m&&(oe(a),a.innerHTML="",a.appendChild(m)),a.__declared=!1,G(a)}else!s&&a.__switchDisposed&&(a.__switchDisposed=!1,a.__declared=!1,G(a))}}de(n,r,i),i()}});ge();ge();F("class-*",{priority:20,init(e,t,n){let r=t.replace("class-",""),i=H(e);if(r==="map"){let a=function(){let l=R(n,i);if(l&&typeof l=="object")for(let[u,d]of Object.entries(l))u.split(/\s+/).filter(Boolean).forEach(y=>e.classList.toggle(y,!!d))};de(n,i,a),a();return}if(r==="list"){let l=function(){let u=R(n,i);if(Array.isArray(u)){a.forEach(m=>{m&&e.classList.remove(m)});let d=u.filter(Boolean);d.forEach(m=>e.classList.add(m)),a=d}},a=[];de(n,i,l),l();return}let o=null;function s(){let a=!!R(n,i);a===o&&e.classList.contains(r)===a||(o=a,e.classList.toggle(r,a))}de(n,i,s),(n.includes("NoJS.locale")||n.includes("window.NoJS.locale"))&&ot(s),s()}});F("style-*",{priority:20,init(e,t,n){let r=t.replace("style-",""),i=H(e);if(r==="map"){let d=function(){let m=R(n,i);if(m&&typeof m=="object"){let y=Object.keys(m);for(let v of u)v in m||(v.startsWith("--")?e.style.removeProperty(v):e.style[v]="");for(let[v,$]of Object.entries(m))v.startsWith("--")?e.style.setProperty(v,$??""):e.style[v]=$??"";u=y}},u=[];de(n,i,d),d();return}let o=r.startsWith("--"),s=o?r:r.replace(/-([a-z])/g,(u,d)=>d.toUpperCase()),a;function l(){let u=R(n,i),d=u!=null?String(u):"";d===a&&e.style.getPropertyValue(r)===d||(a=d,o?e.style.setProperty(s,d):e.style[s]=d)}de(n,i,l),l()}});ge();var $o=new Set(["mounted","init","updated","error","unmounted"]);F("on:*",{priority:20,gated:!0,init(e,t,n){let r=H(e);if(t.indexOf(".")===-1&&!e.__gatedDirs){let v=t.slice(3);if(!$o.has(v)){let $=C=>Le(n,r,{$event:C,$el:e});e.addEventListener(v,$);let T=()=>e.removeEventListener(v,$);T._elOnly=!0,D(T);return}}let i=t.replace("on:","").split("."),o=i[0],s=new Set(i.slice(1));if(o==="mounted"){requestAnimationFrame(()=>Le(n,r,{$el:e}));return}if(o==="init"){Le(n,r,{$el:e});return}if(o==="updated"){let v=!1,$=new MutationObserver(()=>{if(!e.isConnected){$.disconnect();return}if(!v){v=!0;try{Le(n,r,{$el:e})}finally{Promise.resolve().then(()=>{v=!1})}}});$.observe(e,{childList:!0,subtree:!0,characterData:!0,attributes:!0}),D(()=>$.disconnect());return}if(o==="error"){let v=$=>{(e.contains($.target)||$.target===e)&&Le(n,r,{$el:e,$error:$.error||$.message})};window.addEventListener("error",v),D(()=>window.removeEventListener("error",v));return}if(o==="unmounted"){let v=new MutationObserver($=>{for(let T of $)for(let C of T.removedNodes)if(C===e||C.contains?.(e)){Le(n,r,{$el:e}),v.disconnect();return}});e.parentElement&&v.observe(e.parentElement,{childList:!0,subtree:!0}),D(()=>v.disconnect());return}let a=0,l=0,u=i.slice(1);for(let v=0;v{Le(n,r,{$event:v,$el:e})};if(a>0){let v=d,$;d=T=>{clearTimeout($),$=setTimeout(()=>v(T),a)},D(()=>clearTimeout($))}if(l>0){let v=d,$=0;d=T=>{let C=Date.now();C-$>=l&&($=C,v(T))}}let m=v=>{if(o==="keydown"||o==="keyup"||o==="keypress"){let $=["enter","escape","tab","space","delete","backspace","up","down","left","right","ctrl","alt","shift","meta"];for(let T of s)if($.includes(T)&&(T==="enter"&&v.key!=="Enter"||T==="escape"&&v.key!=="Escape"||T==="tab"&&v.key!=="Tab"||T==="space"&&v.key!==" "||T==="delete"&&v.key!=="Delete"&&v.key!=="Backspace"||T==="backspace"&&v.key!=="Backspace"||T==="up"&&v.key!=="ArrowUp"||T==="down"&&v.key!=="ArrowDown"||T==="left"&&v.key!=="ArrowLeft"||T==="right"&&v.key!=="ArrowRight"||T==="ctrl"&&!v.ctrlKey||T==="alt"&&!v.altKey||T==="shift"&&!v.shiftKey||T==="meta"&&!v.metaKey))return}s.has("prevent")&&v.preventDefault(),s.has("stop")&&v.stopPropagation(),!(s.has("self")&&v.target!==e)&&d(v)},y={};if(s.has("once")&&(y.once=!0),e.addEventListener(o,m,y),!y.once){let v=()=>e.removeEventListener(o,m,y);v._elOnly=!0,D(v)}}});F("trigger",{priority:20,init(e,t,n){let r=H(e),i=e.getAttribute("trigger-data"),o=()=>{let a=i?R(i,r):null;e.dispatchEvent(new CustomEvent(n,{detail:a,bubbles:!0}))};e.addEventListener("click",o);let s=()=>e.removeEventListener("click",o);s._elOnly=!0,D(s)}});ge();F("ref",{priority:5,init(e,t,n){it[n]=e,D(()=>{it[n]===e&&delete it[n]})}});F("use",{priority:9,init(e,t,n){let r=H(e),i=ye(n);if(!i)return;let o={};for(let d of[...e.attributes])if(d.name.startsWith("var-")){let m=d.name.replace("var-","");o[m]=R(d.value,r)}let s=K(o,r),a={};for(let d of[...e.children]){let m=d.getAttribute("slot")||"default";a[m]||(a[m]=document.createDocumentFragment()),a[m].appendChild(d.cloneNode(!0))}let l=i.querySelectorAll("slot");for(let d of l){let m=d.getAttribute("name")||"default";a[m]&&d.replaceWith(a[m])}oe(e),e.innerHTML="";let u=document.createElement("div");u.style.display="contents",u.__ctx=s,u.appendChild(i),e.appendChild(u),G(u)}});F("call",{priority:20,init(e,t,n){let r=H(e),i=e.getAttribute("method")||"get",o=e.getAttribute("as")||"data",s=e.getAttribute("into"),a=e.getAttribute("success"),l=e.getAttribute("error"),u=e.getAttribute("loading"),d=e.getAttribute("then"),m=e.getAttribute("confirm"),y=e.getAttribute("body"),v=e.getAttribute("redirect"),$=e.getAttribute("headers"),T=[...e.childNodes].map(h=>h.cloneNode(!0)),C=null,z=null;function re(){z&&(oe(z),z.remove(),z=null)}D(re);let x=async h=>{if(h.preventDefault(),m&&!window.confirm(m))return;C&&C.abort(),C=new AbortController;let w=ze(n,r);if(u){let A=ye(u);A&&(oe(e),e.innerHTML="",e.appendChild(A),G(e),e.disabled=!0)}try{let A=null;if(y){let P=ze(y,r);try{A=JSON.parse(P)}catch{A=P}}let g=$?JSON.parse($):{};if($)for(let P of Object.keys(g)){let c=P.toLowerCase();(Tt.has(c)||/^x-(auth|api)-/.test(c))&&b(`Sensitive header "${P}" is set inline on a headers attribute. Use NoJS.config({ headers }) or an interceptor to avoid exposing credentials in HTML source.`)}let E=await gn(w,i,A,g,e,C.signal);if(u){oe(e),e.innerHTML="";for(let P of T)e.appendChild(P.cloneNode(!0));We(e),G(e),e.disabled=!1}if(o&&r.$set(o,E),s&&(ae[s]||(ae[s]=K({})),ae[s].$set(o,E),Ve(s)),d&&Le(d,r,{result:E}),a){let P=ye(a),c=e.closest("[route-view]")||e.parentElement;if(P&&c){let p=document.getElementById(a.replace("#",""))?.getAttribute("var")||"result",I=K({[p]:E},r);re();let N=document.createElement("div");N.style.display="contents",N.__ctx=I,N.appendChild(P),c.appendChild(N),z=N,G(N)}}v&&me&&me.push(v),Ge("fetch:success",{url:w,data:E}),Te("fetch:success",{method:i,url:w})}catch(A){if(A.name==="AbortError")return;if(b(`call ${i.toUpperCase()} ${w} failed:`,A.message),u){oe(e),e.innerHTML="";for(let g of T)e.appendChild(g.cloneNode(!0));We(e),G(e),e.disabled=!1}if(Ge("fetch:error",{url:w,error:A}),Ge("error",{url:w,error:A}),Te("fetch:error",{method:i,url:w,error:A.message}),l){let g=ye(l),E=e.closest("[route-view]")||e.parentElement;if(g&&E){let c=document.getElementById(l.replace("#",""))?.getAttribute("var")||"err",f=K({[c]:{message:A.message,status:A.status,body:A.body}},r);re();let p=document.createElement("div");p.style.display="contents",p.__ctx=f,p.appendChild(g),E.appendChild(p),z=p,G(p)}}}};e.addEventListener("click",x),D(()=>e.removeEventListener("click",x))}});ge();var ko=Object.freeze({valid:!0,dirty:!1,touched:!1,error:null,value:""}),Ro=new Proxy({},{get:()=>ko});F("validate",{priority:30,init(e,t){b('[NoJS] "validate" has moved to @no-js-dev/nojs-elements. Install the plugin and call NoJS.use(NoJSElements) to enable it.');let n=H(e);n.$form={valid:!1,dirty:!1,touched:!1,submitting:!1,pending:!1,errors:{},values:{},firstError:null,errorCount:0,fields:Ro,reset(){}};let r=e.tagName==="FORM"?e:e.closest("form");if(r){let i=o=>{o.preventDefault(),b('[NoJS] Form submission blocked \u2014 "validate" requires @no-js-dev/nojs-elements.')};r.addEventListener("submit",i),D(()=>r.removeEventListener("submit",i))}}});ge();F("error-boundary",{priority:1,init(e,t,n){let r=H(e),i=!1;function o(l){if(i){b("error-boundary: secondary error inside fallback (suppressed to prevent infinite recursion):",l);return}i=!0;try{let u=ye(n);if(u){let d=K({err:{message:l}},r);oe(e),e.innerHTML="";let m=document.createElement("div");m.style.display="contents",m.__ctx=d,m.appendChild(u),e.appendChild(m),G(m)}}finally{i=!1}}let s=l=>{o(l.detail?.message||"An error occurred")};e.addEventListener("nojs:error",s),D(()=>e.removeEventListener("nojs:error",s));let a=l=>{l.target===window?e.isConnected&&o(l.message||"An error occurred"):(e.contains(l.target)||e===l.target)&&o(l.message||"An error occurred")};window.addEventListener("error",a,{capture:!0}),D(()=>window.removeEventListener("error",a,{capture:!0}))}});st();ge();F("t",{priority:20,gated:!0,init(e,t,n){let r=H(e),i=e.hasAttribute("t-html"),o=[];for(let l of[...e.attributes])l.name.startsWith("t-")&&l.name!=="t"&&l.name!=="t-html"&&o.push(l.value);let s=o.length?n+" "+o.join(" "):n;function a(){let l={};for(let d of[...e.attributes])if(d.name.startsWith("t-")&&d.name!=="t"&&d.name!=="t-html"){let m=d.name.replace("t-","");l[m]=R(d.value,r)??d.value}let u=he.t(n,l);i?(oe(e),e.innerHTML=_n(u)):e.textContent=u}de(s,r,a),ot(a),a()}});F("i18n-ns",{priority:1,init(e,t,n){if(!n)return;e.__i18nSavedChildren=[...e.childNodes].map(i=>i.cloneNode(!0));let r=document.createDocumentFragment();for(;e.firstChild;)r.appendChild(e.firstChild);jn(n).then(()=>{if(e.__ifState!==void 0){Ot();return}e.appendChild(r),G(e),Ot()})}});ge();var Do=e=>`[NoJS] "${e}" has moved to @no-js-dev/nojs-elements. Install the plugin and call NoJS.use(NoJSElements) to enable it.`;for(let e of["drag","drop","drag-list","drag-multiple"])F(e,{priority:e==="drag-multiple"?16:e==="drag-list"?10:15,init(t,n){b(Do(n))}});ge();function jo(e,t){return e.replace(/\{\s*([^}"'{\s][^}]*)\}/g,(n,r)=>{try{let i=R(r.trim(),t);return i!=null?String(i):""}catch{return""}})}F("page-title",{priority:1,gated:!0,init(e,t,n){if(Be(e)){b(`${t}: head directive on a loop element is not supported \u2014 move it to a parent or child element`,e);return}let r=H(e);function i(){let o=R(n,r);o!=null&&(document.title=String(o))}de(n,r,i),i()}});F("page-description",{priority:1,gated:!0,init(e,t,n){if(Be(e)){b(`${t}: head directive on a loop element is not supported \u2014 move it to a parent or child element`,e);return}let r=H(e),i=!1,o=null;function s(){let a=R(n,r);if(a==null)return;let l=document.querySelector('meta[name="description"]');l||(l=document.createElement("meta"),l.name="description",document.head.appendChild(l),i=!0),o=l,l.content=String(a)}de(n,r,s),s(),D(()=>{i&&o&&o.isConnected&&o.remove()})}});F("page-canonical",{priority:1,gated:!0,init(e,t,n){if(Be(e)){b(`${t}: head directive on a loop element is not supported \u2014 move it to a parent or child element`,e);return}let r=H(e),i=!1,o=null;function s(){let a=R(n,r);if(a==null)return;let l=document.querySelector('link[rel="canonical"]');l||(l=document.createElement("link"),l.rel="canonical",document.head.appendChild(l),i=!0);let u=String(a);/^\s*(javascript|vbscript|data):/i.test(u)||(o=l,l.href=u)}de(n,r,s),s(),D(()=>{i&&o&&o.isConnected&&o.remove()})}});F("page-jsonld",{priority:1,gated:!0,init(e,t,n){if(Be(e)){b(`${t}: head directive on a loop element is not supported \u2014 move it to a parent or child element`,e);return}let r=H(e),i=(e.textContent||e.innerHTML).trim();if(!i)return;let o=!1,s=null;function a(){let l=jo(i,r);if(!l)return;let u=document.querySelector('script[type="application/ld+json"][data-nojs]');u||(u=document.createElement("script"),u.type="application/ld+json",u.setAttribute("data-nojs",""),document.head.appendChild(u),o=!0),s=u,u.textContent=l.replace(/<\//g,"<\\/")}de(i,r,a),a(),D(()=>{o&&s&&s.isConnected&&s.remove()})}});ri();var Ye=null,or=!1,Mo=new Set(["store","route","router","i18n","refs","form","sse","parent","watch","set","notify","raw","isProxy","listeners","app","config","env","debug","version","plugins","globals","el","event","self","this","super","window","document","toString","valueOf","hasOwnProperty"]),Fo=typeof window<"u"?new Set([eval,Function,window.eval,window.Function].filter(Boolean)):new Set;function wi(e){return Fo.has(e)}var Ho=new Set(["__proto__","constructor","prototype"]),Bo=new Set(["uppercase","lowercase","capitalize","truncate","trim","stripHtml","slugify","nl","nl2br","encodeUri","number","currency","percent","filesize","ordinal","count","first","last","join","reverse","unique","sortBy","where","pluck","keys","values","json","debug","default","date","datetime","relative","fromNow"]),Uo=new Set(["required","email","url","min","max","minlength","maxlength","pattern","match","number","integer","alpha","alphanumeric"]);function sr(e,t=new Set){if(!(!e||typeof e!="object"||t.has(e))){t.add(e);for(let n of Object.keys(e))if(Ho.has(n))throw b("NoJS.global(): value contains a forbidden key: "+n),new Error("unsafe_global");for(let n of Object.values(e)){if(wi(n))throw b("NoJS.global(): value contains a forbidden reference (eval/Function)."),new Error("unsafe_global");n&&typeof n=="object"&&sr(n,t)}}}function zo(){return typeof window>"u"?null:(_.router||{}).useHash?window.location.hash.slice(1)||"/":Ht(window.location.pathname)}var et={get baseApiUrl(){return _.baseApiUrl},set baseApiUrl(e){_.baseApiUrl=e},get locale(){return he.locale},set locale(e){he.locale=e},config(e={}){if(or){let s=["sanitize","dangerouslyDisableSanitize","sanitizeHtml"];for(let a of s)a in e&&(b(`config.${a} cannot be changed after init()`),delete e[a])}let t={..._.headers},n={..._.cache},r={..._.templates},i={..._.router},o={..._.i18n};if("csp"in e&&(b("csp config option removed \u2014 No.JS is now CSP-safe by default"),delete e.csp),e.exprCacheSize!==void 0){let s=parseInt(e.exprCacheSize);e.exprCacheSize=Number.isFinite(s)&&s>0?s:500}if(Object.assign(_,e),e.sanitize===!1&&b("sanitize:false is deprecated \u2014 use dangerouslyDisableSanitize:true to make the risk explicit."),e.headers&&(_.headers={...t,...e.headers}),e.csrf&&(_.csrf=e.csrf),e.cache&&(_.cache={...n,...e.cache}),e.templates&&(_.templates={...r,...e.templates}),e.router&&("mode"in e.router&&!("useHash"in e.router)&&(q('router.mode is deprecated. Use router.useHash instead: mode: "hash" \u2192 useHash: true, mode: "history" \u2192 useHash: false',"warn"),e.router.useHash=e.router.mode==="hash",delete e.router.mode),_.router={...i,...e.router}),e.i18n&&(_.i18n={...o,...e.i18n},he.locale=e.i18n.defaultLocale||he.locale),e.stores){for(let[s,a]of Object.entries(e.stores))ae[s]||(ae[s]=K(a||{}),Te("store:created",{name:s,keys:Object.keys(a||{})}));delete _.stores}},use(e,t={}){if(en){b("Cannot install plugins during dispose.");return}if(typeof e=="function"){if(!e.name||e.name==="anonymous"){b('Plugin must have a unique, non-empty name. Use { name: "my-plugin", install: fn }.');return}e={name:e.name,install:e}}if(!e.name||typeof e.name!="string"||e.name==="anonymous"){b("Plugin must have a unique, non-empty name.");return}if(Je.has(e.name)){Je.get(e.name).plugin!==e&&b(`Plugin "${e.name}" name collision: a different plugin with this name is already installed.`);return}e.capabilities&&_.debug&&q(`Plugin "${e.name}" declares capabilities:`,e.capabilities),t.trusted===!0&&b(`WARNING: Plugin "${e.name}" installed with trusted access to sensitive HTTP headers.`),$n(e.name);try{e.install(et,t)}finally{$n(null)}Je.set(e.name,{plugin:e,options:t}),Ye&&e.init&&Ye.then(()=>e.init(et)).catch(n=>b(`Plugin "${e.name}" init error:`,n.message)),q(`Plugin "${e.name}" installed.`)},global(e,t){if(typeof e!="string"||!e){b("NoJS.global() requires a non-empty string name.");return}if(e==="__proto__"||e==="constructor"||e==="prototype"){b(`NoJS.global(): "${e}" is a forbidden name.`);return}if(Mo.has(e)){b(`NoJS.global(): "${e}" is reserved and cannot be used.`);return}if(!/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(e)){b(`NoJS.global(): "${e}" is not a valid identifier.`);return}if(wi(t)){b(`NoJS.global(): value for "${e}" is a forbidden reference (eval/Function).`);return}if(e in De&>[e]&>[e]!==_t&&b(`Global "$${e}" owned by "${gt[e]}" is being overwritten.`),t&&typeof t=="object"&&!t.__isProxy){try{t=JSON.parse(JSON.stringify(t))}catch{try{sr(t)}catch(n){if(n.message==="unsafe_global")return}}t=K(t)}else if(t&&typeof t=="object"&&t.__isProxy)try{sr(t.__raw??t)}catch(n){if(n.message==="unsafe_global")return}De[e]=t,_t&&(gt[e]=_t),Ve(),Te("global:set",{name:e,hasValue:t!=null}),q(`Global "$${e}" registered.`)},async dispose(){In(!0);try{if(Ye)try{await Ye}catch{}let e=[...Je.entries()].reverse();for(let[t,{plugin:n}]of e)if(n.dispose)try{let r;await Promise.race([Promise.resolve(n.dispose(et)).finally(()=>clearTimeout(r)),new Promise((i,o)=>{r=setTimeout(()=>o(new Error("Dispose timeout")),3e3)})])}catch(r){b(`Plugin "${t}" dispose error:`,r.message)}Je.clear();for(let t in De)delete De[t];for(let t in gt)delete gt[t];Ke.request.length=0,Ke.response.length=0,me&&me.destroy&&me.destroy(),kn(null),Pr(),or=!1,Ye=null,q("Disposed.")}finally{In(!1)}},async init(e){if(!(typeof document>"u"))return Ye||(or=!0,Ye=(async()=>{if(e=e||document.body,q("Initializing..."),_.i18n.loadPath){let n=new Set([he.locale,_.i18n.fallbackLocale]);await Promise.all([...n].map(r=>Dn(r)))}Mt(e);let t=zo();await ei(t),document.querySelector("[route-view]")&&kn(pi()),G(e),me&&await me.init(),q("Initialized."),ti(),Ir(et);for(let[,{plugin:n}]of Je)n.init&&await n.init(et);Ge("plugins:ready")})(),Ye)},directive(e,t){F(e,t)},filter(e,t){if(typeof e!="string"||!e)throw new TypeError("NoJS.filter() requires a non-empty string name.");if(typeof t!="function")throw new TypeError(`NoJS.filter(): "${e}" handler must be a function.`);if(Bo.has(e)){b(`NoJS.filter(): "${e}" is a built-in filter and cannot be overridden.`);return}te[e]=t},validator(e,t){if(typeof e!="string"||!e)throw new TypeError("NoJS.validator() requires a non-empty string name.");if(typeof t!="function")throw new TypeError(`NoJS.validator(): "${e}" handler must be a function.`);if(Uo.has(e)){b(`NoJS.validator(): "${e}" is a built-in validator and cannot be overridden.`);return}Pn[e]=t},i18n(e){if(e.loadPath!=null&&(_.i18n.loadPath=e.loadPath),e.ns&&(_.i18n.ns=e.ns),Array.isArray(e.supportedLocales)&&(_.i18n.supportedLocales=e.supportedLocales),e.cache!=null&&(_.i18n.cache=e.cache),e.persist!=null&&(_.i18n.persist=e.persist),e.locales&&(he.locales=e.locales),e.fallbackLocale&&(_.i18n.fallbackLocale=e.fallbackLocale),e.defaultLocale&&(he._locale=e.defaultLocale),_.i18n.persist&&typeof localStorage<"u")try{let t=localStorage.getItem("nojs-locale");if(t){he._locale=t;return}}catch{}if(e.detectBrowser){let t=typeof navigator<"u"&&navigator.language||"en",n=t.split("-")[0],r=Array.isArray(_.i18n.supportedLocales)?_.i18n.supportedLocales:[],i=o=>he.locales[o]||r.includes(o);i(t)?he._locale=t:i(n)&&(he._locale=n)}},on(e,t){return rt[e]||(rt[e]=[]),rt[e].length>=_.maxEventListeners&&b('MaxListenersExceeded: event "'+e+'" has '+rt[e].length+" listeners (max "+_.maxEventListeners+"). Possible memory leak."),rt[e].push(t),()=>{rt[e]=rt[e].filter(n=>n!==t)}},interceptor(e,t){if(en){b("Cannot register interceptors during dispose.");return}if(!Ke[e]){b(`NoJS.interceptor(): unknown type "${e}" (expected "request" or "response").`);return}Ke[e].push(_t?{fn:t,pluginName:_t}:t)},get store(){return ae},notify(){Ve()},get router(){return me},createContext:K,evaluate:R,findContext:H,processTree:G,resolve:jt,get internals(){return Object.freeze({execStatement:Le,cloneTemplate:ye,disposeChildren:oe,disposeTree:He,warn:b,validators:Object.freeze({...Pn}),removeCoreDirective:ii,onDispose:D})},version:"1.20.0"};Object.defineProperty(et,"CANCEL",{value:zt,writable:!1,configurable:!1});Object.defineProperty(et,"RESPOND",{value:Lt,writable:!1,configurable:!1});Object.defineProperty(et,"REPLACE",{value:Wt,writable:!1,configurable:!1});Object.defineProperty(et,"_initialized",{get(){return Ye!==null},set(e){e||(Ye=null)},configurable:!0});var xn=et;window.NoJS=xn;document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>xn.init()):xn.init();})();
+`.trim(),t=document.createElement("style");t.setAttribute("data-nojs-animations",""),t.textContent=e,document.head.appendChild(t)}function xt(e,t,n,r,i=!1){wn();let o=r||0;if(t){let s=i?e:e.firstElementChild||e;s.classList.add(t),s.style.animationName=t,s.style.animationFillMode="forwards",s.style.animationTimingFunction="ease",r&&(s.style.animationDuration=r+"ms");let a=!1,l,u=()=>{a||(a=!0,clearTimeout(l),s.removeEventListener("animationend",u),s.classList.remove(t),s.style.animationName="",s.style.animationDuration="",s.style.animationFillMode="",s.style.animationTimingFunction="")};return s.addEventListener("animationend",u,{once:!0}),l=setTimeout(u,o),()=>u()}if(n){let s=i?e:e.firstElementChild||e;s.classList.add(n+"-enter",n+"-enter-active");let a=!1,l,u=requestAnimationFrame(()=>{s.classList.remove(n+"-enter"),s.classList.add(n+"-enter-to");let d=()=>{a||(a=!0,clearTimeout(l),s.removeEventListener("transitionend",d),s.classList.remove(n+"-enter-active",n+"-enter-to"))};s.addEventListener("transitionend",d,{once:!0}),l=setTimeout(d,o)});return()=>{a=!0,cancelAnimationFrame(u),clearTimeout(l),s.classList.remove(n+"-enter",n+"-enter-active",n+"-enter-to")}}return()=>{}}function vn(e,t,n,r,i){wn();let o=i||0;if(!e.firstElementChild&&!e.childNodes.length)return r(),()=>{};if(t){let s=e.firstElementChild||e;s.classList.add(t),i&&(s.style.animationDuration=i+"ms");let a=!1,l=()=>{a||(a=!0,s.classList.remove(t),r())};s.addEventListener("animationend",l,{once:!0});let u=setTimeout(l,o);return()=>{a=!0,clearTimeout(u),s.removeEventListener("animationend",l)}}if(n){let s=e.firstElementChild||e;s.classList.add(n+"-leave",n+"-leave-active");let a=!1,l,u=requestAnimationFrame(()=>{s.classList.remove(n+"-leave"),s.classList.add(n+"-leave-to");let d=()=>{a||(a=!0,s.classList.remove(n+"-leave-active",n+"-leave-to"),r())};s.addEventListener("transitionend",d,{once:!0}),l=setTimeout(d,o)});return()=>{a=!0,cancelAnimationFrame(u),clearTimeout(l),s.classList.remove(n+"-leave",n+"-leave-active",n+"-leave-to")}}return r(),()=>{}}function vo(e,t){return e.replace(/\{([^}"'{][^}]*)\}/g,(n,r)=>{try{let i=R(r.trim(),t);return i!=null?String(i):""}catch{return""}})}function fi(e){return!e||typeof e!="string"?!1:e.startsWith("/")&&!e.startsWith("//")||e.startsWith("#")||e.startsWith(".")}var rr='
404
Page not found
',pi="forward";function Eo(e){let t=new Set;for(let n of e){let r=n.getAttribute("transition");r&&r!=="true"&&t.add(r)}return t.add(pi),[...t]}function di(){return typeof document<"u"&&typeof document.startViewTransition=="function"&&_.router.viewTransition!==!1}function En(){for(let e of document.querySelectorAll("[route-view]")){for(let t of[...e.children])He(t);e.innerHTML=""}}function mi(){let e=[],t=new Map,n={path:"",params:{},query:{},hash:""},r=new Set,i=new Map,o=[];function s(x){let h=e.find(w=>w.path===x);if(!h){let w=[],S=x.replace(/:(\w+)/g,(E,P)=>(w.push(P),"([^/]+)")),g=new RegExp("^"+S+"$");h={path:x,outlets:{},regex:g,paramNames:w},e.push(h)}return h}function a(x){let h={};return new URLSearchParams(x).forEach((w,S)=>{h[S]=w}),h}function l(x){for(let h of e){let w=x.match(h.regex);if(w){let S={},g=h.paramNames;for(let E=0;E=0?x.slice(S+1):"",E=S>=0?x.slice(0,S):x,[P,c=""]=E.split("?"),f=P.length>1?P.replace(/\/+$/,""):P;if(f===n.path&&g){if(n.hash="#"+g,_.router.useHash){let M="#"+x;h?window.location.replace(M):window.location.hash=x}else{let M=(_.router.base||"/").replace(/\/$/,"")+x;h?window.history.replaceState({},"",M):window.history.pushState({},"",M)}requestAnimationFrame(()=>{let M=document.getElementById(g);M&&M.scrollIntoView({behavior:"smooth"})}),jn(),r.forEach(M=>M(n));return}n={path:f,params:{},query:a(c),hash:g?"#"+g:""};let p=l(f);if(p){n.matched=!0,n.params=p.params;let M=p.route.outlets?.default,X=M?.getAttribute("guard"),W=M?.getAttribute("redirect");if(X){let ge=V({},null);if(ge.__raw.$store=ae,ge.__raw.$route=n,!R(X,ge)){W&&fi(W)?await u(W,!0):W?(b(`Route guard redirect blocked \u2014 "${W}" is not a relative path.`),En()):(b(`Route guard failed for "${x}" but no redirect is defined. The route will not render.`),En());return}}}else{n.matched=!1;let M=t.get("default");if(M){let X=M.getAttribute("guard"),W=M.getAttribute("redirect");if(X){let ge=V({},null);if(ge.__raw.$store=ae,ge.__raw.$route=n,!R(X,ge)){W&&fi(W)?await u(W,!0):W?(b(`Route guard redirect blocked \u2014 "${W}" is not a relative path.`),En()):(b(`Route guard failed for "${x}" but no redirect is defined. The route will not render.`),En());return}}}}let I=f+(c?"?"+c:"")+(g?"#"+g:"");if(_.router.useHash){let M="#"+I;h?window.location.replace(M):window.location.hash=I}else{let M=(_.router.base||"/").replace(/\/$/,"")+I;h?window.history.replaceState({},"",M):window.history.pushState({},"",M)}let N=n,J=document.querySelectorAll("[route-view]"),L=[...J].some(M=>M.getAttribute("transition"));if(di()&&L){wn();for(let W of J)if(W.getAttribute("transition")){let ge=(W.getAttribute("route-view")||"").trim();W.style.viewTransitionName=ge&&ge!=="default"?"route-content-"+ge:"route-content"}let M=Eo(J),X=document.startViewTransition({update:async()=>{await T(p,N)},types:M});X.finished.catch(W=>{W.name!=="AbortError"&&b("View transition failed:",W)}),X.ready?.catch(()=>{}),await X.updateCallbackDone}else await T(p,N);if(await v(),jn(),r.forEach(M=>M(n)),Te("route:navigate",{path:n.path,params:n.params,query:n.query,hash:n.hash}),n.hash){let M=n.hash.slice(1);requestAnimationFrame(()=>{let X=document.getElementById(M);X&&X.scrollIntoView({behavior:"smooth"})})}}function d(x,h){if(!document.head)return;let w=V({},null);w.__raw.$route=h,w.__raw.$store=ae;let S=x.getAttribute("page-title");if(S){let c=R(S,w);c!=null&&(document.title=String(c))}let g=x.getAttribute("page-description");if(g){let c=R(g,w);if(c!=null){let f=document.querySelector('meta[name="description"]');f||(f=document.createElement("meta"),f.name="description",document.head.appendChild(f)),f.content=String(c)}}let E=x.getAttribute("page-canonical");if(E){let c=R(E,w);if(c!=null){let f=document.querySelector('link[rel="canonical"]');f||(f=document.createElement("link"),f.rel="canonical",document.head.appendChild(f)),f.href=String(c)}}let P=x.getAttribute("page-jsonld");if(P){let c=document.querySelector('script[type="application/ld+json"][data-nojs]');c||(c=document.createElement("script"),c.type="application/ld+json",c.setAttribute("data-nojs",""),document.head.appendChild(c)),c.textContent=vo(P,w).replace(/<\//g,"<\\/")}}function m(x,h,w,S,g){let E=x+h+w,P=S+":"+E;if(i.has(P))return i.get(P);let c=document.createElement("template");return c.setAttribute("src",E),c.setAttribute("route",g),document.body.appendChild(c),i.set(P,c),c}async function y(x,h){let w=_.router.templates||"";if(!x.hasAttribute("src")&&!w)return null;let g=(x.getAttribute("src")||w).replace(/\/?$/,"/"),E=x.getAttribute("ext")||_.router.ext||".tpl",P=x.getAttribute("route-index")||"index";if(n.path==="/"){let L=P,M=m(g,L,E,h,n.path);return q("[ROUTER] File-based route:",n.path,"\u2192",g+L+E),x.hasAttribute("i18n-ns")&&!M.getAttribute("i18n-ns")&&M.setAttribute("i18n-ns",L),{tpl:M,remainingSegments:[],baseSrc:g,ext:E,indexName:P}}let c=n.path.replace(/^\//,"").split("/").filter(Boolean);if(c.length<=1){let L=c[0]||P,M=m(g,L,E,h,n.path);return q("[ROUTER] File-based route:",n.path,"\u2192",g+L+E),x.hasAttribute("i18n-ns")&&!M.getAttribute("i18n-ns")&&M.setAttribute("i18n-ns",L),{tpl:M,remainingSegments:[],baseSrc:g,ext:E,indexName:P}}let f=c[0],p=h+":layout:"+g+f+E,I;if(i.has(p)){let L=i.get(p);I=L&&!L.__loadFailed}else{let L=m(g,f,E,h,"/"+f);i.set(p,L),L.__srcLoaded||await Fe(L),I=!L.__loadFailed}if(I){let L=i.get(p)||m(g,f,E,h,"/"+f);return q("[ROUTER] Hierarchical layout:",n.path,"\u2192 layout",g+f+E,"remaining:",c.slice(1)),x.hasAttribute("i18n-ns")&&!L.getAttribute("i18n-ns")&&L.setAttribute("i18n-ns",f),{tpl:L,remainingSegments:c.slice(1),baseSrc:g,ext:E,indexName:P}}let N=c.join("/"),J=m(g,N,E,h,n.path);return q("[ROUTER] File-based route (flat fallback):",n.path,"\u2192",g+N+E),x.hasAttribute("i18n-ns")&&!J.getAttribute("i18n-ns")&&J.setAttribute("i18n-ns",N),{tpl:J,remainingSegments:[],baseSrc:g,ext:E,indexName:P}}async function v(){let x=document.querySelectorAll("[route-view][route-index]");for(let h of x){if(h.children.length>0)continue;let w=h.getAttribute("route-index");if(!w)continue;let S=(h.getAttribute("route-view")||"").trim()||"default",g=h.getAttribute("ext")||_.router.ext||".tpl",E=h.getAttribute("src");if(E&&E.startsWith("./")){let L=h.parentNode;for(;L;){if(L.__srcBase){E=L.__srcBase+E.slice(2);break}L=L.parentNode}E.startsWith("./")&&(E=E.slice(2))}let P=E?E.replace(/\/?$/,"/"):"",c=m(P,w,g,S,"/"+w);if(c.getAttribute("src")&&!c.__srcLoaded&&await Fe(c),c.__loadFailed)continue;c.removeAttribute("route"),q("[ROUTER] Nested index route:",P+w+g),h.hasAttribute("i18n-ns")&&!c.getAttribute("i18n-ns")&&c.setAttribute("i18n-ns",w),He(h),h.innerHTML="";let f=c.getAttribute("i18n-ns");if(f){let{_loadI18nNamespace:L}=await Promise.resolve().then(()=>(at(),on));await L(f)}let p=c.content.cloneNode(!0),I=V({$route:n},H(h)),N=document.createElement("div");N.style.display="contents",N.__ctx=I,c.content.__srcBase&&(N.__srcBase=c.content.__srcBase),N.appendChild(p),h.appendChild(N),Mt(N);let J=[...N.querySelectorAll("template[src]")];await Promise.all(J.map(Fe)),ze(N),ne(N)}}async function $(x,h,w,S,g,E){if(!h.length)return;let P=[...x.querySelectorAll("[route-view]")];if(!P.length)return;let c=h[0],f=h.slice(1);for(let p of P){let I=(p.getAttribute("route-view")||"").trim()||"default",N=p.getAttribute("src");if(N&&N.startsWith("./")){let Y=p.parentNode;for(;Y;){if(Y.__srcBase){N=Y.__srcBase+N.slice(2);break}Y=Y.parentNode}N.startsWith("./")&&(N=N.slice(2))}let J=N?N.replace(/\/?$/,"/"):w,L=p.getAttribute("ext")||S,M=I+":"+J+c;if(E.has(M)){b("[ROUTER] Infinite loop detected for nested outlet:",M);continue}E.add(M);let X,W=f;if(f.length>0){let Y=I+":layout:"+J+c+L;if(!i.has(Y)){let De=m(J,c,L,I,"/"+c);i.set(Y,De),De.__srcLoaded||await Fe(De)}let we=i.get(Y);if(we&&!we.__loadFailed)X=we,q("[ROUTER] Nested layout:",c,"\u2192",J+c+L);else{let De=[c,...f].join("/");X=m(J,De,L,I,n.path),W=[],q("[ROUTER] Nested flat fallback:",c,"\u2192",J+De+L)}}else X=m(J,c,L,I,n.path),q("[ROUTER] Nested leaf:",c,"\u2192",J+c+L);if(X.getAttribute("src")&&!X.__srcLoaded&&await Fe(X),X.__loadFailed){let Y=t.get(I)||(I!=="default"?t.get("default"):null);if(Y&&!Y.__loadFailed&&(X=Y,X.getAttribute("src")&&!X.__srcLoaded&&await Fe(X)),!X||X.__loadFailed){He(p),p.innerHTML=rr;continue}}He(p),p.innerHTML="";let ge=X.getAttribute("i18n-ns");if(ge){let{_loadI18nNamespace:Y}=await Promise.resolve().then(()=>(at(),on));await Y(ge)}let Oe=X.content.cloneNode(!0),U=V({$route:n},H(p)),ye=document.createElement("div");ye.style.display="contents",ye.__ctx=U,X.content.__srcBase&&(ye.__srcBase=X.content.__srcBase),ye.appendChild(Oe),p.appendChild(ye),Mt(ye);let O=[...ye.querySelectorAll("template[src]")];await Promise.all(O.map(Fe)),ze(ye),ne(ye),W.length>0&&await $(ye,W,J,L,g,E)}}async function T(x,h=n){let w=document.querySelectorAll("[route-view]");for(let g of w){if(!g.isConnected||g.parentElement&&g.parentElement.closest("[route-view]"))continue;let E=g.getAttribute("route-view"),P=E&&E.trim()!==""?E.trim():"default",c=x?.route?.outlets?.[P],f=[],p="",I="",N="index",J=_.router.templates||"";if(!c&&(g.hasAttribute("src")||J)){let L=await y(g,P);L&&(c=L.tpl,f=L.remainingSegments,p=L.baseSrc,I=L.ext,N=L.indexName)}if((!c||c.__loadFailed)&&(!x||c?.__loadFailed)){let L=t.get(P)||(P!=="default"?t.get("default"):null);L&&(c=L)}for(let L of[...g.children])He(L);if(g.innerHTML="",c&&!c.__loadFailed){if(c.getAttribute("src")&&!c.__srcLoaded&&(q("Loading route template on demand:",c.getAttribute("src")),await Fe(c)),c.__loadFailed){let U=t.get(P)||(P!=="default"?t.get("default"):null);if(U&&!U.__loadFailed&&(c=U,c.getAttribute("src")&&!c.__srcLoaded&&await Fe(c)),!c||c.__loadFailed){g.innerHTML=rr;continue}}let L=c.getAttribute("i18n-ns");if(L){let{_loadI18nNamespace:U}=await Promise.resolve().then(()=>(at(),on));await U(L)}let M=c.content.cloneNode(!0),X=V({$route:h},H(g)),W=document.createElement("div");W.style.display="contents",W.__ctx=X,c.content.__srcBase&&(W.__srcBase=c.content.__srcBase),W.appendChild(M),g.appendChild(W),Mt(W);let ge=[...W.querySelectorAll("template[src]")];q("[ROUTER] nested templates found in wrapper:",ge.length,ge.map(U=>U.getAttribute("src")+(U.__srcLoaded?"[LOADED]":"[NEW]"))),await Promise.all(ge.map(Fe)),q("[ROUTER] all nested loads done for route:",h.path);let Oe=g.getAttribute("transition");if(Oe&&!di()&&(xt(W,null,Oe),b("Class-based route transitions are deprecated. The View Transition API is now used by default. Set router.viewTransition to false to keep legacy behavior.")),ze(W),ne(W),f.length>0&&await $(W,f,p,I,N,new Set),P==="default"){let U=c.getAttribute("page-title");if(U){let ye=V({},null);ye.__raw.$route=h,ye.__raw.$store=ae;let O=R(U,ye);O!=null&&(document.title=String(O))}}P==="default"&&d(c,h),P==="default"&&_.router.focusBehavior==="auto"&&requestAnimationFrame(()=>{let U=g.querySelector("[autofocus]")||g.querySelector('[tabindex="-1"]')||g.querySelector("h1")||g;U.hasAttribute("tabindex")||U.setAttribute("tabindex","-1"),U.focus({preventScroll:!0})})}else(!x||c?.__loadFailed)&&(g.innerHTML=rr)}document.querySelectorAll("[route]").forEach(g=>{let E=g.getAttribute("route"),P=g.getAttribute("route-active")||"active",c=g.getAttribute("route-active-exact");if(c)c.split(/\s+/).filter(Boolean).forEach(f=>g.classList.toggle(f,h.path===E));else if(P&&!g.hasAttribute("route-active-exact")){let f=h.path,p=E==="/"?f==="/":f===E||f.startsWith(E.replace(/\/$/,"")+"/");P.split(/\s+/).filter(Boolean).forEach(I=>g.classList.toggle(I,p))}});let S=_.router.scrollBehavior;S==="top"?window.scrollTo(0,0):S==="smooth"&&window.scrollTo({top:0,behavior:"smooth"})}function C(x,h){h.scrollIntoView({behavior:"smooth"});let w='a[href="#'+x+'"]';document.querySelectorAll('a[href^="#"]').forEach(S=>{S.hasAttribute("route")||S.classList.toggle("active",S.matches(w))})}function z(){let x=document.querySelectorAll("[route-view]");for(let h of x){if(h.parentElement&&h.parentElement.closest("[route-view]"))continue;let w=h.getAttribute("src")||_.router.templates||"";if(!w)continue;let S=w.replace(/\/?$/,"/"),g=h.getAttribute("ext")||_.router.ext||".tpl",E=h.getAttribute("route-index")||"index",P=(h.getAttribute("route-view")||"").trim()||"default",c=new Map;document.querySelectorAll("[route]:not([route-view])").forEach(I=>{let N=I.getAttribute("route");if(!N)return;let J=N.split("?")[0].split("#")[0],L=I.getAttribute("lazy"),M=c.get(J);(!c.has(J)||L==="priority"||M==="ondemand"&&L!=="ondemand")&&c.set(J,L)});let f=[],p=[];for(let[I,N]of c){if(N==="ondemand"||I===n.path||I==="*")continue;let J=I==="/"?E:I.replace(/^\//,""),L=I==="/"?[]:I.replace(/^\//,"").split("/").filter(Boolean);if(L.length>1){let ge=L[0],Oe=P+":layout:"+S+ge+g,U=i.get(Oe);if(U&&!U.__loadFailed){let ye=L.join("/"),O=m(S,ye,g,P,I);O.__srcLoaded||(q("[ROUTER] Prefetch (layout child):",I,"\u2192",S+ye+g,N==="priority"?"(priority)":"(background)"),h.hasAttribute("i18n-ns")&&O.setAttribute("i18n-ns",ye),N==="priority"?f.push(O):p.push(O));continue}}let M=S+J+g,X=P+":"+M;if(i.has(X))continue;let W=m(S,J,g,P,I);q("[ROUTER] Prefetch:",I,"\u2192",M,N==="priority"?"(priority)":"(background)"),h.hasAttribute("i18n-ns")&&W.setAttribute("i18n-ns",J),N==="priority"?f.push(W):p.push(W)}(f.length||p.length)&&Promise.all(f.map(Fe)).then(()=>{p.forEach(Fe)})}}return{get current(){return n},push(x){return u(x)},replace(x){return u(x,!0)},back(){window.history.back()},forward(){window.history.forward()},on(x){return r.add(x),()=>r.delete(x)},register(x,h,w="default"){if(x==="*"){t.set(w,h);return}let S=s(x);S.outlets[w]=h},async init(){_.router.useHash&&!_.router.suppressHashWarning&&b("Router is running in hash mode (useHash: true). URLs like /#/about are not indexed as separate pages by search engines. Use useHash: false with a server-side SPA fallback (try_files) for SEO-friendly routing. See: https://github.com/no-js-dev/nojs/blob/main/docs/md/routing.md#deployment"),document.querySelectorAll("template[route]").forEach(h=>{let w=h.getAttribute("route"),S=h.getAttribute("outlet")||"default";if(w==="*"){t.set(S,h);return}let g=s(w);g.outlets[S]=h});let x=h=>{let w=h.target.closest("[route]");if(w&&!w.hasAttribute("route-view")){h.preventDefault();let g=w.getAttribute("route");u(g);return}let S=h.target.closest('a[href^="#"]');if(S&&!S.hasAttribute("route")){let E=S.getAttribute("href").slice(1);if(E&&!E.startsWith("/")){let P=document.getElementById(E);P&&(h.preventDefault(),C(E,P),_.router.useHash||window.history.replaceState(null,"","#"+E))}}};if(document.addEventListener("click",x),o.push(()=>document.removeEventListener("click",x)),_.router.useHash){let h=()=>{let S=window.location.hash.slice(1)||"/";if(!S.startsWith("/")){let E=document.getElementById(S);E&&(C(S,E),window.history.replaceState(null,"","#"+n.path));return}let[g]=S.split("?");g!==n.path&&u(S,!0,!0)};window.addEventListener("hashchange",h),o.push(()=>window.removeEventListener("hashchange",h));let w=window.location.hash.slice(1)||"/";await u(w,!0)}else{let h=()=>{let S=Ht(window.location.pathname);if(S===n.path){let g=window.location.hash.slice(1);if(g){let E=document.getElementById(g);E&&C(g,E)}return}u(S,!0,!0)};window.addEventListener("popstate",h),o.push(()=>window.removeEventListener("popstate",h));let w=Ht(window.location.pathname);await u(w,!0)}z()},destroy(){o.forEach(x=>x()),o.length=0,r.clear()}}}he();ee.uppercase=e=>String(e??"").toUpperCase();ee.lowercase=e=>String(e??"").toLowerCase();ee.capitalize=e=>String(e??"").replace(/\b\w/g,t=>t.toUpperCase());ee.truncate=(e,t=100)=>{let n=String(e??"");return n.length>t?n.slice(0,t)+"...":n};ee.trim=e=>String(e??"").trim();ee.stripHtml=e=>String(e??"").replace(/<[^>]*>/g,"");ee.slugify=e=>String(e??"").toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"");ee.nl2br=e=>String(e??"").replace(/&/g,"&").replace(//g,">").replace(/\n/g," ");ee.encodeUri=e=>encodeURIComponent(String(e??""));var hi=(e,t=0)=>{let n=Math.floor(Number(e));return isNaN(n)?t:n<0?0:n>100?100:n};ee.number=(e,t=0)=>{let n=Number(e);if(isNaN(n))return e;let r=hi(t);return n.toLocaleString(void 0,{minimumFractionDigits:r,maximumFractionDigits:r})};ee.currency=(e,t="USD")=>{let n=Number(e);if(isNaN(n))return e;try{return n.toLocaleString(void 0,{style:"currency",currency:t})}catch{return`${t} ${n.toFixed(2)}`}};ee.percent=(e,t=0)=>{let n=Number(e);return isNaN(n)?e:(n*100).toFixed(hi(t))+"%"};ee.filesize=e=>{let t=Number(e);if(isNaN(t))return e;let n=["B","KB","MB","GB","TB"],r=t<0?"-":"",i=0,o=Math.abs(t);for(;o>=1024&&i0?1:0)+" "+n[i]};ee.ordinal=e=>{let t=Number(e);if(isNaN(t))return e;let n=["th","st","nd","rd"],r=t%100;return t+(n[(r-20)%10]||n[r]||n[0])};ee.count=e=>Array.isArray(e)?e.length:0;ee.first=e=>Array.isArray(e)?e[0]:e;ee.last=e=>Array.isArray(e)?e[e.length-1]:e;ee.join=(e,t=", ")=>Array.isArray(e)?e.join(t):e;ee.reverse=e=>Array.isArray(e)?[...e].reverse():e;ee.unique=e=>Array.isArray(e)?[...new Set(e)]:e;ee.pluck=(e,t)=>Array.isArray(e)?e.map(n=>n?.[t]):e;ee.sortBy=(e,t)=>{if(!Array.isArray(e))return e;let n=t?.startsWith("-"),r=n?t.slice(1):t,i=o=>o==null||typeof o=="number"&&isNaN(o);return[...e].sort((o,s)=>{let a=o?.[r],l=s?.[r],u=i(a),d=i(l);if(u&&d)return 0;if(u)return 1;if(d)return-1;let m=al?1:0;return n?-m:m})};ee.where=(e,t,n)=>Array.isArray(e)?e.filter(r=>r?.[t]===n):e;var So=/^\d{4}-\d{2}-\d{2}$/,Sn=e=>typeof e=="string"&&So.test(e)?new Date(e+"T00:00:00"):new Date(e);ee.date=(e,t="short")=>{let n=Sn(e);if(isNaN(n))return e;let r=t==="long"?{dateStyle:"long"}:t==="full"?{dateStyle:"full"}:{dateStyle:"short"};return n.toLocaleDateString(void 0,r)};ee.datetime=e=>{let t=Sn(e);return isNaN(t)?e:t.toLocaleString()};ee.relative=e=>{let t=Sn(e);if(isNaN(t))return e;let n=(Date.now()-t.getTime())/1e3;return n<0?ee.fromNow(e):n<60?"just now":n<3600?Math.floor(n/60)+"m ago":n<86400?Math.floor(n/3600)+"h ago":n<2592e3?Math.floor(n/86400)+"d ago":t.toLocaleDateString()};ee.fromNow=e=>{let t=Sn(e);if(isNaN(t))return e;let n=(t.getTime()-Date.now())/1e3;return n<0?ee.relative(e):n<60?"in a moment":n<3600?"in "+Math.floor(n/60)+"m":n<86400?"in "+Math.floor(n/3600)+"h":"in "+Math.floor(n/86400)+"d"};ee.default=(e,t="")=>e==null||e===""?t:e;ee.json=(e,t=2)=>JSON.stringify(e,null,t);ee.debug=e=>(console.log("[No.JS debug]",e),e);ee.keys=e=>e&&typeof e=="object"?Object.keys(e):[];ee.values=e=>e&&typeof e=="object"?Object.values(e):[];he();he();var gi=["foreach","each","for","from","filter","sort","limit","offset","key","index","template","animate-enter","animate-leave","animate-stagger","animate-duration","animate","get","post","put","patch","delete","query","sse","page-title","page-description","page-canonical","page-jsonld","else-if"];function Ao(e,t){for(let n=0;n>1;e[t[u]]0&&(n[o]=t[a-1]),t[a]=o}let r=new Set,i=t.length?t[t.length-1]:-1;for(;i!==-1;)r.add(i),i=n[i];return r}function ir(e,t,n,r){t&&(e.classList.add(t),e.addEventListener("animationend",()=>e.classList.remove(t),{once:!0}),n&&(e.style.animationDelay=r*n+"ms"))}var or={priority:10,init(e,t,n){let r=H(e),i,o,s=n.match(/^(\w+)\s+in\s+(\S+)$/);if(s)[,i,o]=s;else{let O=e.getAttribute("from");if(!O||!/^\w+$/.test(n))return;i=n,o=O,console.warn(`[NoJS] "${t}" with "from" is deprecated. Use ${t}="${i} in ${o}" instead.`)}let a=e.getAttribute("index")||"$index",l=e.hasAttribute("if")||e.hasAttribute("else-if"),u=l?null:e.getAttribute("else"),d=e.getAttribute("filter"),m=e.getAttribute("sort"),y=parseInt(e.getAttribute("limit"))||1/0,v=parseInt(e.getAttribute("offset"))||0,$=e.getAttribute("template"),T=e.getAttribute("key"),C=e.getAttribute("animate-enter")||e.getAttribute("animate"),z=e.getAttribute("animate-leave"),re=parseInt(e.getAttribute("animate-stagger"))||0,x=parseInt(e.getAttribute("animate-duration"))||0,h=null,w=null,S=!1,g=!1,E=!!(d||m||v),P=e.parentNode,c=document.createComment("foreach"),f=document.createComment("/foreach");P.insertBefore(c,e),P.insertBefore(f,e.nextSibling),P.removeChild(e);let p=new Map,I=null,N=null,J=0,L=-1;function M(){if(I&&(N.registryVersion!==Gt&&(N=nr(I)),!$||L===J))return;L=J;let O=$?document.getElementById($):null;I=e.cloneNode(!0),Ao(I,!l),O&&(I.innerHTML="",I.appendChild(O.content.cloneNode(!0))),N=nr(I)}function X(O){M();let Y=I.cloneNode(!0);return Y.__ctx=V(O,r),Y}function W(O){ai(O,N)}function ge(){J++;let O=/[\[\]()\s+\-*\/!?:&|]/.test(o)?R(o,r):jt(o,r);Array.isArray(O)||(O=[]);let Y=An(c,f);if(!S&&O===h&&O.length>0&&Y.length>0){for(let G of Y)G.__ctx&&G.__ctx.$notify&&G.__ctx.$notify();return}if(h=O,d){let G={[i]:void 0,[a]:0},ie=V(G,r),xe=ie.__raw;O=O.filter((Z,k)=>(xe[i]=Z,xe[a]=k,delete xe.__collectKeysCache,!!R(d,ie)))}if(m){let G=m.startsWith("-"),ie=G?m.slice(1):m;O=[...O].sort((xe,Z)=>{let k=jt(ie,xe)??xe?.[ie],Pe=jt(ie,Z)??Z?.[ie],K=k==null||typeof k=="number"&&Number.isNaN(k),B=Pe==null||typeof Pe=="number"&&Number.isNaN(Pe);if(K||B)return K&&B?0:K?1:-1;let ce=kPe?1:0;return G?-ce:ce})}if(O=O.slice(v,v+y),O.length===0&&u){if(S){w=null;return}Xt(c,f),p.clear();let G=_e(u);if(G){P.insertBefore(G,f);let ie=c.nextSibling;for(;ie&&ie!==f;)ie.nodeType===1&&ne(ie),ie=ie.nextSibling;S=!0}else g||(g=!0,b(`${t}: else template "${u}" not found`,e));w=null;return}if(O.length===0){Xt(c,f),p.clear(),S=!1,w=null;return}if(S=!1,T){U(O,O.length),w=null;return}function we(){if(E||!w)return!1;let G=w.length,ie=O.length;if(ie<=G)return!1;let xe=An(c,f);if(xe.length!==G)return!1;for(let K=0;K0){let G=Ne.length;Ne.forEach(ie=>{ie.classList.add(z);let xe=()=>{ie.classList.remove(z),G--,G<=0&&De()};ie.addEventListener("animationend",xe,{once:!0}),setTimeout(xe,x||0)})}else we()||De()}let Oe=null;if(T){let O=T.match(/^(\w+)\.(\w+)$/);O&&O[1]===i&&!["__proto__","constructor","prototype"].includes(O[2])&&(Oe=O[2])}function U(O,Y){p.size===0&&Xt(c,f);let we;if(Oe){we=new Array(O.length);for(let B=0;B(ve[i]=tt,ve[a]=Ae,delete ve.__collectKeysCache,{key:R(T,ce),item:tt,i:Ae}))}let De=new Set(we.map(B=>B.key)),Ne=!1;for(let B of p.keys())if(De.has(B)){Ne=!0;break}if(!Ne&&p.size>0)Xt(c,f),p.clear();else for(let[B,ce]of p)De.has(B)||(He(ce,!0),ce.remove(),p.delete(B));let G=null,ie=null;if(we.forEach(({key:B,item:ce,i:ve})=>{let tt=p.get(B);if(tt){let Ae=tt.__ctx.__raw;(Ae[i]!==ce||Ae[a]!==ve||Ae.$count!==Y)&&(Ae[i]=ce,Ae[a]=ve,Ae.$index=ve,Ae.$count=Y,Ae.$first=ve===0,Ae.$last=ve===Y-1,Ae.$even=ve%2===0,Ae.$odd=ve%2!==0,delete Ae.__collectKeysCache),tt.__ctx.$notify()}else{let Ae=X({[i]:ce,[a]:ve,$index:ve,$count:Y,$first:ve===0,$last:ve===Y-1,$even:ve%2===0,$odd:ve%2!==0});p.set(B,Ae),G||(G=document.createDocumentFragment(),ie=[]),G.appendChild(Ae),ie.push({clone:Ae,i:ve})}}),G){P.insertBefore(G,f);for(let B=0;B=0;B--){let ce=p.get(we[B].key);!Pe.has(B)&&ce.nextSibling!==K&&P.insertBefore(ce,K),K=ce}}let ye=ue;lt(P),de(o,r,ge),ge(),lt(ye)}};F("foreach",or);F("each",or);F("for",or);F("state",{priority:0,init(e,t,n){let r=R(n,V())||{},o=e.__ctx||(e.parentElement?H(e.parentElement):null),s=V(r,o);e.__ctx=s;let a=e.getAttribute("persist"),l=e.getAttribute("persist-key");if(a&&!l){b(`persist="${a}" requires a persist-key attribute. State will not be persisted.`);return}if(a&&l){let u=a==="localStorage"?localStorage:a==="sessionStorage"?sessionStorage:null;if(u){let d=e.getAttribute("persist-fields"),m=d?new Set(d.split(",").map(z=>z.trim())):null,y="nojs_"+(_.appId||"")+"state_"+l;try{let z=u.getItem(y);if(z){let re=JSON.parse(z),x=e.hasAttribute("persist-schema"),h=new Set(["__proto__","constructor","prototype"]);for(let[w,S]of Object.entries(re))if(!h.has(w)&&!w.startsWith("__")&&(!m||m.has(w))){if(x){if(!(w in r)){b('persist-schema: ignoring unknown key "'+w+'"');continue}if(r[w]!==null&&S!==null&&typeof S!=typeof r[w]){b('persist-schema: type mismatch for "'+w+'" (expected '+typeof r[w]+", got "+typeof S+")");continue}}s.$set(w,S)}}}catch{}let v=["token","password","secret","key","auth","credential","session"],T=Object.keys(r).filter(z=>v.some(re=>z.toLowerCase().includes(re)));T.length>0&&b("State key(s) "+T.map(z=>'"'+z+'"').join(", ")+" may contain sensitive data. Consider using persist-fields to exclude them.");let C=s.$watch(()=>{try{let z=s.__raw,re=Object.fromEntries(Object.entries(z).filter(([x])=>!x.startsWith("__")&&(!m||m.has(x))));u.setItem(y,JSON.stringify(re))}catch{}});j(()=>{C&&C()})}}q("state",r)}});F("store",{priority:0,init(e,t,n){let r=e.getAttribute("value");if(n){if(!ae[n]){let i=r?R(r,V())||{}:{};ae[n]=V(i),Te("store:created",{name:n,keys:Object.keys(i)})}q("store",n)}}});F("computed",{priority:2,gated:!0,init(e,t,n){if(Be(e))return;let r=e.getAttribute("expr");if(!n||!r)return;let i=H(e);function o(){let s=R(r,i);i.$set(n,s)}de(r,i,o),o()}});F("watch",{priority:2,gated:!0,init(e,t,n){if(Be(e))return;let r=H(e),i=e.getAttribute("on:change"),o=R(n,r);de(n,r,()=>{let s=R(n,r);if(s!==o){let a=o;o=s,i&&Le(i,r,{$old:a,$new:s})}})}});he();var Co=["get","post","put","patch","delete","query"];function _i(){let e=document.createElement("div");return e.setAttribute("data-nojs-sentinel",""),e.setAttribute("aria-hidden","true"),e.style.height="0",e.style.overflow="hidden",e.style.pointerEvents="none",e}function xn(e){let t=e.parentElement;for(;t&&t!==document.documentElement;){let n=getComputedStyle(t).overflowY;if(n==="scroll"||n==="auto")return t;t=t.parentElement}return document.documentElement}for(let e of Co)F(e,{priority:1,gated:!0,init(t,n,r){if(Be(t)){b(`${n}: HTTP verb directive on a loop element is not supported \u2014 move the fetch to a parent or child element`,t);return}let i=t.getAttribute("as")||"data",o=t.getAttribute("loading"),s=t.getAttribute("error"),a=t.getAttribute("empty"),l=t.getAttribute("success"),u=t.getAttribute("then"),d=t.getAttribute("redirect"),m=t.getAttribute("confirm"),y=250,v=parseInt(t.getAttribute("refresh"))||0,$=v>0?Math.max(v,y):0,T=t.getAttribute("cached"),C=t.hasAttribute("cached")?T||"memory":"none",z=t.getAttribute("body"),re=t.getAttribute("headers"),x=t.getAttribute("var"),h=t.getAttribute("into"),w=parseInt(t.getAttribute("retry"))||_.retries,S=parseInt(t.getAttribute("retry-delay"))||_.retryDelay||1e3,g=t.getAttribute("params"),E=t.getAttribute("skeleton"),P=e==="query"?"query":"get",c=t.getAttribute(`${P}-trigger`),f=t.getAttribute(`${P}-trigger-label`)||"Load More",p=e==="get"||e==="query"&&t.tagName!=="FORM",I=e==="get"||e==="query",N=e==="get"&&t.hasAttribute("get-cursor"),J=N&&t.getAttribute("get-cursor-field")||null,L=e==="get"&&t.hasAttribute("get-page"),M=L?parseInt(t.getAttribute("get-page"),10)||1:null;N&&L&&b("get-cursor and get-page are mutually exclusive; using cursor-based pagination");let W=t.getAttribute(`${P}-threshold`)||(c==="scroll"?"200px":"0px"),ge=e==="get"?t.getAttribute("get-insert"):null,Oe=e==="get"&&t.hasAttribute("get-insert")?ge==="prepend"?"prepend":"append":"replace",U=Oe!=="replace",ye=t.parentElement?H(t.parentElement):V(),O=t.__ctx||V({},ye);t.__ctx=O,N?O.$set("cursor",""):L&&O.$set("page",M);let Y=c==="scroll"||c==="button",we=!1,De=!1,Ne=null,G=null;Y&&!U&&b(`get-trigger="${c}" requires get-insert to be set. Falling back to "${c==="scroll"?"visible":"immediate"}" behavior.`);let ie=Y&&U&&$>0;ie&&b(`get-trigger="${c}" is mutually exclusive with refresh. The refresh interval is being ignored.`);let xe=[...t.childNodes].map(A=>A.cloneNode(!0)),Z=null;j(()=>{Z&&Z.abort(),Z=null});let k=null,Pe=0,K=!0;function B(){k&&k.parentNode&&k.parentNode.removeChild(k);let A=++Pe;k=_i(),Oe==="append"?t.appendChild(k):t.insertBefore(k,t.firstChild),j(()=>{A===Pe&&(k&&k.parentNode&&k.parentNode.removeChild(k),k=null)})}U&&B();function ce(){if(!E)return;let A=document.getElementById(E);A&&A.style.removeProperty("display")}function ve(){if(!E)return;let A=document.getElementById(E);A&&(A.style.display="none")}function tt(){if(t.tagName!=="FORM")return;if(t.__nojsResetSubmitting){t.__nojsResetSubmitting();return}let A=H(t),D=A?.$form;D&&(D.submitting=!1,A.$set("$form",{...D}))}function Ae(){for(let A of[...t.children])He(A);k&&k.parentNode&&k.parentNode.removeChild(k),k=null,t.innerHTML="",K=!0,B()}function Ei(A,D){let oe=D.split("."),Ie=A;for(let Q of oe){if(Ie==null||typeof Ie!="object")return;if(Me[Q]){b("Blocked access to forbidden property '"+Q+"' in get-cursor-field");return}Ie=Ie[Q]}return Ie}let Si=["cursor","next_cursor","nextCursor","next"];function Ai(A,D){if(D&&D.headers){let oe=D.headers.get("X-NoJS-Cursor");if(oe!=null)return oe||null}if(A!=null&&typeof A=="object"&&!Array.isArray(A)){if(J){let oe=Ei(A,J);return oe!==void 0&&oe||null}for(let oe of Si){let Ie=A[oe];if(Ie!=null&&Ie!=="")return Ie}}return null}function xi(A){if(Array.isArray(A))return A;if(A!=null&&typeof A=="object"){for(let D of Object.keys(A))if(Array.isArray(A[D]))return A[D]}return A}async function Ee(){if(Y&&U&&we)return;Z&&Z.abort(),Z=new AbortController;let A=Z;if(m&&!window.confirm(m)){tt();return}Y&&U&&(we=!0),c==="button"&&U&&!K&&Zt(),ce();let D=Ue(r,O);if(g){let Q=R(g,O);if(Q&&typeof Q=="object"){let Ce=new URLSearchParams;for(let[$e,ke]of Object.entries(Q))if(ke!=null)if(Array.isArray(ke))for(let Re of ke)Re!=null&&Ce.append($e,typeof Re=="object"?JSON.stringify(Re):String(Re));else typeof ke=="object"?Ce.append($e,JSON.stringify(ke)):Ce.append($e,String(ke));let Se=Ce.toString();if(Se){let $e=D.includes("?")?"&":"?";D+=$e+Se}}}let oe="";e==="query"&&(t.tagName==="FORM"?oe=JSON.stringify(Object.fromEntries(new FormData(t).entries())):z&&(oe=Ue(z,O)));let Ie=e+":"+D+(oe?":"+oe:"");if(I){let Q=Gr(Ie,C);if(Q!=null){ve(),O.$set(i,Q),ze(t),ne(t);return}}if(o){let Q=_e(o);Q&&(U&&!K?Ci(Q):(se(t),t.innerHTML="",t.appendChild(Q),ne(t)))}try{let Q=null;if(z){let be=Ue(z,O);try{Q=JSON.parse(be)}catch{Q=be}}if(t.tagName==="FORM"){let be=new FormData(t);Q=Object.fromEntries(be.entries())}let Ce=re?JSON.parse(re):{};if(re)for(let be of Object.keys(Ce)){let Ct=be.toLowerCase();(Lt.has(Ct)||/^x-(auth|api)-/.test(Ct))&&b(`Sensitive header "${be}" is set inline on a headers attribute. Use NoJS.config({ headers }) or an interceptor to avoid exposing credentials in HTML source.`)}let Se={},$e=await _n(D,e,Q,Ce,t,Z.signal,w,S,Se),ke=null,Re=$e;N&&(ke=Ai($e,Se),(Se.headers&&Se.headers.get("X-NoJS-Cursor"))==null&&(Re=xi($e))),I&&Xr(Ie,N?Re:$e,C);let We=N?Re:$e,fr=We==null||Array.isArray(We)&&We.length===0||We==="",dr=Se.headers&&Se.headers.get("X-NoJS-Last-Page")==="true",pr=N&&ke==null,qo=fr||dr||pr;if(fr){if(ve(),N&&O.$set("cursor",""),Y&&U&&cr(),a){let be=_e(a);be&&(U&&!K?(Ln(),Yt(be),ne(be)):(se(t),t.innerHTML="",t.appendChild(be),ne(t)))}return}if(ve(),N&&O.$set("cursor",ke||""),U&&!K){Ln();let be=O[i];if(Array.isArray(be)&&Array.isArray(We)){let Ct=Oe==="append"?[...be,...We]:[...We,...be];if(Oe==="prepend"){let Tt=xn(t),Nn=Tt.scrollTop,mt=Tt.scrollHeight;O.$set(i,Ct);let Oi=Tt.scrollHeight;Tt.scrollTop=Nn+(Oi-mt)}else O.$set(i,Ct)}else O.$set(i,We);K=!1}else O.$set(i,We);if(h&&(ae[h]||(ae[h]=V({})),ae[h].$set(i,We),Ge(h)),!U||K){if(l){let be=_e(l);if(be){se(t),t.innerHTML="";let Tt=document.getElementById(l.replace("#",""))?.getAttribute("var")||x||"result",Nn=V({[Tt]:We},O),mt=document.createElement("div");mt.style.display="contents",mt.__ctx=Nn,mt.appendChild(be),t.appendChild(mt),ne(mt)}}else{se(t),t.innerHTML="";for(let be of xe)t.appendChild(be.cloneNode(!0));ze(t),ne(t)}if(U){K=!1,k&&k.parentNode&&k.parentNode.removeChild(k);let be=++Pe;k=_i(),Oe==="append"?t.appendChild(k):t.insertBefore(k,t.firstChild),j(()=>{be===Pe&&(k&&k.parentNode&&k.parentNode.removeChild(k),k=null)})}}L&&!N&&Y&&U&&O.$set("page",O.page+1),(dr||pr)&&Y&&U?cr():Y&&U&&!De&&Li(),u&&Le(u,O,{result:We}),d&&me&&me.push(d),Xe("fetch:success",{url:D,data:We}),Te("fetch:success",{method:e,url:D})}catch(Q){if(Q.name==="AbortError")return;if(ve(),b(`${e.toUpperCase()} ${D} failed:`,Q.message),Xe("fetch:error",{url:D,error:Q}),Xe("error",{url:D,error:Q}),Te("fetch:error",{method:e,url:D,error:Q.message}),s){let Ce=_e(s);if(Ce){let $e=document.getElementById(s.replace("#",""))?.getAttribute("var")||"err",ke=V({[$e]:{message:Q.message,status:Q.status,body:Q.body}},O),Re=document.createElement("div");Re.style.display="contents",Re.__ctx=ke,Re.appendChild(Ce),U&&!K?(Ln(),Ti(),Re.setAttribute("data-nojs-inline-error",""),Yt(Re)):(se(t),t.innerHTML="",t.appendChild(Re)),ne(Re)}}}finally{Y&&U&&(we=!1),t.tagName==="FORM"&&e!=="get"&&!A.signal.aborted&&Z===A&&tt()}}function Ci(A){let D=document.createElement("div");D.style.display="contents",D.setAttribute("data-nojs-inline-loading",""),D.appendChild(A),Yt(D),ne(D)}function Ln(){let A=t.querySelector("[data-nojs-inline-loading]");A&&(He(A),A.parentNode.removeChild(A))}function Ti(){let A=t.querySelector("[data-nojs-inline-error]");A&&(He(A),A.parentNode.removeChild(A))}function Yt(A){Oe==="append"&&k?t.insertBefore(A,k):Oe==="prepend"&&k&&k.nextSibling?t.insertBefore(A,k.nextSibling):t.appendChild(A)}function cr(){De=!0,Ne&&(Ne.disconnect(),Ne=null),Zt(),k&&k.parentNode&&k.parentNode.removeChild(k),k=null,Xe("fetch:end",{url:r})}function Li(){c==="button"&&lr(),c==="scroll"&&(Ne?k&&Ne.observe(k):ur())}function lr(){Zt();let A=document.createElement("button");A.setAttribute("data-nojs-load-more",""),A.setAttribute("type","button"),A.setAttribute("aria-label",f),A.textContent=f,G=A;let D=()=>{De||we||Ee()};A.addEventListener("click",D),j(()=>{A.removeEventListener("click",D)}),Yt(A)}function Zt(){G&&G.parentNode&&G.parentNode.removeChild(G),G=null;let A=t.querySelector("[data-nojs-load-more]");A&&A.parentNode&&A.parentNode.removeChild(A)}function ur(){if(typeof IntersectionObserver>"u"){b('IntersectionObserver not available, get-trigger="scroll" falling back to button trigger'),lr();return}let A=xn(t);Ne=new IntersectionObserver(D=>{for(let oe of D)if(oe.isIntersecting&&t.isConnected&&!De&&!we){Ee();break}},{root:A===document.documentElement?null:A,rootMargin:W}),k&&Ne.observe(k),j(()=>{Ne&&(Ne.disconnect(),Ne=null)})}function On(){De=!1,we=!1,N?O.$set("cursor",""):L&&O.$set("page",M),Zt(),Ae(),c==="scroll"&&k&&(Ne&&Ne.disconnect(),Ne=null,ur())}if(t.tagName==="FORM"&&e!=="get"){let A=D=>{D.preventDefault(),Ee()};t.addEventListener("submit",A),j(()=>t.removeEventListener("submit",A))}else if(p){if(c!=="none")if(c==="visible")if(typeof IntersectionObserver<"u"){let A=xn(t),D=new IntersectionObserver(oe=>{for(let Ie of oe)if(Ie.isIntersecting&&t.isConnected){D.disconnect(),Ee();break}},{root:A===document.documentElement?null:A,rootMargin:W});D.observe(t),j(()=>D.disconnect())}else b('IntersectionObserver not available, get-trigger="visible" falling back to immediate fetch'),queueMicrotask(()=>{t.isConnected&&Ee()});else if(c==="scroll"&&U)queueMicrotask(()=>{t.isConnected&&Ee()});else if(c==="scroll"&&!U)if(typeof IntersectionObserver<"u"){let A=xn(t),D=new IntersectionObserver(oe=>{for(let Ie of oe)if(Ie.isIntersecting&&t.isConnected){D.disconnect(),Ee();break}},{root:A===document.documentElement?null:A,rootMargin:W});D.observe(t),j(()=>D.disconnect())}else queueMicrotask(()=>{t.isConnected&&Ee()});else if(c==="button"&&U)queueMicrotask(()=>{t.isConnected&&Ee()});else if(c==="button"&&!U)queueMicrotask(()=>{t.isConnected&&Ee()});else if(c==="hover"){let A=$<=0,D=()=>{Ee()};A?t.addEventListener("mouseenter",D,{once:!0}):t.addEventListener("mouseenter",D),j(()=>t.removeEventListener("mouseenter",D))}else queueMicrotask(()=>{t.isConnected&&Ee()})}else{let A=D=>{D.preventDefault(),Ee()};t.addEventListener("click",A),j(()=>t.removeEventListener("click",A))}if(/\{[^}]+\}/.test(r)&&c!=="none"){let Se=function(){let ke=Ue(r,O);if(ke!==D){if(D=ke,Y&&U&&Q){let Re=Ue(Q,O);if(Re!==Ce){Ce=Re,oe&&clearTimeout(oe),A>0?oe=setTimeout(()=>{On(),Ee()},A):(On(),Ee());return}}oe&&clearTimeout(oe),A>0?oe=setTimeout(Ee,A):Ee()}},A=parseInt(t.getAttribute("debounce"))||0,D=Ue(r,O),oe=null,Ie=L||N,Q=N?r.replace(/\{cursor\}/g,"__CURSOR__"):L?r.replace(/\{page\}/g,"__PAGE__"):null,Ce=Q?Ue(Q,O):null;j(()=>{oe&&clearTimeout(oe)});let $e=ye;for(;$e&&$e.__isProxy;){let ke=$e.$watch(Se);j(ke),$e=$e.$parent}if(r.includes("$store")){let ke=_t(r)||"*";Bt(Se,ke),Se._el=ue,j(()=>ut(Se))}r.includes("$route")&&(Ut(Se),Se._el=ue,j(()=>yt(Se))),r.includes("$i18n")&&(ot(Se),Se._el=ue,j(()=>qe.delete(Se)))}if(g&&c!=="none"){let Ie=function(){let Ce="";try{let Se=R(g,O);Ce=JSON.stringify(Se)||""}catch{return}Ce!==D&&(D=Ce,oe&&clearTimeout(oe),A>0?oe=setTimeout(Ee,A):Ee())},A=parseInt(t.getAttribute("debounce"))||0,D="";try{let Ce=R(g,O);D=JSON.stringify(Ce)||""}catch{}let oe=null;j(()=>{oe&&clearTimeout(oe)});let Q=ye;for(;Q&&Q.__isProxy;){let Ce=Q.$watch(Ie);j(Ce),Q=Q.$parent}}if(Y&&U?t.refresh=function(){On(),Ee()}:U?t.refresh=function(){Ae(),Ee()}:t.refresh=Ee,j(()=>{delete t.refresh}),$>0&&!ie){let A=setInterval(()=>{if(!t.isConnected){clearInterval(A);return}Ee()},$);j(()=>clearInterval(A))}}});he();var Cn=new Map;function To(e,t){let n=Cn.get(e);n||(n=new Set,Cn.set(e,n)),n.add(t),n.size>=6&&b("SSE: "+n.size+" connections to "+e+". Browsers limit HTTP/1.1 to 6 concurrent connections per origin. Consider HTTP/2 or reducing open streams.")}function yi(e,t){let n=Cn.get(e);n&&(n.delete(t),n.size===0&&Cn.delete(e))}F("sse",{priority:1,gated:!0,init(e,t,n){if(Be(e)){b("sse: SSE directive on a loop element is not supported \u2014 move it to a parent or child element",e);return}let r=e.getAttribute("as")||"data",i=e.getAttribute("sse-event")||null,o=e.getAttribute("sse-insert"),s=o==="append"||o==="prepend"?o:"replace",a=e.getAttribute("sse-limit"),l=e.hasAttribute("sse-limit"),u=l?parseInt(a,10):0;l&&(isNaN(u)||u<=0)&&b('sse-limit="'+a+'" is not a valid positive integer; ignoring.');let d=isNaN(u)||u<=0?0:u,m=e.hasAttribute("sse-credentials"),y=e.getAttribute("into"),v=e.getAttribute("error"),$=e.getAttribute("then");d>0&&s==="replace"&&b("sse-limit has no effect without sse-insert (append or prepend)."),s!=="replace"&&!d&&b('sse-insert="'+s+'" without sse-limit may cause unbounded memory growth on long-lived streams. Set sse-limit to cap the array.');let T=e.parentElement?H(e.parentElement):V(),C=e.__ctx||V({},T);e.__ctx=C,s!=="replace"&&C.$set(r,[]),C.$set("$sse",{connecting:!0,open:!1,error:!1});let z=null,re=null,x=null;function h(c,f,p){C.$set("$sse",{connecting:c,open:f,error:p})}function w(){z&&(q("SSE: closing connection"),z.close(),re&&yi(re,z),z=null,re=null)}function S(){if(!v)return;let c=_e(v);if(!c)return;let p=document.getElementById(v.replace("#",""))?.getAttribute("var")||"err",I=V({[p]:{message:"SSE connection closed"}},C),N=document.createElement("div");N.style.display="contents",N.__ctx=I,N.appendChild(c),se(e),e.innerHTML="",e.appendChild(N),ne(N)}function g(c){w(),s!=="replace"&&C.$set(r,[]),h(!0,!1,!1),x=c,q("SSE: connecting to",c);try{re=new URL(c,window.location.origin).origin}catch{re=window.location.origin}let f=new EventSource(c,{withCredentials:m});z=f,To(re,f),f.onopen=function(){if(!e.isConnected){f.close();return}q("SSE: connection opened",c),h(!1,!0,!1)},f.onerror=function(){if(!e.isConnected){f.close();return}f.readyState===EventSource.CLOSED?(q("SSE: connection closed by server",c),h(!1,!1,!0),re&&yi(re,f),z===f&&(z=null,re=null),S()):(q("SSE: auto-reconnecting",c),h(!0,!1,!1))};let p=i||"message";function I(N){if(!e.isConnected){f.close();return}q("SSE: message received on",p);let J;try{J=JSON.parse(N.data)}catch{J=N.data}if(s==="append"){let L=Array.isArray(C[r])?[...C[r]]:[];L.push(J),d>0&&L.length>d&&L.shift(),C.$set(r,L)}else if(s==="prepend"){let L=Array.isArray(C[r])?[...C[r]]:[];L.unshift(J),d>0&&L.length>d&&L.pop(),C.$set(r,L)}else C.$set(r,J);y&&(ae[y]||(ae[y]=V({})),ae[y].$set(r,C[r]),Ge(y)),$&&Le($,C,{$event:J})}f.addEventListener(p,I)}j(w);let E=Ue(n,C);if(g(E),/\{[^}]+\}/.test(n)){let c=function(){let p=Ue(n,C);p!==x&&(q("SSE: URL changed, reconnecting",x,"\u2192",p),g(p))},f=T;for(;f&&f.__isProxy;){let p=f.$watch(c);j(p),f=f.$parent}if(n.includes("$store")){let p=_t(n)||"*";Bt(c,p),c._el=ue,j(()=>ut(c))}n.includes("$route")&&(Ut(c),c._el=ue,j(()=>yt(c))),n.includes("$i18n")&&(ot(c),c._el=ue,j(()=>qe.delete(c)))}}});he();F("bind",{priority:20,gated:!0,init(e,t,n){let r=H(e),i;function o(){let s=R(n,r),a=s!=null?String(s):"";a!==i&&(i=a,e.textContent=a)}de(n,r,o),o()}});F("bind-html",{priority:20,gated:!0,init(e,t,n){let r=H(e);(_.debug||_.devtools)&&!/^['"`]/.test(n.trim())&&b(`[Security] bind-html used with dynamic expression: "${n}". Ensure the value is trusted or sanitized \u2014 use bind for plain text.`,e);let i;function o(){let s=R(n,r);if(s!=null){let a=String(s);if(a===i)return;i=a,se(e),e.innerHTML=yn(a)}}de(n,r,o),o()}});var Lo=new Set(["href","src","action","formaction","poster","data"]);function bi(e){let n=new DOMParser().parseFromString(e,"image/svg+xml").documentElement;if(n.querySelector("parsererror")||n.nodeName!=="svg"||n.getElementsByTagNameNS("http://www.mozilla.org/newlayout/xml/parsererror.xml","parsererror").length)return"";function r(i){for(let o of[...i.attributes]){let s=o.name.toLowerCase();if(s.startsWith("on")){i.removeAttribute(o.name);continue}if(s==="href"||s==="xlink:href"){let a=o.value.toLowerCase().replace(/[\u0000-\u0020]/g,"");/^(javascript|vbscript):/.test(a)&&i.removeAttribute(o.name)}}}for(let i of[...n.querySelectorAll("script")])i.remove();r(n);for(let i of n.querySelectorAll("*"))r(i);return new XMLSerializer().serializeToString(n)}function Oo(e){try{let t=e.match(/^data:image\/svg\+xml;base64,(.+)$/i);if(t){let o=bi(atob(t[1]));return"data:image/svg+xml;base64,"+btoa(o)}let n=e.indexOf(",");if(n===-1)return"#";let r=e.slice(0,n+1),i=bi(decodeURIComponent(e.slice(n+1)));return r+encodeURIComponent(i)}catch{return"#"}}function No(e,t){if(Lo.has(e)){let n=String(t).trimStart(),r=n.toLowerCase().replace(/[\u0000-\u0020]/g,"");if(/^(javascript|vbscript):/.test(r))return"#";if(/^data:/.test(r)){if(/^data:image\/svg\+xml/.test(r))return Oo(n);if(!/^data:image\//.test(r))return"#"}}return t}var Po=new Set(["disabled","readonly","checked","selected","hidden","required"]);F("bind-*",{priority:20,init(e,t,n){let r=t.replace("bind-",""),i=H(e);if(r==="value"&&(e.tagName==="INPUT"||e.tagName==="TEXTAREA"||e.tagName==="SELECT")){let a=()=>{let l;if(e.type==="number"){if(e.value===""||isNaN(e.valueAsNumber))return;l=e.valueAsNumber}else l=e.value;Le(`${n} = __val`,i,{__val:l})};e.addEventListener("input",a),j(()=>e.removeEventListener("input",a))}let o;function s(){let a=R(n,i);if(Po.has(r)){let u=!!a,d=r in e?!!e[r]:e.hasAttribute(r);if(u===o&&d===u)return;o=u,u?e.setAttribute(r,""):e.removeAttribute(r),r in e&&(e[r]=u);return}let l=a!=null?String(No(r,a)):null;l===o&&e.getAttribute(r)===l||(o=l,l!=null?e.setAttribute(r,l):e.removeAttribute(r))}de(n,i,s),s()}});F("model",{priority:20,gated:!0,init(e,t,n){let r=H(e),i=e.tagName,o=e.type;function s(){let u=R(n,r);if(i==="INPUT"&&o==="checkbox")e.checked=!!u;else if(i==="INPUT"&&o==="radio")e.checked=e.value===String(u);else if(i==="SELECT")e.value=u!=null?String(u):"";else{let d=u!=null?String(u):"";if(document.activeElement===e){if(o==="number"||o==="range"){let m=e.valueAsNumber;if(u!=null&&!isNaN(m)&&m===Number(u))return}else if(e.value===d)return}e.value=d}}let a=i==="SELECT"||o==="checkbox"||o==="radio"?"change":"input",l=()=>{let u;o==="checkbox"?u=e.checked:o==="number"||o==="range"?u=Number(e.value):u=e.value,Le(`${n} = __val`,r,{__val:u})};e.addEventListener(a,l),j(()=>e.removeEventListener(a,l)),de(n,r,s),s()}});he();function wi(e){let t=[],n=e.previousElementSibling;for(;n;){let r=n.getAttribute("if")||n.getAttribute("else-if");if(r)t.push(r);else break;n=n.previousElementSibling}return t}F("if",{priority:10,init(e,t,n){let r=H(e),i=e.getAttribute("then"),o=e.getAttribute("else"),s=e.getAttribute("animate-enter")||e.getAttribute("animate"),a=e.getAttribute("animate-leave"),l=e.getAttribute("transition"),u=parseInt(e.getAttribute("animate-duration"))||0,d=(e.__i18nSavedChildren||[...e.childNodes]).map(T=>T.cloneNode(!0)),m,y=null;j(()=>{y&&(y(),y=null)});function v(){let T=!!R(n,r);T!==m&&(m=T,a||l?(y&&(y(),y=null),y=vn(e,a,l,()=>{y=null,$(T)},u)):$(T))}function $(T){if(e.__ifState=T,T||li(e),se(e),T)if(i){let C=_e(i);C&&(e.innerHTML="",e.appendChild(C))}else{e.innerHTML="";for(let C of d)e.appendChild(C.cloneNode(!0))}else if(o){let C=_e(o);C&&(e.innerHTML="",e.appendChild(C))}else e.innerHTML="";ze(e),ne(e),T&&ci(e),(s||l)&&xt(e,s,l,u)}de(n,r,v),v()}});F("else-if",{priority:10,init(e,t,n){if(Be(e))return;let r=H(e),i=e.getAttribute("then"),o=[...e.childNodes].map(u=>u.cloneNode(!0)),s;function a(){let u=e.previousElementSibling;for(;u;){let y=u.getAttribute("if")||u.getAttribute("else-if");if(y){if(R(y,r)){if(s==="hidden")return;s="hidden",se(e),e.innerHTML="",e.style.display="none";return}}else break;u=u.previousElementSibling}let d=!!R(n,r),m=d?"shown":"hidden";if(m!==s)if(s=m,e.style.display="",d){if(i){let y=_e(i);y&&(se(e),e.innerHTML="",e.appendChild(y))}else{se(e),e.innerHTML="";for(let y of o)e.appendChild(y.cloneNode(!0))}ze(e),ne(e)}else se(e),e.innerHTML=""}let l=wi(e);l.push(n),de(l.join(" "),r,a),a()}});F("else",{priority:10,init(e){if(e.hasAttribute("if")||Be(e))return;let t=H(e),n=e.getAttribute("then"),r=[...e.childNodes].map(a=>a.cloneNode(!0)),i,o=!1;function s(){let a=e.previousElementSibling,l=!1;for(;a;){let u=a.getAttribute("if")||a.getAttribute("else-if");if(u){if(l=!0,R(u,t)){if(i==="hidden")return;i="hidden",se(e),e.innerHTML="",e.style.display="none";return}}else break;a=a.previousElementSibling}if(!l&&!o&&(o=!0,b('else: no preceding if/else-if sibling found \u2014 note the sibling else pattern for loops was removed in v1.15; use else="templateId" on the loop element',e)),i!=="shown"){if(i="shown",e.style.display="",n){let u=_e(n);u&&(se(e),e.innerHTML="",e.appendChild(u))}else{se(e),e.innerHTML="";for(let u of r)e.appendChild(u.cloneNode(!0))}ze(e),ne(e)}}de(wi(e).join(" "),t,s),s()}});F("show",{priority:20,init(e,t,n){let r=H(e),i=e.getAttribute("animate-enter")||e.getAttribute("animate"),o=e.getAttribute("animate-leave"),s=e.getAttribute("transition"),a=parseInt(e.getAttribute("animate-duration"))||0,l;function u(){let d=!!R(n,r);d!==l&&(l=d,d?(e.style.display="",(i||s)&&xt(e,i,s,a)):o||s?vn(e,o,s,()=>{e.style.display="none"},a):e.style.display="none")}de(n,r,u),u()}});F("hide",{priority:20,init(e,t,n){let r=H(e),i=e.getAttribute("animate-enter")||e.getAttribute("animate"),o=e.getAttribute("animate-leave"),s=e.getAttribute("transition"),a=parseInt(e.getAttribute("animate-duration"))||0,l;function u(){let d=!R(n,r);d!==l&&(l=d,d?(e.style.display="",(i||s)&&xt(e,i,s,a)):o||s?vn(e,o,s,()=>{e.style.display="none"},a):e.style.display="none")}de(n,r,u),u()}});F("animate",{priority:15,init(e,t,n){if(e.hasAttribute("if")||e.hasAttribute("show")||e.hasAttribute("hide"))return;let r=e.getAttribute("animate-enter")||n,i=e.getAttribute("transition"),o=parseInt(e.getAttribute("animate-duration"))||0;(r||i)&&xt(e,r,i,o,!0)}});function Io(e){let t=[],n=0,r=null,i=0;for(let o=0;o=0&&e[l]==="\\";l--)a++;a%2===0&&(r=null)}}else s==="'"||s==='"'||s==="`"?r=s:s==="("||s==="["||s==="{"?n++:s===")"||s==="]"||s==="}"?n--:s===","&&n===0&&(t.push(e.slice(i,o)),i=o+1)}return t.push(e.slice(i)),t}F("switch",{priority:10,init(e,t,n){let r=H(e);function i(){let o=R(n,r),s=!1;for(let a of[...e.children]){let l=a.getAttribute("case"),u=a.hasAttribute("default"),d=a.getAttribute("then");if(l){let m=Io(l).map(y=>R(y.trim(),r));if(!s&&m.includes(o))if(s=!0,a.style.display="",d){let y=_e(d);y&&(se(a),a.innerHTML="",a.appendChild(y)),a.__declared=!1,ne(a)}else a.__switchDisposed&&(a.__switchDisposed=!1,a.__declared=!1,ne(a));else se(a),a.__switchDisposed=!0,a.style.display="none"}else if(u)if(s&&(se(a),a.__switchDisposed=!0),a.style.display=s?"none":"",!s&&d){let m=_e(d);m&&(se(a),a.innerHTML="",a.appendChild(m)),a.__declared=!1,ne(a)}else!s&&a.__switchDisposed&&(a.__switchDisposed=!1,a.__declared=!1,ne(a))}}de(n,r,i),i()}});he();he();F("class-*",{priority:20,init(e,t,n){let r=t.replace("class-",""),i=H(e);if(r==="map"){let a=function(){let l=R(n,i);if(l&&typeof l=="object")for(let[u,d]of Object.entries(l))u.split(/\s+/).filter(Boolean).forEach(y=>e.classList.toggle(y,!!d))};de(n,i,a),a();return}if(r==="list"){let l=function(){let u=R(n,i);if(Array.isArray(u)){a.forEach(m=>{m&&e.classList.remove(m)});let d=u.filter(Boolean);d.forEach(m=>e.classList.add(m)),a=d}},a=[];de(n,i,l),l();return}let o=null;function s(){let a=!!R(n,i);a===o&&e.classList.contains(r)===a||(o=a,e.classList.toggle(r,a))}de(n,i,s),(n.includes("NoJS.locale")||n.includes("window.NoJS.locale"))&&ot(s),s()}});F("style-*",{priority:20,init(e,t,n){let r=t.replace("style-",""),i=H(e);if(r==="map"){let d=function(){let m=R(n,i);if(m&&typeof m=="object"){let y=Object.keys(m);for(let v of u)v in m||(v.startsWith("--")?e.style.removeProperty(v):e.style[v]="");for(let[v,$]of Object.entries(m))v.startsWith("--")?e.style.setProperty(v,$??""):e.style[v]=$??"";u=y}},u=[];de(n,i,d),d();return}let o=r.startsWith("--"),s=o?r:r.replace(/-([a-z])/g,(u,d)=>d.toUpperCase()),a;function l(){let u=R(n,i),d=u!=null?String(u):"";d===a&&e.style.getPropertyValue(r)===d||(a=d,o?e.style.setProperty(s,d):e.style[s]=d)}de(n,i,l),l()}});he();var $o=new Set(["mounted","init","updated","error","unmounted"]);F("on:*",{priority:20,gated:!0,init(e,t,n){let r=H(e);if(t.indexOf(".")===-1&&!e.__gatedDirs){let v=t.slice(3);if(!$o.has(v)){let $=C=>Le(n,r,{$event:C,$el:e});e.addEventListener(v,$);let T=()=>e.removeEventListener(v,$);T._elOnly=!0,j(T);return}}let i=t.replace("on:","").split("."),o=i[0],s=new Set(i.slice(1));if(o==="mounted"){requestAnimationFrame(()=>Le(n,r,{$el:e}));return}if(o==="init"){Le(n,r,{$el:e});return}if(o==="updated"){let v=!1,$=new MutationObserver(()=>{if(!e.isConnected){$.disconnect();return}if(!v){v=!0;try{Le(n,r,{$el:e})}finally{Promise.resolve().then(()=>{v=!1})}}});$.observe(e,{childList:!0,subtree:!0,characterData:!0,attributes:!0}),j(()=>$.disconnect());return}if(o==="error"){let v=$=>{(e.contains($.target)||$.target===e)&&Le(n,r,{$el:e,$error:$.error||$.message})};window.addEventListener("error",v),j(()=>window.removeEventListener("error",v));return}if(o==="unmounted"){let v=new MutationObserver($=>{for(let T of $)for(let C of T.removedNodes)if(C===e||C.contains?.(e)){Le(n,r,{$el:e}),v.disconnect();return}});e.parentElement&&v.observe(e.parentElement,{childList:!0,subtree:!0}),j(()=>v.disconnect());return}let a=0,l=0,u=i.slice(1);for(let v=0;v{Le(n,r,{$event:v,$el:e})};if(a>0){let v=d,$;d=T=>{clearTimeout($),$=setTimeout(()=>v(T),a)},j(()=>clearTimeout($))}if(l>0){let v=d,$=0;d=T=>{let C=Date.now();C-$>=l&&($=C,v(T))}}let m=v=>{if(o==="keydown"||o==="keyup"||o==="keypress"){let $=["enter","escape","tab","space","delete","backspace","up","down","left","right","ctrl","alt","shift","meta"];for(let T of s)if($.includes(T)&&(T==="enter"&&v.key!=="Enter"||T==="escape"&&v.key!=="Escape"||T==="tab"&&v.key!=="Tab"||T==="space"&&v.key!==" "||T==="delete"&&v.key!=="Delete"&&v.key!=="Backspace"||T==="backspace"&&v.key!=="Backspace"||T==="up"&&v.key!=="ArrowUp"||T==="down"&&v.key!=="ArrowDown"||T==="left"&&v.key!=="ArrowLeft"||T==="right"&&v.key!=="ArrowRight"||T==="ctrl"&&!v.ctrlKey||T==="alt"&&!v.altKey||T==="shift"&&!v.shiftKey||T==="meta"&&!v.metaKey))return}s.has("prevent")&&v.preventDefault(),s.has("stop")&&v.stopPropagation(),!(s.has("self")&&v.target!==e)&&d(v)},y={};if(s.has("once")&&(y.once=!0),e.addEventListener(o,m,y),!y.once){let v=()=>e.removeEventListener(o,m,y);v._elOnly=!0,j(v)}}});F("trigger",{priority:20,init(e,t,n){let r=H(e),i=e.getAttribute("trigger-data"),o=()=>{let a=i?R(i,r):null;e.dispatchEvent(new CustomEvent(n,{detail:a,bubbles:!0}))};e.addEventListener("click",o);let s=()=>e.removeEventListener("click",o);s._elOnly=!0,j(s)}});he();F("ref",{priority:5,init(e,t,n){it[n]=e,j(()=>{it[n]===e&&delete it[n]})}});F("use",{priority:9,init(e,t,n){let r=H(e),i=_e(n);if(!i)return;let o={};for(let d of[...e.attributes])if(d.name.startsWith("var-")){let m=d.name.replace("var-","");o[m]=R(d.value,r)}let s=V(o,r),a={};for(let d of[...e.children]){let m=d.getAttribute("slot")||"default";a[m]||(a[m]=document.createDocumentFragment()),a[m].appendChild(d.cloneNode(!0))}let l=i.querySelectorAll("slot");for(let d of l){let m=d.getAttribute("name")||"default";a[m]&&d.replaceWith(a[m])}se(e),e.innerHTML="";let u=document.createElement("div");u.style.display="contents",u.__ctx=s,u.appendChild(i),e.appendChild(u),ne(u)}});F("call",{priority:20,init(e,t,n){let r=H(e),i=e.getAttribute("method")||"get",o=e.getAttribute("as")||"data",s=e.getAttribute("into"),a=e.getAttribute("success"),l=e.getAttribute("error"),u=e.getAttribute("loading"),d=e.getAttribute("then"),m=e.getAttribute("confirm"),y=e.getAttribute("body"),v=e.getAttribute("redirect"),$=e.getAttribute("headers"),T=[...e.childNodes].map(h=>h.cloneNode(!0)),C=null,z=null;function re(){z&&(se(z),z.remove(),z=null)}j(re);let x=async h=>{if(h.preventDefault(),m&&!window.confirm(m))return;C&&C.abort(),C=new AbortController;let w=Ue(n,r);if(u){let S=_e(u);S&&(se(e),e.innerHTML="",e.appendChild(S),ne(e),e.disabled=!0)}try{let S=null;if(y){let P=Ue(y,r);try{S=JSON.parse(P)}catch{S=P}}let g=$?JSON.parse($):{};if($)for(let P of Object.keys(g)){let c=P.toLowerCase();(Lt.has(c)||/^x-(auth|api)-/.test(c))&&b(`Sensitive header "${P}" is set inline on a headers attribute. Use NoJS.config({ headers }) or an interceptor to avoid exposing credentials in HTML source.`)}let E=await _n(w,i,S,g,e,C.signal);if(u){se(e),e.innerHTML="";for(let P of T)e.appendChild(P.cloneNode(!0));ze(e),ne(e),e.disabled=!1}if(o&&r.$set(o,E),s&&(ae[s]||(ae[s]=V({})),ae[s].$set(o,E),Ge(s)),d&&Le(d,r,{result:E}),a){let P=_e(a),c=e.closest("[route-view]")||e.parentElement;if(P&&c){let p=document.getElementById(a.replace("#",""))?.getAttribute("var")||"result",I=V({[p]:E},r);re();let N=document.createElement("div");N.style.display="contents",N.__ctx=I,N.appendChild(P),c.appendChild(N),z=N,ne(N)}}v&&me&&me.push(v),Xe("fetch:success",{url:w,data:E}),Te("fetch:success",{method:i,url:w})}catch(S){if(S.name==="AbortError")return;if(b(`call ${i.toUpperCase()} ${w} failed:`,S.message),u){se(e),e.innerHTML="";for(let g of T)e.appendChild(g.cloneNode(!0));ze(e),ne(e),e.disabled=!1}if(Xe("fetch:error",{url:w,error:S}),Xe("error",{url:w,error:S}),Te("fetch:error",{method:i,url:w,error:S.message}),l){let g=_e(l),E=e.closest("[route-view]")||e.parentElement;if(g&&E){let c=document.getElementById(l.replace("#",""))?.getAttribute("var")||"err",f=V({[c]:{message:S.message,status:S.status,body:S.body}},r);re();let p=document.createElement("div");p.style.display="contents",p.__ctx=f,p.appendChild(g),E.appendChild(p),z=p,ne(p)}}}};e.addEventListener("click",x),j(()=>e.removeEventListener("click",x))}});he();var ko=Object.freeze({valid:!0,dirty:!1,touched:!1,error:null,value:""}),Ro=new Proxy({},{get:()=>ko});F("validate",{priority:30,init(e,t){b('[NoJS] "validate" has moved to @no-js-dev/nojs-elements. Install the plugin and call NoJS.use(NoJSElements) to enable it.');let n=H(e);n.$form={valid:!1,dirty:!1,touched:!1,submitting:!1,pending:!1,errors:{},values:{},firstError:null,errorCount:0,fields:Ro,reset(){}};let r=e.tagName==="FORM"?e:e.closest("form");if(r){let i=o=>{o.preventDefault(),b('[NoJS] Form submission blocked \u2014 "validate" requires @no-js-dev/nojs-elements.')};r.addEventListener("submit",i),j(()=>r.removeEventListener("submit",i))}}});he();F("error-boundary",{priority:1,init(e,t,n){let r=H(e),i=!1;function o(l){if(i){b("error-boundary: secondary error inside fallback (suppressed to prevent infinite recursion):",l);return}i=!0;try{let u=_e(n);if(u){let d=V({err:{message:l}},r);se(e),e.innerHTML="";let m=document.createElement("div");m.style.display="contents",m.__ctx=d,m.appendChild(u),e.appendChild(m),ne(m)}}finally{i=!1}}let s=l=>{o(l.detail?.message||"An error occurred")};e.addEventListener("nojs:error",s),j(()=>e.removeEventListener("nojs:error",s));let a=l=>{l.target===window?e.isConnected&&o(l.message||"An error occurred"):(e.contains(l.target)||e===l.target)&&o(l.message||"An error occurred")};window.addEventListener("error",a,{capture:!0}),j(()=>window.removeEventListener("error",a,{capture:!0}))}});at();he();F("t",{priority:20,gated:!0,init(e,t,n){let r=H(e),i=e.hasAttribute("t-html"),o=[];for(let l of[...e.attributes])l.name.startsWith("t-")&&l.name!=="t"&&l.name!=="t-html"&&o.push(l.value);let s=o.length?n+" "+o.join(" "):n;function a(){let l={};for(let d of[...e.attributes])if(d.name.startsWith("t-")&&d.name!=="t"&&d.name!=="t-html"){let m=d.name.replace("t-","");l[m]=R(d.value,r)??d.value}let u=fe.t(n,l);i?(se(e),e.innerHTML=yn(u)):e.textContent=u}de(s,r,a),ot(a),a()}});F("i18n-ns",{priority:1,init(e,t,n){if(!n)return;e.__i18nSavedChildren=[...e.childNodes].map(i=>i.cloneNode(!0));let r=document.createDocumentFragment();for(;e.firstChild;)r.appendChild(e.firstChild);Mn(n).then(()=>{if(e.__ifState!==void 0){st();return}e.appendChild(r),ne(e),st()})}});he();var Do=e=>`[NoJS] "${e}" has moved to @no-js-dev/nojs-elements. Install the plugin and call NoJS.use(NoJSElements) to enable it.`;for(let e of["drag","drop","drag-list","drag-multiple"])F(e,{priority:e==="drag-multiple"?16:e==="drag-list"?10:15,init(t,n){b(Do(n))}});he();function jo(e,t){return e.replace(/\{\s*([^}"'{\s][^}]*)\}/g,(n,r)=>{try{let i=R(r.trim(),t);return i!=null?String(i):""}catch{return""}})}F("page-title",{priority:1,gated:!0,init(e,t,n){if(Be(e)){b(`${t}: head directive on a loop element is not supported \u2014 move it to a parent or child element`,e);return}let r=H(e);function i(){let o=R(n,r);o!=null&&(document.title=String(o))}de(n,r,i),i()}});F("page-description",{priority:1,gated:!0,init(e,t,n){if(Be(e)){b(`${t}: head directive on a loop element is not supported \u2014 move it to a parent or child element`,e);return}let r=H(e),i=!1,o=null;function s(){let a=R(n,r);if(a==null)return;let l=document.querySelector('meta[name="description"]');l||(l=document.createElement("meta"),l.name="description",document.head.appendChild(l),i=!0),o=l,l.content=String(a)}de(n,r,s),s(),j(()=>{i&&o&&o.isConnected&&o.remove()})}});F("page-canonical",{priority:1,gated:!0,init(e,t,n){if(Be(e)){b(`${t}: head directive on a loop element is not supported \u2014 move it to a parent or child element`,e);return}let r=H(e),i=!1,o=null;function s(){let a=R(n,r);if(a==null)return;let l=document.querySelector('link[rel="canonical"]');l||(l=document.createElement("link"),l.rel="canonical",document.head.appendChild(l),i=!0);let u=String(a);/^\s*(javascript|vbscript|data):/i.test(u)||(o=l,l.href=u)}de(n,r,s),s(),j(()=>{i&&o&&o.isConnected&&o.remove()})}});F("page-jsonld",{priority:1,gated:!0,init(e,t,n){if(Be(e)){b(`${t}: head directive on a loop element is not supported \u2014 move it to a parent or child element`,e);return}let r=H(e),i=(e.textContent||e.innerHTML).trim();if(!i)return;let o=!1,s=null;function a(){let l=jo(i,r);if(!l)return;let u=document.querySelector('script[type="application/ld+json"][data-nojs]');u||(u=document.createElement("script"),u.type="application/ld+json",u.setAttribute("data-nojs",""),document.head.appendChild(u),o=!0),s=u,u.textContent=l.replace(/<\//g,"<\\/")}de(i,r,a),a(),j(()=>{o&&s&&s.isConnected&&s.remove()})}});ii();var Ke=null,sr=!1,Mo=new Set(["store","route","router","i18n","refs","form","sse","parent","watch","set","notify","raw","isProxy","listeners","app","config","env","debug","version","plugins","globals","el","event","self","this","super","window","document","toString","valueOf","hasOwnProperty"]),Fo=typeof window<"u"?new Set([eval,Function,window.eval,window.Function].filter(Boolean)):new Set;function vi(e){return Fo.has(e)}var Ho=new Set(["__proto__","constructor","prototype"]),Bo=new Set(["uppercase","lowercase","capitalize","truncate","trim","stripHtml","slugify","nl","nl2br","encodeUri","number","currency","percent","filesize","ordinal","count","first","last","join","reverse","unique","sortBy","where","pluck","keys","values","json","debug","default","date","datetime","relative","fromNow"]),Uo=new Set(["required","email","url","min","max","minlength","maxlength","pattern","match","number","integer","alpha","alphanumeric"]);function ar(e,t=new Set){if(!(!e||typeof e!="object"||t.has(e))){t.add(e);for(let n of Object.keys(e))if(Ho.has(n))throw b("NoJS.global(): value contains a forbidden key: "+n),new Error("unsafe_global");for(let n of Object.values(e)){if(vi(n))throw b("NoJS.global(): value contains a forbidden reference (eval/Function)."),new Error("unsafe_global");n&&typeof n=="object"&&ar(n,t)}}}function zo(){return typeof window>"u"?null:(_.router||{}).useHash?window.location.hash.slice(1)||"/":Ht(window.location.pathname)}var et={get baseApiUrl(){return _.baseApiUrl},set baseApiUrl(e){_.baseApiUrl=e},get locale(){return fe.locale},set locale(e){fe.locale=e},config(e={}){if(sr){let s=["sanitize","dangerouslyDisableSanitize","sanitizeHtml"];for(let a of s)a in e&&(b(`config.${a} cannot be changed after init()`),delete e[a])}let t={..._.headers},n={..._.cache},r={..._.templates},i={..._.router},o={..._.i18n};if("csp"in e&&(b("csp config option removed \u2014 No.JS is now CSP-safe by default"),delete e.csp),e.exprCacheSize!==void 0){let s=parseInt(e.exprCacheSize);e.exprCacheSize=Number.isFinite(s)&&s>0?s:500}if(Object.assign(_,e),e.sanitize===!1&&b("sanitize:false is deprecated \u2014 use dangerouslyDisableSanitize:true to make the risk explicit."),e.headers&&(_.headers={...t,...e.headers}),e.csrf&&(_.csrf=e.csrf),e.cache&&(_.cache={...n,...e.cache}),e.templates&&(_.templates={...r,...e.templates}),e.router&&("mode"in e.router&&!("useHash"in e.router)&&(q('router.mode is deprecated. Use router.useHash instead: mode: "hash" \u2192 useHash: true, mode: "history" \u2192 useHash: false',"warn"),e.router.useHash=e.router.mode==="hash",delete e.router.mode),_.router={...i,...e.router}),e.i18n&&(_.i18n={...o,...e.i18n},fe.locale=e.i18n.defaultLocale||fe.locale),e.stores){for(let[s,a]of Object.entries(e.stores))ae[s]||(ae[s]=V(a||{}),Te("store:created",{name:s,keys:Object.keys(a||{})}));delete _.stores}},use(e,t={}){if(en){b("Cannot install plugins during dispose.");return}if(typeof e=="function"){if(!e.name||e.name==="anonymous"){b('Plugin must have a unique, non-empty name. Use { name: "my-plugin", install: fn }.');return}e={name:e.name,install:e}}if(!e.name||typeof e.name!="string"||e.name==="anonymous"){b("Plugin must have a unique, non-empty name.");return}if(Je.has(e.name)){Je.get(e.name).plugin!==e&&b(`Plugin "${e.name}" name collision: a different plugin with this name is already installed.`);return}e.capabilities&&_.debug&&q(`Plugin "${e.name}" declares capabilities:`,e.capabilities),t.trusted===!0&&b(`WARNING: Plugin "${e.name}" installed with trusted access to sensitive HTTP headers.`),Rn(e.name);try{e.install(et,t)}finally{Rn(null)}Je.set(e.name,{plugin:e,options:t}),Ke&&e.init&&Ke.then(()=>e.init(et)).catch(n=>b(`Plugin "${e.name}" init error:`,n.message)),q(`Plugin "${e.name}" installed.`)},global(e,t){if(typeof e!="string"||!e){b("NoJS.global() requires a non-empty string name.");return}if(e==="__proto__"||e==="constructor"||e==="prototype"){b(`NoJS.global(): "${e}" is a forbidden name.`);return}if(Mo.has(e)){b(`NoJS.global(): "${e}" is reserved and cannot be used.`);return}if(!/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(e)){b(`NoJS.global(): "${e}" is not a valid identifier.`);return}if(vi(t)){b(`NoJS.global(): value for "${e}" is a forbidden reference (eval/Function).`);return}if(e in je&&ht[e]&&ht[e]!==gt&&b(`Global "$${e}" owned by "${ht[e]}" is being overwritten.`),t&&typeof t=="object"&&!t.__isProxy){try{t=JSON.parse(JSON.stringify(t))}catch{try{ar(t)}catch(n){if(n.message==="unsafe_global")return}}t=V(t)}else if(t&&typeof t=="object"&&t.__isProxy)try{ar(t.__raw??t)}catch(n){if(n.message==="unsafe_global")return}je[e]=t,gt&&(ht[e]=gt),Ge(),Te("global:set",{name:e,hasValue:t!=null}),q(`Global "$${e}" registered.`)},async dispose(){kn(!0);try{if(Ke)try{await Ke}catch{}let e=[...Je.entries()].reverse();for(let[t,{plugin:n}]of e)if(n.dispose)try{let r;await Promise.race([Promise.resolve(n.dispose(et)).finally(()=>clearTimeout(r)),new Promise((i,o)=>{r=setTimeout(()=>o(new Error("Dispose timeout")),3e3)})])}catch(r){b(`Plugin "${t}" dispose error:`,r.message)}Je.clear();for(let t in je)delete je[t];for(let t in ht)delete ht[t];Ve.request.length=0,Ve.response.length=0,me&&me.destroy&&me.destroy(),Dn(null),Ir(),sr=!1,Ke=null,q("Disposed.")}finally{kn(!1)}},async init(e){if(!(typeof document>"u"))return Ke||(sr=!0,Ke=(async()=>{if(e=e||document.body,q("Initializing..."),_.i18n.loadPath){let n=new Set([fe.locale,_.i18n.fallbackLocale]);await Promise.all([...n].map(r=>rn(r)))}Mt(e);let t=zo();await ti(t),document.querySelector("[route-view]")&&Dn(mi()),ne(e),me&&await me.init(),q("Initialized."),ni(),$r(et);for(let[,{plugin:n}]of Je)n.init&&await n.init(et);Xe("plugins:ready")})(),Ke)},directive(e,t){F(e,t)},filter(e,t){if(typeof e!="string"||!e)throw new TypeError("NoJS.filter() requires a non-empty string name.");if(typeof t!="function")throw new TypeError(`NoJS.filter(): "${e}" handler must be a function.`);if(Bo.has(e)){b(`NoJS.filter(): "${e}" is a built-in filter and cannot be overridden.`);return}ee[e]=t},validator(e,t){if(typeof e!="string"||!e)throw new TypeError("NoJS.validator() requires a non-empty string name.");if(typeof t!="function")throw new TypeError(`NoJS.validator(): "${e}" handler must be a function.`);if(Uo.has(e)){b(`NoJS.validator(): "${e}" is a built-in validator and cannot be overridden.`);return}$n[e]=t},i18n(e){e.loadPath!=null&&(_.i18n.loadPath=e.loadPath),e.ns&&(_.i18n.ns=e.ns),Array.isArray(e.supportedLocales)&&(_.i18n.supportedLocales=e.supportedLocales),e.cache!=null&&(_.i18n.cache=e.cache),e.persist!=null&&(_.i18n.persist=e.persist),e.locales&&(fe.locales=e.locales),e.fallbackLocale&&(_.i18n.fallbackLocale=e.fallbackLocale),e.defaultLocale&&(fe._locale=e.defaultLocale);let t=!1;if(_.i18n.persist&&typeof localStorage<"u")try{let n=localStorage.getItem("nojs-locale");n&&(fe._locale=n,t=!0)}catch{}if(!t&&e.detectBrowser){let n=typeof navigator<"u"&&navigator.language||"en",r=n.split("-")[0],i=Array.isArray(_.i18n.supportedLocales)?_.i18n.supportedLocales:[],o=s=>fe.locales[s]||i.includes(s);o(n)?fe._locale=n:o(r)&&(fe._locale=r)}Ke&&(_.i18n.loadPath&&!fe.locales[fe._locale]?rn(fe._locale).then(()=>st()):e.locales&&st())},on(e,t){return rt[e]||(rt[e]=[]),rt[e].length>=_.maxEventListeners&&b('MaxListenersExceeded: event "'+e+'" has '+rt[e].length+" listeners (max "+_.maxEventListeners+"). Possible memory leak."),rt[e].push(t),()=>{rt[e]=rt[e].filter(n=>n!==t)}},interceptor(e,t){if(en){b("Cannot register interceptors during dispose.");return}if(!Ve[e]){b(`NoJS.interceptor(): unknown type "${e}" (expected "request" or "response").`);return}Ve[e].push(gt?{fn:t,pluginName:gt}:t)},get store(){return ae},notify(){Ge()},get router(){return me},createContext:V,evaluate:R,findContext:H,processTree:ne,resolve:jt,get internals(){return Object.freeze({execStatement:Le,cloneTemplate:_e,disposeChildren:se,disposeTree:He,warn:b,validators:Object.freeze({...$n}),removeCoreDirective:oi,onDispose:j})},version:"1.20.0"};Object.defineProperty(et,"CANCEL",{value:zt,writable:!1,configurable:!1});Object.defineProperty(et,"RESPOND",{value:Ot,writable:!1,configurable:!1});Object.defineProperty(et,"REPLACE",{value:Wt,writable:!1,configurable:!1});Object.defineProperty(et,"_initialized",{get(){return Ke!==null},set(e){e||(Ke=null)},configurable:!0});var Tn=et;window.NoJS=Tn;document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>Tn.init()):Tn.init();})();
//# sourceMappingURL=no.js.map
diff --git a/dist/iife/no.js.map b/dist/iife/no.js.map
index 826a408..521d30e 100644
--- a/dist/iife/no.js.map
+++ b/dist/iife/no.js.map
@@ -1,7 +1,7 @@
{
"version": 3,
"sources": ["../../src/globals.js", "../../src/i18n.js", "../../src/index.js", "../../src/context.js", "../../src/devtools.js", "../../src/evaluate.js", "../../src/dom.js", "../../src/fetch.js", "../../src/registry.js", "../../src/router.js", "../../src/animations.js", "../../src/filters.js", "../../src/directives/state.js", "../../src/directives/loops.js", "../../src/directives/http.js", "../../src/directives/sse.js", "../../src/directives/binding.js", "../../src/directives/conditionals.js", "../../src/directives/styling.js", "../../src/directives/events.js", "../../src/directives/refs.js", "../../src/directives/validate-stub.js", "../../src/directives/error-boundary.js", "../../src/directives/i18n.js", "../../src/directives/dnd-stub.js", "../../src/directives/head.js", "../../src/cdn.js"],
- "sourcesContent": ["// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// SHARED STATE & UTILITIES\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nexport const _config = {\n baseApiUrl: \"\",\n headers: {},\n timeout: 10000,\n retries: 0,\n retryDelay: 1000,\n credentials: \"same-origin\",\n csrf: null,\n cache: { strategy: \"none\", ttl: 300000 },\n templates: { cache: true },\n router: { useHash: false, base: \"/\", scrollBehavior: \"top\", templates: \"pages\", ext: \".tpl\", suppressHashWarning: false, focusBehavior: \"none\", viewTransition: true },\n i18n: { defaultLocale: \"en\", fallbackLocale: \"en\", detectBrowser: false, loadPath: null, ns: [], supportedLocales: [], cache: true, persist: false },\n debug: false,\n devtools: false,\n sanitize: true,\n dangerouslyDisableSanitize: false,\n sanitizeHtml: null,\n exprCacheSize: 500,\n maxEventListeners: 100,\n appId: \"\",\n};\n\nexport const _interceptors = { request: [], response: [] };\nexport const _eventBus = {};\nexport const _stores = {};\nexport const _storeWatchers = new Map(); // storeName \u2192 Set, '*' = wildcard\nexport const _i18nListeners = new Set(); // fns watching $i18n expressions\nexport const _routeWatchers = new Set(); // fns watching $route expressions\nexport const _filters = {};\nexport const _validators = {};\nexport const _cache = new Map();\nexport const _refs = {};\nexport let _routerInstance = null;\n\n// \u2500\u2500\u2500 Plugin system shared state \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport const _plugins = new Map(); // name \u2192 { plugin, options }\nexport const _globals = Object.create(null); // name \u2192 reactive value (prototype-free)\nexport const _globalOwners = Object.create(null); // name \u2192 plugin name (collision tracking)\nexport let _disposing = false;\n// Internal: used by index.js dispose() only \u2014 plugins receive the NoJS API, not module imports\nexport function _setDisposing(v) { _disposing = v; }\nexport let _currentPluginName = null;\nexport function _setCurrentPluginName(v) { _currentPluginName = v; }\n\nexport const _SENSITIVE_HEADERS = new Set([\n 'authorization', 'x-api-key', 'x-auth-token', 'cookie',\n 'proxy-authorization', 'set-cookie', 'x-csrf-token',\n]);\n\nexport const _SENSITIVE_KEYS = new Set([\n 'token', 'password', 'secret', 'key', 'auth', 'credential', 'session',\n]);\n\nexport const _SENSITIVE_RESPONSE_HEADERS = new Set([\n 'set-cookie', 'x-csrf-token', 'x-auth-token',\n 'www-authenticate', 'proxy-authenticate',\n]);\n\n// \u2500\u2500\u2500 Lifecycle: tracks the element being processed by processElement \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Used by ctx.$watch and _onDispose to transparently tag watchers/disposers\n// with the owning DOM element \u2014 no changes needed in directive files.\nexport let _currentEl = null;\n\nexport function _setCurrentEl(el) {\n _currentEl = el;\n}\n\nexport function setRouterInstance(r) {\n _routerInstance = r;\n}\n\nexport function _log(...args) {\n if (_config.debug) console.log(\"[No.JS]\", ...args);\n}\n\nexport function _warn(...args) {\n console.warn(\"[No.JS]\", ...args);\n}\n\n// \u2500\u2500\u2500 Shared URL utility \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport function _stripBase(pathname) {\n const base = (_config.router.base || \"/\").replace(/\\/$/, \"\");\n if (!base) return pathname || \"/\";\n const escaped = base.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n return pathname.replace(new RegExp(\"^\" + escaped), \"\") || \"/\";\n}\n\n// Regex to extract the first store name from expressions like $store.cart.items\nconst _STORE_NAME_RE = /\\$store\\.(\\w+)/;\n\nexport function _extractStoreName(expr) {\n if (typeof expr !== \"string\") return null;\n const m = _STORE_NAME_RE.exec(expr);\n return m ? m[1] : null;\n}\n\nfunction _notifyPartition(set) {\n // Snapshot before iterating: a watcher may synchronously add/delete watchers\n // in this same partition, which would otherwise skip or re-run listeners.\n // Isolate each listener so one throwing watcher doesn't abort the rest.\n for (const fn of [...set]) {\n if (fn._el && !fn._el.isConnected) {\n set.delete(fn);\n continue;\n }\n try {\n fn();\n } catch (err) {\n _warn(\"store watcher threw; continuing with remaining watchers:\", err);\n }\n }\n}\n\nexport function _notifyStoreWatchers(storeName) {\n if (storeName) {\n // Notify only the targeted partition + wildcards\n const partition = _storeWatchers.get(storeName);\n if (partition) _notifyPartition(partition);\n const wildcard = _storeWatchers.get(\"*\");\n if (wildcard) _notifyPartition(wildcard);\n } else {\n // No store name \u2014 notify ALL partitions (backward compat)\n for (const set of _storeWatchers.values()) {\n _notifyPartition(set);\n }\n }\n}\n\nexport function _addStoreWatcher(fn, partition) {\n let set = _storeWatchers.get(partition);\n if (!set) {\n set = new Set();\n _storeWatchers.set(partition, set);\n }\n set.add(fn);\n fn._storePartition = partition;\n}\n\nexport function _deleteStoreWatcher(fn) {\n const partition = fn._storePartition;\n if (partition) {\n const set = _storeWatchers.get(partition);\n if (set) {\n set.delete(fn);\n if (set.size === 0) _storeWatchers.delete(partition);\n }\n } else {\n // Fallback: scan all partitions (legacy safety net)\n for (const [key, set] of _storeWatchers) {\n set.delete(fn);\n if (set.size === 0) _storeWatchers.delete(key);\n }\n }\n}\n\nexport function _notifyRouteWatchers() {\n for (const fn of [..._routeWatchers]) {\n if (fn._el && !fn._el.isConnected) {\n _routeWatchers.delete(fn);\n continue;\n }\n try {\n fn();\n } catch (err) {\n _warn(\"route watcher threw; continuing with remaining watchers:\", err);\n }\n }\n}\n\nexport function _addRouteWatcher(fn) {\n _routeWatchers.add(fn);\n}\n\nexport function _deleteRouteWatcher(fn) {\n _routeWatchers.delete(fn);\n}\n\nexport function _watchI18n(fn) {\n _i18nListeners.add(fn);\n return () => _i18nListeners.delete(fn);\n}\n\n// Root-key extractor, injected by evaluate.js at load time (avoids a\n// globals \u2194 evaluate import cycle). Returns Set | null.\nlet _exprRootKeysFn = null;\nexport function _setExprRootKeysFn(f) { _exprRootKeysFn = f; }\n\nexport function _watchExpr(expr, ctx, fn) {\n // Key-scope the watcher: notify(key) skips fn when the changed key cannot\n // appear in the expression. null roots (calls, filters, $-specials, parse\n // failure) lock fn to unkeyed \u2014 it fires on every notification, exactly\n // the pre-scoping behavior. A fn watched under several exprs unions their\n // roots; one ambiguous expr makes it permanently unkeyed.\n if (_exprRootKeysFn && !fn._unkeyed) {\n const roots = _exprRootKeysFn(expr);\n if (roots === null) {\n fn._unkeyed = true;\n fn._keys = undefined;\n } else if (fn._keys) {\n // Union branch mutates \u2014 a fn still holding the shared memoized Set\n // must take a private copy first (copy-on-write) so every other\n // watcher of the same expression keeps its clean shared Set.\n if (fn._keysShared) {\n fn._keys = new Set(fn._keys);\n fn._keysShared = false;\n }\n for (const k of roots) fn._keys.add(k);\n } else {\n // roots is the shared memoized Set from evaluate.js \u2014 adopt it\n // directly instead of copying. The common case (one expression per\n // watcher, i.e. every loop-row binding) then shares one Set across\n // all rows; the union branch above copies lazily if a second\n // expression ever lands on this fn.\n fn._keys = roots;\n fn._keysShared = true;\n }\n }\n\n // Register fn on ctx and every parent context so that changes to inherited\n // variables (e.g. outer state modified from an outer button) fire fn even\n // when ctx is a nested child context. This generalises the per-directive\n // ancestor-walk proven in http.js and fixes the nested-state one-behind /\n // dead-reactivity gap (audit finding 1). Set.add is a no-op for duplicates,\n // so the same fn registered twice on the same context is harmless.\n //\n // Registration goes through __listeners directly instead of $watch: the\n // per-registration unwatch closures $watch returns (one per chain level per\n // watcher) dominated per-row watcher memory; a single dispose closure that\n // re-walks the same chain replaces them all.\n if (_currentEl) fn._el = _currentEl;\n let c = ctx;\n while (c && c.__isProxy) {\n c.__listeners.add(fn);\n c = c.$parent;\n }\n\n // Cleanup: registry's _disposeElement walks fn._wctx's chain deleting fn\n // (plus the store/route/i18n registries) for every fn in el.__watcherFns \u2014\n // pure data instead of one dispose closure per watcher. Gated elements\n // still need a real disposer: gate deactivation runs __gateDisposers, not\n // element disposal.\n if (_currentEl && !_currentEl.__gatedDirs) {\n fn._wctx = ctx;\n (_currentEl.__watcherFns || (_currentEl.__watcherFns = [])).push(fn);\n } else {\n _onDispose(() => {\n let d = ctx;\n while (d && d.__isProxy) {\n d.__listeners.delete(fn);\n d = d.$parent;\n }\n _deleteStoreWatcher(fn);\n _deleteRouteWatcher(fn);\n _i18nListeners.delete(fn);\n });\n }\n if (typeof expr === \"string\" && expr.includes(\"$store\")) {\n const partition = _extractStoreName(expr) || \"*\";\n _addStoreWatcher(fn, partition);\n fn._el = _currentEl;\n }\n if (typeof expr === \"string\" && expr.includes(\"$route\")) {\n _addRouteWatcher(fn);\n fn._el = _currentEl;\n }\n if (typeof expr === \"string\" && expr.includes(\"$i18n\")) {\n _watchI18n(fn);\n fn._el = _currentEl;\n }\n}\n\n// Register a dispose callback on the element currently being processed.\n// Called from directives to clean up intervals, observers, window listeners.\nexport function _onDispose(fn) {\n if (_currentEl) {\n _currentEl.__disposers = _currentEl.__disposers || [];\n _currentEl.__disposers.push(fn);\n }\n}\n\nexport function _emitEvent(name, data) {\n (_eventBus[name] || []).forEach((fn) => fn(data));\n}\n\n// \u2500\u2500\u2500 Plugin sentinel symbols \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport const _CANCEL = Symbol(\"nojs.cancel\");\nexport const _RESPOND = Symbol(\"nojs.respond\");\nexport const _REPLACE = Symbol(\"nojs.replace\");\n", "// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// i18n SYSTEM\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nimport { _config, _warn, _i18nListeners } from \"./globals.js\";\n\n// \u2500\u2500\u2500 Notify all i18n listeners (shared by setter + directive) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport function _notifyI18n() {\n _i18nTranslationCache.clear();\n for (const fn of _i18nListeners) {\n if (fn._el && !fn._el.isConnected) { _i18nListeners.delete(fn); continue; }\n fn();\n }\n}\n\n// \u2500\u2500\u2500 Deep merge (recursive, returns new object) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst _FORBIDDEN_MERGE_KEYS = new Set([\"__proto__\", \"constructor\", \"prototype\"]);\nfunction _deepMerge(target, source) {\n const out = { ...target };\n for (const key of Object.keys(source)) {\n if (_FORBIDDEN_MERGE_KEYS.has(key)) continue;\n if (\n source[key] && typeof source[key] === \"object\" && !Array.isArray(source[key]) &&\n target[key] && typeof target[key] === \"object\" && !Array.isArray(target[key])\n ) {\n out[key] = _deepMerge(target[key], source[key]);\n } else {\n out[key] = source[key];\n }\n }\n return out;\n}\n\n// \u2500\u2500\u2500 Translation lookup cache: Map key = \"en:namespace.key.path\"\n// Stores the raw resolved message (before interpolation/pluralization).\n// Cleared on locale change and namespace reload to avoid stale entries.\nexport const _i18nTranslationCache = new Map();\n\n// \u2500\u2500\u2500 Locale file cache: Map key = \"en\" or \"en:dashboard\"\nconst _i18nCache = new Map();\nconst _loadedNs = new Set();\n\n// \u2500\u2500\u2500 Fetch a single JSON file and merge into _i18n.locales[locale] \u2500\u2500\nasync function _loadLocale(locale, ns) {\n const cacheKey = ns ? `${locale}:${ns}` : locale;\n if (_config.i18n.cache && _i18nCache.has(cacheKey)) return;\n\n // Percent-encode placeholder values before substituting into the loadPath,\n // mirroring _interpolate() which encodes every {placeholder}. This neutralizes\n // path traversal (e.g. \"../../../account/settings\") even when supportedLocales\n // is unset \u2014 \"/\" becomes \"%2F\", so the \"../\" cannot escape the configured path.\n let url = _config.i18n.loadPath.replace(\"{locale}\", encodeURIComponent(locale));\n if (ns) url = url.replace(\"{ns}\", encodeURIComponent(ns));\n else if (url.includes(\"{ns}\")) return; // no namespace to substitute\n\n\n try {\n const res = await fetch(url);\n if (!res.ok) { _warn(`i18n: failed to load ${url} (${res.status})`); return; }\n const data = await res.json();\n _i18n.locales[locale] = _deepMerge(_i18n.locales[locale] || {}, data);\n _i18nTranslationCache.clear();\n if (_config.i18n.cache) _i18nCache.set(cacheKey, data);\n } catch (e) {\n _warn(`i18n: error loading ${url}`, e);\n }\n}\n\n// \u2500\u2500\u2500 Load all configured data for a locale (flat or all namespaces) \u2500\u2500\nexport async function _loadI18nForLocale(locale) {\n if (!_config.i18n.loadPath) return;\n const ns = _config.i18n.ns;\n if (!ns.length || !_config.i18n.loadPath.includes(\"{ns}\")) {\n await _loadLocale(locale, null);\n } else {\n await Promise.all(ns.map((n) => _loadLocale(locale, n)));\n }\n}\n\n// \u2500\u2500\u2500 Load a single namespace for current + fallback locales \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport async function _loadI18nNamespace(ns) {\n if (!_config.i18n.loadPath) return;\n _loadedNs.add(ns);\n const locales = new Set([_i18n.locale, _config.i18n.fallbackLocale]);\n await Promise.all([...locales].map((l) => _loadLocale(l, ns)));\n}\n\nexport const _i18n = {\n _locale: \"en\",\n _locales: {},\n get locales() {\n return this._locales;\n },\n set locales(v) {\n this._locales = v;\n _i18nTranslationCache.clear();\n },\n get locale() {\n return this._locale;\n },\n set locale(v) {\n // Reject unsupported locales when supportedLocales is configured (a non-empty\n // array). Untrusted input (e.g. $route.params.lang) flowing into setLocale must\n // not load an attacker-chosen path. When supportedLocales is unset, the\n // encodeURIComponent in _loadLocale is the defense-in-depth fallback.\n const supported = _config.i18n.supportedLocales;\n if (Array.isArray(supported) && supported.length && !supported.includes(v)) {\n _warn(`i18n: locale \"${v}\" is not in supportedLocales; ignoring`);\n return;\n }\n if (this._locale !== v) {\n this._locale = v;\n _i18nTranslationCache.clear();\n if (_config.i18n.persist && typeof localStorage !== \"undefined\") {\n try { localStorage.setItem(\"nojs-locale\", v); } catch (_) {}\n }\n if (_config.i18n.loadPath) {\n // Load configured ns + any route-loaded ns for the new locale\n const allNs = new Set([..._config.i18n.ns, ..._loadedNs]);\n Promise.all([...allNs].map((n) => _loadLocale(v, n))).then(() => _notifyI18n());\n } else {\n _notifyI18n();\n }\n }\n },\n t(key, params = {}) {\n // \u2500\u2500\u2500 Flat translation cache: avoid repeated split+reduce on same key \u2500\u2500\n const cacheKey = `${_i18n.locale}:${key}`;\n let msg = _i18nTranslationCache.get(cacheKey);\n if (msg === undefined) {\n const resolve = (locale) =>\n key.split(\".\").reduce((o, k) => o?.[k], _i18n.locales[locale] || {});\n msg = resolve(_i18n.locale);\n // Per-key fallback: a present bundle missing this key still falls back\n // to the fallbackLocale bundle (not just when the whole bundle is absent).\n if (\n msg == null &&\n _config.i18n.fallbackLocale &&\n _config.i18n.fallbackLocale !== _i18n.locale\n ) {\n msg = resolve(_config.i18n.fallbackLocale);\n }\n // Cache resolved value (including null for missing keys)\n _i18nTranslationCache.set(cacheKey, msg ?? null);\n }\n if (msg == null) return key;\n\n // Pluralization: \"one item | {count} items\"\n if (\n typeof msg === \"string\" &&\n msg.includes(\"|\") &&\n params.count != null\n ) {\n const forms = msg.split(\"|\").map((s) => s.trim());\n msg = Number(params.count) === 1 ? forms[0] : forms[1] || forms[0];\n }\n\n // Interpolation: {name}\n if (typeof msg === \"string\") {\n msg = msg.replace(/\\{(\\w+)\\}/g, (_, k) =>\n params[k] != null ? params[k] : \"\",\n );\n }\n return msg;\n },\n};\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// $i18n REACTIVE PROXY\n// Exposes translation data as dot-notation properties so i18n keys can\n// be used in any expression context (state, bind, computed, watch, etc.)\n// Usage: $i18n.shell.sidebar.introduction \u2192 resolved translation\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nconst _I18N_RESERVED = new Set([\"locale\", \"locales\", \"t\", \"setLocale\"]);\n\nfunction _createI18nNestedProxy(path) {\n return new Proxy(Object.create(null), {\n get(_, key) {\n // Symbol properties \u2014 delegate normally (for JSON.stringify, console.log, etc.)\n if (typeof key !== \"string\") return undefined;\n\n const fullPath = path ? path + \".\" + key : key;\n const locale = _i18n.locale;\n const data = _i18n.locales[locale];\n const val = fullPath.split(\".\").reduce((o, k) => o?.[k], data || {});\n\n // If resolved value is an object \u2192 return a nested proxy (recursive)\n if (val != null && typeof val === \"object\" && !Array.isArray(val)) {\n return _createI18nNestedProxy(fullPath);\n }\n\n // If it's a string/number \u2192 return the leaf value\n if (val != null) return val;\n\n // Fallback locale: try fallbackLocale before returning undefined\n const fb = _config.i18n.fallbackLocale;\n if (fb && fb !== locale) {\n const fbData = _i18n.locales[fb];\n const fbVal = fullPath.split(\".\").reduce((o, k) => o?.[k], fbData || {});\n if (fbVal != null && typeof fbVal === \"object\" && !Array.isArray(fbVal)) {\n return _createI18nNestedProxy(fullPath);\n }\n if (fbVal != null) return fbVal;\n }\n\n return undefined;\n },\n });\n}\n\nexport const _i18nProxy = new Proxy(_i18n, {\n get(target, key) {\n // Symbol properties \u2014 delegate to _i18n for JSON.stringify, console.log, etc.\n if (typeof key !== \"string\") return target[key];\n\n // Reserved properties \u2014 delegate to _i18n directly\n if (key === \"locale\" || key === \"locales\" || key === \"t\") {\n return target[key];\n }\n\n // setLocale \u2014 a convenience method that sets _i18n.locale\n if (key === \"setLocale\") {\n return (value) => { target.locale = value; };\n }\n\n // Unknown string properties \u2192 resolve into translation data\n const locale = target.locale;\n const data = target.locales[locale];\n const val = data?.[key];\n\n // If the resolved value is an object \u2192 return a nested proxy\n if (val != null && typeof val === \"object\" && !Array.isArray(val)) {\n return _createI18nNestedProxy(key);\n }\n\n // If it's a string/number \u2192 return the leaf value\n if (val != null) return val;\n\n // Fallback locale\n const fb = _config.i18n.fallbackLocale;\n if (fb && fb !== locale) {\n const fbData = target.locales[fb];\n const fbVal = fbData?.[key];\n if (fbVal != null && typeof fbVal === \"object\" && !Array.isArray(fbVal)) {\n return _createI18nNestedProxy(key);\n }\n if (fbVal != null) return fbVal;\n }\n\n return undefined;\n },\n});\n", "// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// No.JS \u2014 Module Entry Point\n// For npm/ESM/CJS consumers: import NoJS from 'nojs'\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n// Core modules\nimport {\n _config,\n _filters,\n _validators,\n _interceptors,\n _eventBus,\n _stores,\n _refs,\n _routerInstance,\n setRouterInstance,\n _log,\n _warn,\n _notifyStoreWatchers,\n _plugins,\n _globals,\n _globalOwners,\n _disposing,\n _setDisposing,\n _currentPluginName,\n _setCurrentPluginName,\n _emitEvent,\n _CANCEL,\n _RESPOND,\n _REPLACE,\n _onDispose,\n _stripBase,\n} from \"./globals.js\";\nimport { _i18n, _loadI18nForLocale } from \"./i18n.js\";\nimport { createContext } from \"./context.js\";\nimport { evaluate, resolve, _execStatement } from \"./evaluate.js\";\nimport { findContext, _loadRemoteTemplates, _loadRemoteTemplatesPhase1, _loadRemoteTemplatesPhase2, _processTemplateIncludes, _cloneTemplate } from \"./dom.js\";\nimport { registerDirective, processTree, _removeCoreDirective, _disposeChildren, _disposeTree } from \"./registry.js\";\nimport { _createRouter } from \"./router.js\";\nimport { initDevtools, destroyDevtools, _devtoolsEmit } from \"./devtools.js\";\n\n// Side-effect imports: register built-in filters\nimport \"./filters.js\";\n\n// Side-effect imports: register all built-in directives\nimport \"./directives/state.js\";\nimport \"./directives/http.js\";\nimport \"./directives/sse.js\";\nimport \"./directives/binding.js\";\nimport \"./directives/conditionals.js\";\nimport \"./directives/loops.js\";\nimport \"./directives/styling.js\";\nimport \"./directives/events.js\";\nimport \"./directives/refs.js\";\nimport \"./directives/validate-stub.js\";\nimport \"./directives/error-boundary.js\";\nimport \"./directives/i18n.js\";\nimport \"./directives/dnd-stub.js\";\nimport \"./directives/head.js\";\n\n// Lock core directives \u2014 plugins can only register NEW names\nimport { _freezeDirectives } from \"./registry.js\";\n_freezeDirectives();\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// PLUGIN SYSTEM INTERNALS\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nlet _initPromise = null;\nlet _configLocked = false;\n\n// Keep in sync with context.js proxy handler $xxx variables.\n// Any new $xxx context variable requires adding xxx to this list.\nconst _RESERVED_GLOBAL_NAMES = new Set([\n \"store\", \"route\", \"router\", \"i18n\", \"refs\", \"form\", \"sse\", \"parent\",\n \"watch\", \"set\", \"notify\", \"raw\", \"isProxy\", \"listeners\",\n \"app\", \"config\", \"env\", \"debug\", \"version\", \"plugins\", \"globals\",\n \"el\", \"event\", \"self\", \"this\", \"super\", \"window\", \"document\",\n \"toString\", \"valueOf\", \"hasOwnProperty\",\n]);\n\nconst _DANGEROUS_REFS = typeof window !== \"undefined\"\n ? new Set([eval, Function, window.eval, window.Function].filter(Boolean))\n : new Set();\n\nfunction _isUnsafeGlobalValue(value) {\n return _DANGEROUS_REFS.has(value);\n}\n\nconst _FORBIDDEN_GLOBAL_KEYS = new Set([\"__proto__\", \"constructor\", \"prototype\"]);\n\n// Built-in filter names \u2014 prevent user code from overwriting core filters\nconst _BUILTIN_FILTER_NAMES = new Set([\n \"uppercase\", \"lowercase\", \"capitalize\", \"truncate\", \"trim\", \"stripHtml\",\n \"slugify\", \"nl\", \"nl2br\", \"encodeUri\", \"number\", \"currency\", \"percent\",\n \"filesize\", \"ordinal\", \"count\", \"first\", \"last\", \"join\", \"reverse\",\n \"unique\", \"sortBy\", \"where\", \"pluck\", \"keys\", \"values\", \"json\", \"debug\",\n \"default\", \"date\", \"datetime\", \"relative\", \"fromNow\",\n]);\n\n// Built-in validator names \u2014 prevent user code from overwriting core validators\n// (core validators are registered by @no-js-dev/nojs-elements)\nconst _BUILTIN_VALIDATOR_NAMES = new Set([\n \"required\", \"email\", \"url\", \"min\", \"max\", \"minlength\", \"maxlength\",\n \"pattern\", \"match\", \"number\", \"integer\", \"alpha\", \"alphanumeric\",\n]);\nfunction _deepCheckUnsafe(obj, seen = new Set()) {\n if (!obj || typeof obj !== \"object\" || seen.has(obj)) return;\n seen.add(obj);\n for (const key of Object.keys(obj)) {\n if (_FORBIDDEN_GLOBAL_KEYS.has(key)) {\n _warn(\"NoJS.global(): value contains a forbidden key: \" + key);\n throw new Error(\"unsafe_global\");\n }\n }\n for (const val of Object.values(obj)) {\n if (_isUnsafeGlobalValue(val)) {\n _warn(\"NoJS.global(): value contains a forbidden reference (eval/Function).\");\n throw new Error(\"unsafe_global\");\n }\n if (val && typeof val === \"object\") _deepCheckUnsafe(val, seen);\n }\n}\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// PUBLIC API\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nfunction _getDefaultRoutePath() {\n if (typeof window === \"undefined\") return null;\n const routerCfg = _config.router || {};\n if (routerCfg.useHash) {\n return window.location.hash.slice(1) || \"/\";\n }\n return _stripBase(window.location.pathname);\n}\n\nconst NoJS = {\n get baseApiUrl() {\n return _config.baseApiUrl;\n },\n set baseApiUrl(v) {\n _config.baseApiUrl = v;\n },\n\n get locale() {\n return _i18n.locale;\n },\n set locale(v) {\n _i18n.locale = v;\n },\n\n config(opts = {}) {\n // Block security-critical config changes after init\n if (_configLocked) {\n const locked = [\"sanitize\", \"dangerouslyDisableSanitize\", \"sanitizeHtml\"];\n for (const key of locked) {\n if (key in opts) {\n _warn(`config.${key} cannot be changed after init()`);\n delete opts[key];\n }\n }\n }\n // Save nested objects before shallow assign overwrites them\n const prevHeaders = { ..._config.headers };\n const prevCache = { ..._config.cache };\n const prevTemplates = { ..._config.templates };\n const prevRouter = { ..._config.router };\n const prevI18n = { ..._config.i18n };\n if (\"csp\" in opts) {\n _warn(\"csp config option removed \u2014 No.JS is now CSP-safe by default\");\n delete opts.csp;\n }\n if (opts.exprCacheSize !== undefined) {\n const n = parseInt(opts.exprCacheSize);\n opts.exprCacheSize = (Number.isFinite(n) && n > 0) ? n : 500;\n }\n Object.assign(_config, opts);\n if (opts.sanitize === false) {\n _warn('sanitize:false is deprecated \u2014 use dangerouslyDisableSanitize:true to make the risk explicit.');\n }\n if (opts.headers)\n _config.headers = { ...prevHeaders, ...opts.headers };\n if (opts.csrf) _config.csrf = opts.csrf;\n if (opts.cache) _config.cache = { ...prevCache, ...opts.cache };\n if (opts.templates) _config.templates = { ...prevTemplates, ...opts.templates };\n if (opts.router) {\n if (\"mode\" in opts.router && !(\"useHash\" in opts.router)) {\n _log(\n 'router.mode is deprecated. Use router.useHash instead: ' +\n 'mode: \"hash\" \u2192 useHash: true, mode: \"history\" \u2192 useHash: false',\n \"warn\"\n );\n opts.router.useHash = opts.router.mode === \"hash\";\n delete opts.router.mode;\n }\n _config.router = { ...prevRouter, ...opts.router };\n }\n if (opts.i18n) {\n _config.i18n = { ...prevI18n, ...opts.i18n };\n _i18n.locale = opts.i18n.defaultLocale || _i18n.locale;\n }\n if (opts.stores) {\n for (const [name, data] of Object.entries(opts.stores)) {\n if (!_stores[name]) {\n _stores[name] = createContext(data || {});\n _devtoolsEmit(\"store:created\", { name, keys: Object.keys(data || {}) });\n }\n }\n delete _config.stores;\n }\n },\n\n // \u2500\u2500\u2500 Plugin registration \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n use(plugin, options = {}) {\n if (_disposing) {\n _warn(\"Cannot install plugins during dispose.\");\n return;\n }\n\n // Normalize function shorthand (named functions only)\n if (typeof plugin === \"function\") {\n if (!plugin.name || plugin.name === \"anonymous\") {\n _warn('Plugin must have a unique, non-empty name. Use { name: \"my-plugin\", install: fn }.');\n return;\n }\n plugin = { name: plugin.name, install: plugin };\n }\n\n // Validate name\n if (!plugin.name || typeof plugin.name !== \"string\" || plugin.name === \"anonymous\") {\n _warn('Plugin must have a unique, non-empty name.');\n return;\n }\n\n // Duplicate detection with object identity comparison\n if (_plugins.has(plugin.name)) {\n const existing = _plugins.get(plugin.name);\n if (existing.plugin !== plugin) {\n _warn(`Plugin \"${plugin.name}\" name collision: a different plugin with this name is already installed.`);\n }\n return;\n }\n\n // Log declared capabilities in debug mode\n if (plugin.capabilities && _config.debug) {\n _log(`Plugin \"${plugin.name}\" declares capabilities:`, plugin.capabilities);\n }\n\n // Warn on trusted access\n if (options.trusted === true) {\n _warn(`WARNING: Plugin \"${plugin.name}\" installed with trusted access to sensitive HTTP headers.`);\n }\n\n // Set current plugin name for interceptor tracking\n _setCurrentPluginName(plugin.name);\n try {\n plugin.install(NoJS, options);\n } finally {\n _setCurrentPluginName(null);\n }\n\n _plugins.set(plugin.name, { plugin, options });\n\n // If already initialized and plugin has init, await then call\n if (_initPromise && plugin.init) {\n _initPromise.then(() => plugin.init(NoJS)).catch(e => _warn(`Plugin \"${plugin.name}\" init error:`, e.message));\n }\n\n _log(`Plugin \"${plugin.name}\" installed.`);\n },\n\n // \u2500\u2500\u2500 Plugin globals \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n global(name, value) {\n if (typeof name !== \"string\" || !name) {\n _warn(\"NoJS.global() requires a non-empty string name.\");\n return;\n }\n\n // Block prototype pollution vectors\n if (name === \"__proto__\" || name === \"constructor\" || name === \"prototype\") {\n _warn(`NoJS.global(): \"${name}\" is a forbidden name.`);\n return;\n }\n\n // Block reserved names\n if (_RESERVED_GLOBAL_NAMES.has(name)) {\n _warn(`NoJS.global(): \"${name}\" is reserved and cannot be used.`);\n return;\n }\n\n // Validate identifier characters\n if (!/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name)) {\n _warn(`NoJS.global(): \"${name}\" is not a valid identifier.`);\n return;\n }\n\n // Block dangerous function references\n if (_isUnsafeGlobalValue(value)) {\n _warn(`NoJS.global(): value for \"${name}\" is a forbidden reference (eval/Function).`);\n return;\n }\n\n // Warn on overwrite by a different plugin\n if (name in _globals && _globalOwners[name] && _globalOwners[name] !== _currentPluginName) {\n _warn(`Global \"$${name}\" owned by \"${_globalOwners[name]}\" is being overwritten.`);\n }\n\n // Sanitize object values to strip __proto__ keys\n if (value && typeof value === \"object\" && !value.__isProxy) {\n try {\n value = JSON.parse(JSON.stringify(value));\n } catch {\n // Non-serializable objects \u2014 check for dangerous function references\n try {\n _deepCheckUnsafe(value);\n } catch (safetyErr) {\n if (safetyErr.message === \"unsafe_global\") return;\n // Other errors pass through\n }\n }\n // Wrap in reactive context for deep reactivity\n value = createContext(value);\n } else if (value && typeof value === \"object\" && value.__isProxy) {\n // Already a reactive proxy \u2014 sanitization branch above is skipped, so run the\n // recursive unsafe-reference / prototype-pollution check on the raw target\n // to avoid storing nested eval/Function refs or polluted prototypes verbatim.\n try {\n _deepCheckUnsafe(value.__raw ?? value);\n } catch (safetyErr) {\n if (safetyErr.message === \"unsafe_global\") return;\n // Other errors pass through\n }\n }\n\n _globals[name] = value;\n if (_currentPluginName) _globalOwners[name] = _currentPluginName;\n\n // Notify all store watchers since globals are scope-wide\n _notifyStoreWatchers();\n\n _devtoolsEmit(\"global:set\", { name, hasValue: value != null });\n _log(`Global \"$${name}\" registered.`);\n },\n\n // \u2500\u2500\u2500 App teardown \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n async dispose() {\n _setDisposing(true);\n try {\n // Wait for any in-flight init() to settle before tearing down \u2014 otherwise the\n // init IIFE keeps running against state we are about to clear and re-locks config\n // after teardown. _setDisposing(true) above prevents new plugin installs meanwhile.\n if (_initPromise) {\n try { await _initPromise; } catch { /* init failure is irrelevant during dispose */ }\n }\n\n // Dispose plugins in reverse installation order\n const entries = [..._plugins.entries()].reverse();\n for (const [name, { plugin }] of entries) {\n if (plugin.dispose) {\n try {\n let timeoutId;\n await Promise.race([\n Promise.resolve(plugin.dispose(NoJS)).finally(() => clearTimeout(timeoutId)),\n new Promise((_, reject) => {\n timeoutId = setTimeout(() => reject(new Error(\"Dispose timeout\")), 3000);\n }),\n ]);\n } catch (e) {\n _warn(`Plugin \"${name}\" dispose error:`, e.message);\n }\n }\n }\n _plugins.clear();\n for (const k in _globals) delete _globals[k];\n for (const k in _globalOwners) delete _globalOwners[k];\n\n // Clear interceptors\n _interceptors.request.length = 0;\n _interceptors.response.length = 0;\n\n // Destroy router listeners\n if (_routerInstance && _routerInstance.destroy) {\n _routerInstance.destroy();\n }\n setRouterInstance(null);\n\n // Clean up devtools listener\n destroyDevtools();\n\n _configLocked = false;\n\n _initPromise = null;\n _log(\"Disposed.\");\n } finally {\n _setDisposing(false);\n }\n },\n\n // \u2500\u2500\u2500 Init (Promise-based lifecycle) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n async init(root) {\n if (typeof document === \"undefined\") return;\n if (_initPromise) return _initPromise;\n // Lock security-critical config synchronously, BEFORE the async init body awaits\n // anything \u2014 otherwise sanitize flags remain mutable during the async init window.\n _configLocked = true;\n _initPromise = (async () => {\n root = root || document.body;\n _log(\"Initializing...\");\n\n // Load external locale files (blocking \u2014 translations must be available for first paint)\n if (_config.i18n.loadPath) {\n const locales = new Set([_i18n.locale, _config.i18n.fallbackLocale]);\n await Promise.all([...locales].map((l) => _loadI18nForLocale(l)));\n }\n\n // Inline template includes (e.g. skeletons) \u2014 synchronous, before any fetch\n _processTemplateIncludes(root);\n\n // Determine active route path for phase 1 prioritization\n const defaultRoutePath = _getDefaultRoutePath();\n\n // Phase 1 (blocking): priority + non-route + default route templates\n await _loadRemoteTemplatesPhase1(defaultRoutePath);\n\n // Check for route-view outlets to activate router\n if (document.querySelector(\"[route-view]\")) {\n setRouterInstance(_createRouter());\n }\n\n processTree(root); // \u2190 first paint happens here\n\n // Init router after tree is processed\n if (_routerInstance) await _routerInstance.init();\n\n _log(\"Initialized.\");\n\n // Phase 2 (non-blocking): background preload remaining route templates\n _loadRemoteTemplatesPhase2();\n\n // DevTools integration\n initDevtools(NoJS);\n\n // Plugin init hooks\n for (const [, { plugin }] of _plugins) {\n if (plugin.init) await plugin.init(NoJS);\n }\n _emitEvent(\"plugins:ready\");\n })();\n return _initPromise;\n },\n\n // Register custom directive\n directive(name, handler) {\n registerDirective(name, handler);\n },\n\n // Register custom filter\n filter(name, fn) {\n if (typeof name !== \"string\" || !name) {\n throw new TypeError(\"NoJS.filter() requires a non-empty string name.\");\n }\n if (typeof fn !== \"function\") {\n throw new TypeError(`NoJS.filter(): \"${name}\" handler must be a function.`);\n }\n if (_BUILTIN_FILTER_NAMES.has(name)) {\n _warn(`NoJS.filter(): \"${name}\" is a built-in filter and cannot be overridden.`);\n return;\n }\n _filters[name] = fn;\n },\n\n // Register custom validator\n validator(name, fn) {\n if (typeof name !== \"string\" || !name) {\n throw new TypeError(\"NoJS.validator() requires a non-empty string name.\");\n }\n if (typeof fn !== \"function\") {\n throw new TypeError(`NoJS.validator(): \"${name}\" handler must be a function.`);\n }\n if (_BUILTIN_VALIDATOR_NAMES.has(name)) {\n _warn(`NoJS.validator(): \"${name}\" is a built-in validator and cannot be overridden.`);\n return;\n }\n _validators[name] = fn;\n },\n\n // i18n\n i18n(opts) {\n // Set config options BEFORE locale (setter checks loadPath)\n if (opts.loadPath != null) _config.i18n.loadPath = opts.loadPath;\n if (opts.ns) _config.i18n.ns = opts.ns;\n if (Array.isArray(opts.supportedLocales)) _config.i18n.supportedLocales = opts.supportedLocales;\n if (opts.cache != null) _config.i18n.cache = opts.cache;\n if (opts.persist != null) _config.i18n.persist = opts.persist;\n if (opts.locales) _i18n.locales = opts.locales;\n if (opts.fallbackLocale) _config.i18n.fallbackLocale = opts.fallbackLocale;\n\n // Set defaultLocale WITHOUT the setter (avoids overwriting localStorage)\n if (opts.defaultLocale) _i18n._locale = opts.defaultLocale;\n\n // Restore persisted locale (highest priority)\n if (_config.i18n.persist && typeof localStorage !== \"undefined\") {\n try {\n const saved = localStorage.getItem(\"nojs-locale\");\n if (saved) { _i18n._locale = saved; return; }\n } catch (_) {}\n }\n\n // Detect browser language (second priority)\n if (opts.detectBrowser) {\n const browserLang =\n (typeof navigator !== \"undefined\" && navigator.language) || \"en\";\n const prefix = browserLang.split(\"-\")[0];\n // A locale is a match if it exists in the in-memory bundle map (inline\n // `locales`) OR is declared in `supportedLocales`. The latter is required\n // for lazy `loadPath` setups where no bundles are loaded yet at this point.\n const list = Array.isArray(_config.i18n.supportedLocales)\n ? _config.i18n.supportedLocales\n : [];\n const has = (loc) => _i18n.locales[loc] || list.includes(loc);\n if (has(browserLang)) _i18n._locale = browserLang;\n else if (has(prefix)) _i18n._locale = prefix;\n }\n },\n\n // Event bus\n on(event, fn) {\n if (!_eventBus[event]) _eventBus[event] = [];\n if (_eventBus[event].length >= _config.maxEventListeners) {\n _warn(\n 'MaxListenersExceeded: event \"' + event + '\" has ' + _eventBus[event].length +\n ' listeners (max ' + _config.maxEventListeners + '). Possible memory leak.'\n );\n }\n _eventBus[event].push(fn);\n return () => {\n _eventBus[event] = _eventBus[event].filter((f) => f !== fn);\n };\n },\n\n // Request/response interceptors (with plugin tracking)\n interceptor(type, fn) {\n if (_disposing) {\n _warn(\"Cannot register interceptors during dispose.\");\n return;\n }\n if (!_interceptors[type]) {\n _warn(`NoJS.interceptor(): unknown type \"${type}\" (expected \"request\" or \"response\").`);\n return;\n }\n _interceptors[type].push(\n _currentPluginName ? { fn, pluginName: _currentPluginName } : fn\n );\n },\n\n // Access global stores\n get store() {\n return _stores;\n },\n\n // Notify global store watchers (for external JS mutations)\n notify() {\n _notifyStoreWatchers();\n },\n\n // Access router\n get router() {\n return _routerInstance;\n },\n\n // Utilities (for custom directives)\n createContext,\n evaluate,\n findContext,\n processTree,\n resolve,\n\n // Internal API for trusted plugins (e.g. NoJS-Elements)\n get internals() {\n return Object.freeze({\n execStatement: _execStatement,\n cloneTemplate: _cloneTemplate,\n disposeChildren: _disposeChildren,\n disposeTree: _disposeTree,\n warn: _warn,\n validators: Object.freeze({..._validators}),\n removeCoreDirective: _removeCoreDirective,\n onDispose: _onDispose,\n });\n },\n\n // Version\n version: \"1.20.0\",\n};\n\n// Expose sentinel symbols as read-only properties\nObject.defineProperty(NoJS, \"CANCEL\", { value: _CANCEL, writable: false, configurable: false });\nObject.defineProperty(NoJS, \"RESPOND\", { value: _RESPOND, writable: false, configurable: false });\nObject.defineProperty(NoJS, \"REPLACE\", { value: _REPLACE, writable: false, configurable: false });\n\n// Backward-compat: _initialized getter/setter (tests use `NoJS._initialized = false` to reset)\nObject.defineProperty(NoJS, \"_initialized\", {\n get() { return _initPromise !== null; },\n set(v) { if (!v) _initPromise = null; },\n configurable: true,\n});\n\nexport default NoJS;\n", "// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// REACTIVE CONTEXT\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nimport { _config, _stores, _refs, _routerInstance, _currentEl, _globals, _SENSITIVE_KEYS, _warn } from \"./globals.js\";\nimport { _i18nProxy } from \"./i18n.js\";\nimport { _devtoolsEmit, _ctxRegistry } from \"./devtools.js\";\n\nconst _FORBIDDEN_CTX_KEYS = new Set([\"__proto__\", \"constructor\", \"prototype\"]);\n\nlet _batchDepth = 0;\nlet _batchQueue = new Set();\nlet _ctxId = 0;\nlet _ctxGeneration = 0;\n\nexport function _resetCtxId() { _ctxId = 0; }\n\nexport function _startBatch() {\n _batchDepth++;\n _devtoolsEmit(\"batch:start\", { depth: _batchDepth });\n}\n\nexport function _endBatch() {\n _batchDepth--;\n if (_batchDepth === 0 && _batchQueue.size > 0) {\n _devtoolsEmit(\"batch:end\", { depth: 0, queueSize: _batchQueue.size });\n // Drain with the depth held above zero so notifies fired by running\n // listeners re-queue into the live Set (deduped) instead of executing\n // synchronously \u2014 each listener runs once per settled state instead of\n // once per notification source.\n let rounds = 0;\n while (_batchQueue.size > 0) {\n if (++rounds > 100) {\n _warn(\"batch drain exceeded 100 rounds \u2014 possible update cycle; aborting drain\");\n _batchQueue = new Set();\n break;\n }\n const fns = _batchQueue;\n _batchQueue = new Set();\n _batchDepth++;\n try {\n // Drain defensively: a throwing listener must not drop the rest of the\n // queued updates (Safety Rule 8 \u2014 one broken attribute must not abort the page).\n fns.forEach((fn) => {\n if (fn._el && !fn._el.isConnected) return;\n // Running now supersedes a re-queue made by an earlier listener\n // in this same round; re-queues that happen after fn ran (or\n // during it) stay for the next round.\n _batchQueue.delete(fn);\n try {\n fn();\n } catch (err) {\n _warn(\"batched listener threw; continuing with remaining listeners:\", err);\n }\n });\n } finally {\n _batchDepth--;\n }\n }\n }\n}\n\n// Per-context bookkeeping lives in one meta record behind a Symbol key on the\n// raw object (invisible to Object.keys / _collectKeys). Everything a context\n// used to close over per instance \u2014 parent link, listeners Set, $watch/$set/\n// $notify closures \u2014 is either stored here or created lazily on first access,\n// so a context that is only ever read (the common loop-row case before its\n// bindings attach) costs raw + meta + Proxy and nothing else.\nconst META = Symbol(\"no.js\");\n\n// `key` is the changed property when the notification comes from a single\n// write (set trap / $set); undefined for manual $notify() and bulk changes,\n// which fire every listener. Key-scoped listeners (fn._keys, assigned in\n// _watchExpr) are skipped when the changed key cannot affect them.\n//\n// Every notify runs through the batch queue: outside an explicit batch it\n// opens its own, so listeners always execute from _endBatch's drain. A\n// notify fired by a running listener (e.g. computed's $set cascading into\n// a watch) queues for the next drain round instead of recursing \u2014 or being\n// dropped, as the old reentrancy guard did. Still fully synchronous: the\n// drain completes before the outermost notify/set returns.\nfunction _notifyMeta(meta, key) {\n const listeners = meta.listeners;\n if (!listeners || listeners.size === 0) return;\n const ownBatch = _batchDepth === 0;\n if (ownBatch) _startBatch();\n for (const fn of listeners) {\n if (fn._el && !fn._el.isConnected) { listeners.delete(fn); continue; }\n if (key !== undefined && fn._keys && !fn._keys.has(key)) continue;\n _batchQueue.add(fn);\n }\n if (ownBatch) _endBatch();\n}\n\n// One handler object shared by every context Proxy. The old per-context\n// handler literal carried three trap closures per instance; here the traps\n// reach per-context state through target[META] instead.\nconst _sharedHandler = {\n get(target, key) {\n // Fast path: non-string keys (Symbols) go straight to target\n if (typeof key !== \"string\") return target[key];\n\n const c0 = key.charCodeAt(0);\n\n // Fast path: keys not starting with '$' (36) or '_' (95) are user properties.\n // Skip all special-key checks entirely.\n if (c0 !== 36 /* $ */ && c0 !== 95 /* _ */) {\n if (key in target) return target[key];\n const parent = target[META].parent;\n if (parent && parent.__isProxy) return parent[key];\n return undefined;\n }\n\n // Dunder keys (__isProxy, __raw, __listeners)\n if (c0 === 95) {\n if (key === \"__isProxy\") return true;\n if (key === \"__raw\") return target;\n if (key === \"__listeners\") {\n // Materialize on access: devtools counting and disposal clearing see\n // the same always-a-Set shape the eager version provided.\n const meta = target[META];\n return meta.listeners || (meta.listeners = new Set());\n }\n // Fall through to target/parent lookup for other _ keys\n if (key in target) return target[key];\n const parentU = target[META].parent;\n if (parentU && parentU.__isProxy) return parentU[key];\n return undefined;\n }\n\n // $ prefix: dispatch core context keys via switch for O(1) branching.\n // The $watch/$notify/$set closures are created on first access and cached\n // on meta \u2014 stable identity, zero cost for contexts that never use them.\n switch (key) {\n case \"$watch\": {\n const meta = target[META];\n return meta.watchFn || (meta.watchFn = (fn) => {\n if (_currentEl) fn._el = _currentEl;\n (meta.listeners || (meta.listeners = new Set())).add(fn);\n return () => { if (meta.listeners) meta.listeners.delete(fn); };\n });\n }\n case \"$notify\": {\n const meta = target[META];\n // Only a string arg narrows to key-scoped listeners. $notify()\n // historically took no argument, so userland calls passing anything\n // else ($notify($event), $notify(0)) must keep firing every\n // listener rather than silently matching nothing.\n return meta.notifyFn || (meta.notifyFn =\n (k) => _notifyMeta(meta, typeof k === \"string\" ? k : undefined));\n }\n case \"$set\": {\n const meta = target[META];\n return meta.setFn || (meta.setFn = (k, v) => {\n const parts = k.split(\".\");\n if (parts.some(p => _FORBIDDEN_CTX_KEYS.has(p))) return;\n if (parts.length === 1) {\n meta.proxy[k] = v;\n } else {\n let obj = meta.proxy;\n for (let i = 0; i < parts.length - 1; i++) {\n obj = obj[parts[i]];\n if (obj == null) return;\n }\n const lastKey = parts[parts.length - 1];\n const old = obj[lastKey];\n obj[lastKey] = v;\n if (old !== v) {\n // Bump the generation so _collectKeys' cache invalidates, mirroring the\n // set trap. Otherwise a nested $set leaves stale vals cached.\n _ctxGeneration++;\n _notifyMeta(meta, parts[0]);\n }\n }\n });\n }\n case \"$parent\": return target[META].parent;\n case \"$refs\": return _refs;\n case \"$store\": return _stores;\n case \"$route\": return _routerInstance ? _routerInstance.current : {};\n case \"$router\": return _routerInstance;\n case \"$i18n\": return _i18nProxy;\n case \"$form\": return target.$form || null;\n case \"$sse\": return target.$sse || null;\n }\n\n // Plugin globals fallback (after all core $ checks)\n const globalKey = key.slice(1);\n if (globalKey in _globals) return _globals[globalKey];\n\n // Target / parent chain lookup\n if (key in target) return target[key];\n const parent = target[META].parent;\n if (parent && parent.__isProxy) return parent[key];\n return undefined;\n },\n set(target, key, value) {\n if (typeof key === \"string\" && _FORBIDDEN_CTX_KEYS.has(key)) return true;\n const old = target[key];\n target[key] = value;\n if (old !== value) {\n _ctxGeneration++;\n _notifyMeta(target[META], typeof key === \"string\" ? key : undefined);\n if (_config.devtools) {\n let isSensitive = false;\n if (typeof key === \"string\") {\n const lk = key.toLowerCase();\n for (const s of _SENSITIVE_KEYS) {\n if (lk.includes(s)) { isSensitive = true; break; }\n }\n }\n _devtoolsEmit(\"ctx:updated\", {\n id: target.__devtoolsId,\n key,\n oldValue: isSensitive ? \"[REDACTED]\" : old,\n newValue: isSensitive ? \"[REDACTED]\" : value,\n });\n }\n }\n return true;\n },\n has(target, key) {\n if (key in target) return true;\n if (typeof key === \"string\" && key.startsWith(\"$\") && key.slice(1) in _globals) return true;\n const parent = target[META].parent;\n if (parent && parent.__isProxy) return key in parent;\n return false;\n },\n};\n\nexport function createContext(data = {}, parent = null) {\n const raw = {};\n Object.assign(raw, data);\n if (_config.devtools) raw.__devtoolsId = ++_ctxId;\n\n const meta = {\n parent,\n proxy: null,\n listeners: null,\n watchFn: null,\n notifyFn: null,\n setFn: null,\n };\n raw[META] = meta;\n const proxy = new Proxy(raw, _sharedHandler);\n meta.proxy = proxy;\n\n if (_config.devtools && raw.__devtoolsId) {\n _ctxRegistry.set(raw.__devtoolsId, proxy);\n _devtoolsEmit(\"ctx:created\", {\n id: raw.__devtoolsId,\n parentId: parent?.__raw?.__devtoolsId ?? null,\n keys: Object.keys(data),\n elementTag: _currentEl?.tagName?.toLowerCase() ?? null,\n });\n }\n\n return proxy;\n}\n\n// Collect all keys from a context + its parent chain\n// Result is cached per context and invalidated on any reactive mutation.\nexport function _collectKeys(ctx) {\n const cache = ctx.__raw.__collectKeysCache;\n if (cache && cache.gen === _ctxGeneration) return cache.result;\n\n const allKeys = new Set();\n const allVals = {};\n let c = ctx;\n while (c && c.__isProxy) {\n const raw = c.__raw;\n for (const k of Object.keys(raw)) {\n if (!allKeys.has(k)) {\n allKeys.add(k);\n allVals[k] = raw[k];\n }\n }\n c = c.$parent;\n }\n const result = { keys: [...allKeys], vals: allVals };\n ctx.__raw.__collectKeysCache = { gen: _ctxGeneration, result };\n return result;\n}\n", "// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// DEVTOOLS PROTOCOL\n// Zero-overhead runtime inspection, mutation, and monitoring.\n// All hooks are guarded by _config.devtools \u2014 no cost when disabled.\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nimport { _config, _stores, _refs, _routerInstance, _plugins, _globals } from \"./globals.js\";\nimport { _i18n } from \"./i18n.js\";\n\n// \u2500\u2500\u2500 Context registry (populated by createContext when devtools enabled) \u2500\u2500\u2500\u2500\n// Maps __devtoolsId \u2192 Proxy reference for inspect/mutate commands.\nexport const _ctxRegistry = new Map();\n\n// \u2500\u2500\u2500 Hostname guard \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Optional `hostname` param exists for unit-testing without window.location mocking.\nexport function _isLocalHostname(hostname) {\n if (hostname === undefined) {\n hostname = (typeof window !== \"undefined\" && window.location) ? window.location.hostname : \"\";\n }\n return (\n hostname === \"\" ||\n hostname === \"localhost\" ||\n hostname === \"127.0.0.1\" ||\n hostname === \"::1\" ||\n hostname === \"0.0.0.0\" ||\n hostname.endsWith(\".localhost\")\n );\n}\n\n// \u2500\u2500\u2500 Emit a devtools event \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport function _devtoolsEmit(type, data) {\n if (!_config.devtools || typeof window === \"undefined\") return;\n window.dispatchEvent(\n new CustomEvent(\"nojs:devtools\", {\n detail: { type, data, timestamp: Date.now() },\n }),\n );\n}\n\n// \u2500\u2500\u2500 Serialization helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction _safeSnapshot(proxy, seen) {\n if (!proxy || !proxy.__isProxy) return null;\n const raw = proxy.__raw;\n // Cycle detection: a self-referential reactive object would otherwise recurse\n // forever (RangeError: Maximum call stack size exceeded).\n if (!seen) seen = new WeakSet();\n if (raw && seen.has(raw)) return \"[Circular]\";\n if (raw) seen.add(raw);\n const snapshot = {};\n for (const key of Object.keys(raw)) {\n if (key.startsWith(\"__\")) continue;\n const val = raw[key];\n if (val && typeof val === \"object\" && val.__isProxy) {\n snapshot[key] = _safeSnapshot(val, seen);\n } else {\n try {\n // Verify serializable \u2014 drop functions and circular refs\n JSON.stringify(val);\n snapshot[key] = val;\n } catch {\n snapshot[key] = String(val);\n }\n }\n }\n return snapshot;\n}\n\nfunction _cssEscape(s) {\n return (typeof CSS !== \"undefined\" && typeof CSS.escape === \"function\")\n ? CSS.escape(s)\n : s;\n}\n\nfunction _elementTag(el) {\n if (!el || !el.tagName) return null;\n const tag = el.tagName.toLowerCase();\n const id = el.id ? `#${_cssEscape(el.id)}` : \"\";\n // SVG elements expose className as an SVGAnimatedString (not a string), so the\n // typeof guard skips them. Escape each class so special chars stay valid selectors.\n const cls = el.className && typeof el.className === \"string\"\n ? el.className.trim().split(/\\s+/).filter(Boolean).map((c) => \".\" + _cssEscape(c)).join(\"\")\n : \"\";\n return tag + id + cls;\n}\n\n// \u2500\u2500\u2500 Inspect commands \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction _inspectElement(selector) {\n const el = document.querySelector(selector);\n if (!el) return { error: \"Element not found\", selector };\n const ctx = el.__ctx;\n return {\n selector,\n tag: _elementTag(el),\n hasContext: !!ctx,\n contextId: ctx?.__raw?.__devtoolsId ?? null,\n data: ctx ? _safeSnapshot(ctx) : null,\n directives: [...el.attributes]\n .filter((a) => ![\"class\", \"id\", \"style\"].includes(a.name))\n .map((a) => ({ name: a.name, value: a.value })),\n };\n}\n\nfunction _inspectStore(name) {\n const store = _stores[name];\n if (!store) return { error: \"Store not found\", name };\n return {\n name,\n contextId: store.__raw?.__devtoolsId ?? null,\n data: _safeSnapshot(store),\n };\n}\n\nfunction _inspectTree(selector) {\n const root = selector ? document.querySelector(selector) : document.body;\n if (!root) return { error: \"Root not found\", selector };\n\n function walk(el) {\n const ctx = el.__ctx;\n const node = {\n tag: _elementTag(el),\n contextId: ctx?.__raw?.__devtoolsId ?? null,\n children: [],\n };\n for (const child of el.children) {\n if (child.tagName === \"TEMPLATE\" || child.tagName === \"SCRIPT\") continue;\n if (child.__ctx || child.__declared) {\n node.children.push(walk(child));\n }\n }\n return node;\n }\n\n return walk(root);\n}\n\nfunction _mutateContext(id, key, value) {\n const proxy = _ctxRegistry.get(id);\n if (!proxy) return { error: \"Context not found\", id };\n proxy[key] = value;\n return { ok: true, id, key };\n}\n\nfunction _mutateStore(name, key, value) {\n const store = _stores[name];\n if (!store) return { error: \"Store not found\", name };\n store[key] = value;\n return { ok: true, name, key };\n}\n\nfunction _getStats() {\n let listenerCount = 0;\n for (const [, proxy] of _ctxRegistry) {\n if (proxy.__listeners) listenerCount += proxy.__listeners.size;\n }\n return {\n contexts: _ctxRegistry.size,\n stores: Object.keys(_stores).length,\n listeners: listenerCount,\n refs: Object.keys(_refs).length,\n hasRouter: !!_routerInstance,\n locale: _i18n.locale,\n };\n}\n\n// \u2500\u2500\u2500 Highlight overlay \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nlet _highlightOverlay = null;\nlet _highlightTarget = null;\nlet _highlightReposition = null;\n\nfunction _positionHighlight() {\n if (!_highlightOverlay || !_highlightTarget) return;\n const rect = _highlightTarget.getBoundingClientRect();\n Object.assign(_highlightOverlay.style, {\n top: rect.top + \"px\",\n left: rect.left + \"px\",\n width: rect.width + \"px\",\n height: rect.height + \"px\",\n });\n}\n\nfunction _highlightElement(selector) {\n _unhighlight();\n const el = document.querySelector(selector);\n if (!el) return;\n _highlightTarget = el;\n _highlightOverlay = document.createElement(\"div\");\n _highlightOverlay.id = \"__nojs_devtools_highlight__\";\n Object.assign(_highlightOverlay.style, {\n position: \"fixed\",\n background: \"rgba(66, 133, 244, 0.25)\",\n border: \"2px solid rgba(66, 133, 244, 0.8)\",\n pointerEvents: \"none\",\n zIndex: \"2147483647\",\n borderRadius: \"3px\",\n });\n document.body.appendChild(_highlightOverlay);\n _positionHighlight();\n // Keep the overlay anchored to its target on scroll/resize while shown.\n _highlightReposition = () => _positionHighlight();\n window.addEventListener(\"scroll\", _highlightReposition, true);\n window.addEventListener(\"resize\", _highlightReposition);\n}\n\nfunction _unhighlight() {\n if (_highlightReposition) {\n window.removeEventListener(\"scroll\", _highlightReposition, true);\n window.removeEventListener(\"resize\", _highlightReposition);\n _highlightReposition = null;\n }\n if (_highlightOverlay) {\n _highlightOverlay.remove();\n _highlightOverlay = null;\n }\n _highlightTarget = null;\n}\n\n// \u2500\u2500\u2500 Command handler \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction _handleDevtoolsCommand(event) {\n const { command, args = {} } = event.detail || {};\n let result;\n\n switch (command) {\n case \"inspect:element\":\n result = _inspectElement(args.selector);\n break;\n case \"inspect:store\":\n result = _inspectStore(args.name);\n break;\n case \"inspect:tree\":\n result = _inspectTree(args.selector);\n break;\n case \"mutate:context\":\n result = _mutateContext(args.id, args.key, args.value);\n break;\n case \"mutate:store\":\n result = _mutateStore(args.name, args.key, args.value);\n break;\n case \"get:config\":\n result = { ..._config };\n if (result.csrf) result.csrf = { ...result.csrf, token: '[REDACTED]' };\n if (result.headers) result.headers = '[REDACTED]';\n break;\n case \"get:routes\":\n result = _routerInstance ? _routerInstance.routes || [] : [];\n break;\n case \"get:stats\":\n result = _getStats();\n break;\n case \"highlight:element\":\n _highlightElement(args.selector);\n result = { ok: true };\n break;\n case \"unhighlight\":\n _unhighlight();\n result = { ok: true };\n break;\n default:\n result = { error: \"Unknown command\", command };\n }\n\n // Respond with result\n window.dispatchEvent(\n new CustomEvent(\"nojs:devtools:response\", {\n detail: { command, result, timestamp: Date.now() },\n }),\n );\n}\n\n// \u2500\u2500\u2500 Cleanup reference \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nlet _devtoolsCleanup = null;\n\n// Called from NoJS.dispose() to remove the command listener and clean up.\nexport function destroyDevtools() {\n // Remove any lingering highlight overlay + its scroll/resize listeners.\n _unhighlight();\n if (_devtoolsCleanup) _devtoolsCleanup();\n}\n\n// \u2500\u2500\u2500 Initialization \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function initDevtools(nojs) {\n if (!_config.devtools || typeof window === \"undefined\") return;\n\n if (!_isLocalHostname()) {\n console.warn(\"[No.JS] devtools: true is ignored outside local environments. Remove devtools: true before deploying to production.\");\n return;\n }\n\n // Listen for commands (store reference for cleanup)\n window.addEventListener(\"nojs:devtools:cmd\", _handleDevtoolsCommand);\n _devtoolsCleanup = () => {\n window.removeEventListener(\"nojs:devtools:cmd\", _handleDevtoolsCommand);\n delete window.__NOJS_DEVTOOLS__;\n _devtoolsCleanup = null;\n };\n\n // Expose public API on window\n window.__NOJS_DEVTOOLS__ = {\n // Data access (read-only snapshots \u2014 no live references leak)\n get stores() {\n return Object.fromEntries(\n Object.entries(_stores).map(([k, v]) => [k, _safeSnapshot(v)])\n );\n },\n get config() {\n const c = { ..._config };\n if (c.headers) c.headers = { ...c.headers };\n if (c.router) c.router = { ...c.router };\n if (c.cache) c.cache = { ...c.cache };\n if (c.csrf) c.csrf = { ...c.csrf };\n if (c.i18n) c.i18n = { ...c.i18n };\n return c;\n },\n get refs() { return { ..._refs }; },\n router: _routerInstance,\n version: nojs.version,\n get plugins() { return new Map(_plugins); },\n get globals() { return { ..._globals }; },\n\n // Inspect API\n inspect: (selector) => _inspectElement(selector),\n inspectStore: (name) => _inspectStore(name),\n inspectTree: (selector) => _inspectTree(selector),\n stats: () => _getStats(),\n\n // Mutation API\n mutate: (id, key, value) => _mutateContext(id, key, value),\n mutateStore: (name, key, value) => _mutateStore(name, key, value),\n\n // Visual\n highlight: (selector) => _highlightElement(selector),\n unhighlight: () => _unhighlight(),\n\n // Event subscription shorthand\n on: (type, fn) => {\n const handler = (e) => {\n if (!type || e.detail.type === type) fn(e.detail);\n };\n window.addEventListener(\"nojs:devtools\", handler);\n return () => window.removeEventListener(\"nojs:devtools\", handler);\n },\n };\n\n console.log(\"[No.JS DevTools] enabled \u2014 access via window.__NOJS_DEVTOOLS__\");\n}\n", "// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// EXPRESSION EVALUATOR\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nimport { _config, _stores, _routerInstance, _filters, _warn, _notifyStoreWatchers, _extractStoreName, _globals, _setExprRootKeysFn } from \"./globals.js\";\nimport { _i18nProxy } from \"./i18n.js\";\nimport { _collectKeys, _startBatch, _endBatch } from \"./context.js\";\n\nfunction _makeCache() {\n const map = new Map();\n return {\n get(k) {\n if (!map.has(k)) return undefined;\n // Move to end so this entry is the most-recently-used\n const v = map.get(k);\n map.delete(k);\n map.set(k, v);\n return v;\n },\n has(k) { return map.has(k); },\n set(k, v) {\n const max = _config.exprCacheSize;\n if (map.has(k)) {\n map.delete(k); // refresh position before re-inserting\n } else if (map.size >= max) {\n map.delete(map.keys().next().value); // evict LRU (insertion-order first)\n }\n map.set(k, v);\n },\n get size() { return map.size; },\n };\n}\nexport const _exprCache = _makeCache();\nexport const _stmtCache = _makeCache();\n\n// \u2500\u2500 Tokenizer \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst _KEYWORDS = new Set([\"true\", \"false\", \"null\", \"undefined\", \"typeof\", \"in\", \"instanceof\"]);\nconst _FORBIDDEN = new Set([\"__proto__\", \"constructor\", \"prototype\"]);\n\n// Multi-char operators/punctuation, sorted longest-first for greedy matching\nconst _MULTI = [\"===\", \"!==\", \"...\", \"??\", \"?.\", \"==\", \"!=\", \">=\", \"<=\", \"&&\", \"||\", \"+=\", \"-=\", \"*=\", \"/=\", \"%=\", \"++\", \"--\", \"=>\"];\nconst _SINGLE_OPS = new Set([\"+\", \"-\", \"*\", \"/\", \"%\", \">\", \"<\", \"!\", \"=\", \"|\"]);\nconst _SINGLE_PUNC = new Set([\"(\", \")\", \"[\", \"]\", \"{\", \"}\", \".\", \",\", \":\", \";\", \"?\"]);\n\nfunction _tokenize(expr) {\n if (typeof expr !== \"string\") return [];\n const tokens = [];\n const len = expr.length;\n let pos = 0;\n\n while (pos < len) {\n const ch = expr[pos];\n\n // Skip whitespace\n if (ch === \" \" || ch === \"\\t\" || ch === \"\\n\" || ch === \"\\r\") { pos++; continue; }\n\n // String literals (single or double quoted)\n if (ch === \"'\" || ch === '\"') {\n const start = pos;\n const quote = ch;\n pos++;\n let value = \"\";\n while (pos < len && expr[pos] !== quote) {\n if (expr[pos] === \"\\\\\") {\n pos++;\n if (pos < len) {\n const esc = expr[pos];\n if (esc === \"n\") value += \"\\n\";\n else if (esc === \"t\") value += \"\\t\";\n else if (esc === \"r\") value += \"\\r\";\n else value += esc;\n pos++;\n }\n } else {\n value += expr[pos++];\n }\n }\n if (pos < len) pos++; // skip closing quote\n tokens.push({ type: \"String\", value, pos: start });\n continue;\n }\n\n // Template literals\n if (ch === \"`\") {\n const start = pos;\n pos++;\n const parts = [];\n const exprs = [];\n let seg = \"\";\n while (pos < len && expr[pos] !== \"`\") {\n if (expr[pos] === \"\\\\\" && pos + 1 < len) {\n const esc = expr[pos + 1];\n if (esc === \"n\") seg += \"\\n\";\n else if (esc === \"t\") seg += \"\\t\";\n else if (esc === \"r\") seg += \"\\r\";\n else seg += esc;\n pos += 2;\n } else if (expr[pos] === \"$\" && pos + 1 < len && expr[pos + 1] === \"{\") {\n parts.push(seg);\n seg = \"\";\n pos += 2; // skip ${\n // Collect expression text respecting nested braces\n let depth = 1;\n let inner = \"\";\n while (pos < len && depth > 0) {\n if (expr[pos] === \"{\") depth++;\n else if (expr[pos] === \"}\") { depth--; if (depth === 0) break; }\n else if (expr[pos] === \"'\" || expr[pos] === '\"' || expr[pos] === \"`\") {\n // skip string inside interpolation\n const q = expr[pos];\n inner += q; pos++;\n while (pos < len && expr[pos] !== q) {\n if (expr[pos] === \"\\\\\") { inner += expr[pos++]; if (pos < len) inner += expr[pos++]; }\n else inner += expr[pos++];\n }\n if (pos < len) { inner += expr[pos]; pos++; }\n continue;\n }\n inner += expr[pos++];\n }\n if (pos < len) pos++; // skip closing }\n exprs.push(_tokenize(inner));\n } else {\n seg += expr[pos++];\n }\n }\n if (pos < len) pos++; // skip closing `\n parts.push(seg);\n tokens.push({ type: \"Template\", parts, exprs, pos: start });\n continue;\n }\n\n // Numbers: starts with digit, or '.' followed by digit\n if ((ch >= \"0\" && ch <= \"9\") || (ch === \".\" && pos + 1 < len && expr[pos + 1] >= \"0\" && expr[pos + 1] <= \"9\")) {\n const start = pos;\n let num = \"\";\n while (pos < len && ((expr[pos] >= \"0\" && expr[pos] <= \"9\") || expr[pos] === \".\")) {\n num += expr[pos++];\n }\n tokens.push({ type: \"Number\", value: num, pos: start });\n continue;\n }\n\n // Identifiers / Keywords\n if ((ch >= \"a\" && ch <= \"z\") || (ch >= \"A\" && ch <= \"Z\") || ch === \"_\" || ch === \"$\") {\n const start = pos;\n let id = \"\";\n while (pos < len) {\n const c = expr[pos];\n if ((c >= \"a\" && c <= \"z\") || (c >= \"A\" && c <= \"Z\") || (c >= \"0\" && c <= \"9\") || c === \"_\" || c === \"$\") {\n id += c; pos++;\n } else break;\n }\n if (_FORBIDDEN.has(id)) tokens.push({ type: \"Forbidden\", value: id, pos: start });\n else if (_KEYWORDS.has(id)) tokens.push({ type: \"Keyword\", value: id, pos: start });\n else tokens.push({ type: \"Ident\", value: id, pos: start });\n continue;\n }\n\n // Multi-char operators / punctuation (longest first)\n let matched = false;\n for (let m = 0; m < _MULTI.length; m++) {\n const op = _MULTI[m];\n if (expr.startsWith(op, pos)) {\n const isPunc = op === \"...\" || op === \"?.\";\n tokens.push({ type: isPunc ? \"Punc\" : \"Op\", value: op, pos });\n pos += op.length;\n matched = true;\n break;\n }\n }\n if (matched) continue;\n\n // Single-char operators\n if (_SINGLE_OPS.has(ch)) {\n tokens.push({ type: \"Op\", value: ch, pos });\n pos++;\n continue;\n }\n\n // Single-char punctuation\n if (_SINGLE_PUNC.has(ch)) {\n tokens.push({ type: \"Punc\", value: ch, pos });\n pos++;\n continue;\n }\n\n // Unrecognized character \u2014 skip\n pos++;\n }\n\n return tokens;\n}\n\n// \u2500\u2500 Recursive-descent expression parser \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction _parseExpr(tokens) {\n if (!tokens || tokens.length === 0) return { type: \"Literal\", value: undefined };\n\n let pos = 0;\n\n function peek() { return tokens[pos]; }\n function next() { return tokens[pos++]; }\n\n function match(type, value) {\n const t = tokens[pos];\n if (!t) return false;\n if (value !== undefined) return t.type === type && t.value === value;\n return t.type === type;\n }\n\n function expect(type, value) {\n const t = tokens[pos];\n if (t && t.type === type && (value === undefined || t.value === value)) {\n pos++;\n return t;\n }\n return null;\n }\n\n // \u2500\u2500\u2500 Grammar rules (lowest \u2192 highest precedence) \u2500\u2500\u2500\n\n function parseExpression() {\n return parseTernary();\n }\n\n function parseTernary() {\n let node = parseNullishOr();\n if (match(\"Punc\", \"?\")) {\n next(); // consume ?\n const consequent = parseTernary();\n expect(\"Punc\", \":\");\n const alternate = parseTernary();\n node = { type: \"ConditionalExpr\", test: node, consequent, alternate };\n }\n return node;\n }\n\n function parseNullishOr() {\n let node = parseLogicalOr();\n if (match(\"Op\", \"??\")) {\n next();\n const right = parseNullishOr();\n node = { type: \"BinaryExpr\", op: \"??\", left: node, right };\n }\n return node;\n }\n\n function parseLogicalOr() {\n let node = parseLogicalAnd();\n while (match(\"Op\", \"||\")) {\n next();\n const right = parseLogicalAnd();\n node = { type: \"BinaryExpr\", op: \"||\", left: node, right };\n }\n return node;\n }\n\n function parseLogicalAnd() {\n let node = parseBitwiseOr();\n while (match(\"Op\", \"&&\")) {\n next();\n const right = parseBitwiseOr();\n node = { type: \"BinaryExpr\", op: \"&&\", left: node, right };\n }\n return node;\n }\n\n function parseBitwiseOr() {\n let node = parseComparison();\n while (peek() && peek().type === \"Op\" && peek().value === \"|\" && (!tokens[pos + 1] || tokens[pos + 1].value !== \"|\")) {\n next();\n const right = parseComparison();\n node = { type: \"BinaryExpr\", op: \"|\", left: node, right };\n }\n return node;\n }\n\n function parseComparison() {\n let node = parseAddition();\n const t = peek();\n if (!t) return node;\n const compOps = [\"===\", \"!==\", \"==\", \"!=\", \">=\", \"<=\", \">\", \"<\"];\n if ((t.type === \"Op\" && compOps.indexOf(t.value) !== -1) ||\n (t.type === \"Keyword\" && (t.value === \"in\" || t.value === \"instanceof\"))) {\n const op = next().value;\n const right = parseAddition();\n node = { type: \"BinaryExpr\", op, left: node, right };\n }\n return node;\n }\n\n function parseAddition() {\n let node = parseMultiplication();\n while (peek() && peek().type === \"Op\" && (peek().value === \"+\" || peek().value === \"-\")) {\n const op = next().value;\n const right = parseMultiplication();\n node = { type: \"BinaryExpr\", op, left: node, right };\n }\n return node;\n }\n\n function parseMultiplication() {\n let node = parseUnary();\n while (peek() && peek().type === \"Op\" && (peek().value === \"*\" || peek().value === \"/\" || peek().value === \"%\")) {\n const op = next().value;\n const right = parseUnary();\n node = { type: \"BinaryExpr\", op, left: node, right };\n }\n return node;\n }\n\n function parseUnary() {\n const t = peek();\n if (!t) return { type: \"Literal\", value: undefined };\n // typeof\n if (t.type === \"Keyword\" && t.value === \"typeof\") {\n next();\n return { type: \"UnaryExpr\", op: \"typeof\", argument: parseUnary() };\n }\n // ! or unary - or unary +\n if (t.type === \"Op\" && (t.value === \"!\" || t.value === \"-\" || t.value === \"+\")) {\n next();\n return { type: \"UnaryExpr\", op: t.value, argument: parseUnary() };\n }\n // Prefix ++ / --\n if (t.type === \"Op\" && (t.value === \"++\" || t.value === \"--\")) {\n next();\n return { type: \"UnaryExpr\", op: t.value, argument: parseUnary(), prefix: true };\n }\n return parsePostfix();\n }\n\n function parsePostfix() {\n let node = parseCallMember();\n const t = peek();\n if (t && t.type === \"Op\" && (t.value === \"++\" || t.value === \"--\")) {\n next();\n node = { type: \"PostfixExpr\", op: t.value, argument: node };\n }\n return node;\n }\n\n function parseCallMember() {\n let node = parsePrimary();\n\n while (true) {\n const t = peek();\n if (!t) break;\n\n // Dot access: obj.prop\n if (t.type === \"Punc\" && t.value === \".\") {\n next();\n const prop = peek();\n if (prop && (prop.type === \"Ident\" || prop.type === \"Keyword\")) {\n next();\n node = { type: \"MemberExpr\", object: node, property: { type: \"Identifier\", name: prop.value }, computed: false };\n } else if (prop && prop.type === \"Forbidden\") {\n next();\n node = { type: \"Forbidden\" };\n } else {\n break;\n }\n continue;\n }\n\n // Optional chaining: obj?.prop or obj?.(args)\n if (t.type === \"Punc\" && t.value === \"?.\") {\n next();\n const nt = peek();\n // Optional call: obj?.(args)\n if (nt && nt.type === \"Punc\" && nt.value === \"(\") {\n next(); // consume (\n const args = parseArgsList();\n expect(\"Punc\", \")\");\n node = { type: \"OptionalCallExpr\", callee: node, args };\n }\n // Optional member: obj?.prop\n else if (nt && (nt.type === \"Ident\" || nt.type === \"Keyword\")) {\n next();\n node = { type: \"OptionalMemberExpr\", object: node, property: { type: \"Identifier\", name: nt.value }, computed: false };\n }\n // Optional bracket: obj?.[expr]\n else if (nt && nt.type === \"Punc\" && nt.value === \"[\") {\n next(); // consume [\n const prop = parseExpression();\n expect(\"Punc\", \"]\");\n node = { type: \"OptionalMemberExpr\", object: node, property: prop, computed: true };\n } else {\n break;\n }\n continue;\n }\n\n // Bracket access: obj[expr]\n if (t.type === \"Punc\" && t.value === \"[\") {\n next();\n const prop = parseExpression();\n expect(\"Punc\", \"]\");\n node = { type: \"MemberExpr\", object: node, property: prop, computed: true };\n continue;\n }\n\n // Function call: fn(args)\n if (t.type === \"Punc\" && t.value === \"(\") {\n next();\n const args = parseArgsList();\n expect(\"Punc\", \")\");\n node = { type: \"CallExpr\", callee: node, args };\n continue;\n }\n\n break;\n }\n\n return node;\n }\n\n function parseArgsList() {\n const args = [];\n if (match(\"Punc\", \")\")) return args;\n args.push(parseSpreadOrExpr());\n while (match(\"Punc\", \",\")) {\n next();\n if (match(\"Punc\", \")\")) break; // trailing comma\n args.push(parseSpreadOrExpr());\n }\n return args;\n }\n\n function parseSpreadOrExpr() {\n if (match(\"Punc\", \"...\")) {\n next();\n return { type: \"SpreadElement\", argument: parseExpression() };\n }\n return parseExpression();\n }\n\n // \u2500\u2500\u2500 Arrow function detection helpers \u2500\u2500\u2500\n\n function isArrowParams() {\n // Lookahead from current pos (after consuming \"(\") to see if this is (id, id, ...) =>\n const saved = pos;\n // Empty params: () =>\n if (match(\"Punc\", \")\")) {\n const after = tokens[pos + 1];\n if (after && after.type === \"Op\" && after.value === \"=>\") {\n pos = saved;\n return true;\n }\n pos = saved;\n return false;\n }\n // Check for ident list followed by ) =>\n while (pos < tokens.length) {\n const t = peek();\n if (!t) break;\n if (t.type === \"Ident\") {\n next();\n if (match(\"Punc\", \",\")) {\n next();\n continue;\n }\n if (match(\"Punc\", \")\")) {\n const after = tokens[pos + 1];\n if (after && after.type === \"Op\" && after.value === \"=>\") {\n pos = saved;\n return true;\n }\n pos = saved;\n return false;\n }\n pos = saved;\n return false;\n }\n // Spread param: (...rest) =>\n if (t.type === \"Punc\" && t.value === \"...\") {\n next();\n if (match(\"Ident\")) { next(); }\n if (match(\"Punc\", \")\")) {\n const after = tokens[pos + 1];\n if (after && after.type === \"Op\" && after.value === \"=>\") {\n pos = saved;\n return true;\n }\n }\n pos = saved;\n return false;\n }\n pos = saved;\n return false;\n }\n pos = saved;\n return false;\n }\n\n function parseArrowParams() {\n // Parse comma-separated identifiers until \")\"\n const params = [];\n if (match(\"Punc\", \")\")) return params;\n if (match(\"Punc\", \"...\")) {\n next();\n if (match(\"Ident\")) params.push(\"...\" + next().value);\n } else if (match(\"Ident\")) {\n params.push(next().value);\n }\n while (match(\"Punc\", \",\")) {\n next();\n if (match(\"Punc\", \")\")) break;\n if (match(\"Punc\", \"...\")) {\n next();\n if (match(\"Ident\")) params.push(\"...\" + next().value);\n } else if (match(\"Ident\")) {\n params.push(next().value);\n }\n }\n return params;\n }\n\n // \u2500\u2500\u2500 Primary \u2500\u2500\u2500\n\n function parsePrimary() {\n const t = peek();\n if (!t) return { type: \"Literal\", value: undefined };\n\n // Forbidden token\n if (t.type === \"Forbidden\") {\n next();\n return { type: \"Forbidden\" };\n }\n\n // Number literal\n if (t.type === \"Number\") {\n next();\n return { type: \"Literal\", value: Number(t.value) };\n }\n\n // String literal\n if (t.type === \"String\") {\n next();\n return { type: \"Literal\", value: t.value };\n }\n\n // Template literal\n if (t.type === \"Template\") {\n next();\n return {\n type: \"TemplateLiteral\",\n parts: t.parts,\n expressions: t.exprs.map(function(exprTokens) { return _parseExpr(exprTokens); })\n };\n }\n\n // Keywords: true, false, null, undefined\n if (t.type === \"Keyword\") {\n if (t.value === \"true\") { next(); return { type: \"Literal\", value: true }; }\n if (t.value === \"false\") { next(); return { type: \"Literal\", value: false }; }\n if (t.value === \"null\") { next(); return { type: \"Literal\", value: null }; }\n if (t.value === \"undefined\") { next(); return { type: \"Literal\", value: undefined }; }\n }\n\n // Array literal: [...]\n if (t.type === \"Punc\" && t.value === \"[\") {\n next();\n const elements = [];\n while (!match(\"Punc\", \"]\") && pos < tokens.length) {\n elements.push(parseSpreadOrExpr());\n if (match(\"Punc\", \",\")) next();\n }\n expect(\"Punc\", \"]\");\n return { type: \"ArrayExpr\", elements };\n }\n\n // Object literal: { ... }\n if (t.type === \"Punc\" && t.value === \"{\") {\n return parseObjectLiteral();\n }\n\n // Parenthesized expression or arrow function with parens\n if (t.type === \"Punc\" && t.value === \"(\") {\n next(); // consume (\n\n // Check for arrow function: (params) =>\n if (isArrowParams()) {\n const params = parseArrowParams();\n expect(\"Punc\", \")\");\n expect(\"Op\", \"=>\");\n const body = parseExpression();\n return { type: \"ArrowFunction\", params, body };\n }\n\n // Regular grouping\n const expr = parseExpression();\n expect(\"Punc\", \")\");\n return expr;\n }\n\n // Identifier (possibly arrow: x => ...)\n if (t.type === \"Ident\") {\n next();\n // Single-param arrow function: x => expr\n if (match(\"Op\", \"=>\")) {\n next(); // consume =>\n const body = parseExpression();\n return { type: \"ArrowFunction\", params: [t.value], body };\n }\n return { type: \"Identifier\", name: t.value };\n }\n\n // Assignment operators\n if (t.type === \"Op\" && (t.value === \"=\" || t.value === \"+=\" || t.value === \"-=\" || t.value === \"*=\" || t.value === \"/=\" || t.value === \"%=\")) {\n // Should not appear as primary; skip\n next();\n return { type: \"Literal\", value: undefined };\n }\n\n // Spread in unexpected position (e.g. top level)\n if (t.type === \"Punc\" && t.value === \"...\") {\n next();\n return { type: \"SpreadElement\", argument: parseExpression() };\n }\n\n // Fallback: skip unrecognized token\n next();\n return { type: \"Literal\", value: undefined };\n }\n\n function parseObjectLiteral() {\n next(); // consume {\n const properties = [];\n while (!match(\"Punc\", \"}\") && pos < tokens.length) {\n // Spread property: ...expr\n if (match(\"Punc\", \"...\")) {\n next();\n properties.push({ key: null, value: parseExpression(), computed: false, spread: true });\n if (match(\"Punc\", \",\")) next();\n continue;\n }\n\n // Computed property: [expr]: value\n if (match(\"Punc\", \"[\")) {\n next();\n const keyExpr = parseExpression();\n expect(\"Punc\", \"]\");\n expect(\"Punc\", \":\");\n const val = parseExpression();\n properties.push({ key: keyExpr, value: val, computed: true, spread: false });\n if (match(\"Punc\", \",\")) next();\n continue;\n }\n\n // String key: 'key': value\n if (match(\"String\")) {\n const keyToken = next();\n if (match(\"Punc\", \":\")) {\n next();\n const val = parseExpression();\n properties.push({ key: keyToken.value, value: val, computed: false, spread: false });\n }\n if (match(\"Punc\", \",\")) next();\n continue;\n }\n\n // Identifier key (shorthand or key: value)\n if (match(\"Ident\") || match(\"Keyword\")) {\n const keyToken = next();\n if (match(\"Punc\", \":\")) {\n next();\n const val = parseExpression();\n properties.push({ key: keyToken.value, value: val, computed: false, spread: false });\n } else {\n // Shorthand: { key } \u2192 { key: key }\n properties.push({\n key: keyToken.value,\n value: { type: \"Identifier\", name: keyToken.value },\n computed: false,\n spread: false\n });\n }\n if (match(\"Punc\", \",\")) next();\n continue;\n }\n\n // Number key\n if (match(\"Number\")) {\n const keyToken = next();\n if (match(\"Punc\", \":\")) {\n next();\n const val = parseExpression();\n properties.push({ key: keyToken.value, value: val, computed: false, spread: false });\n }\n if (match(\"Punc\", \",\")) next();\n continue;\n }\n\n // Unrecognized \u2014 skip\n next();\n }\n expect(\"Punc\", \"}\");\n return { type: \"ObjectExpr\", properties };\n }\n\n // \u2500\u2500\u2500 Handle top-level assignment \u2500\u2500\u2500\n\n function parseTopLevel() {\n const expr = parseExpression();\n // Check for assignment at top level: ident = expr, ident += expr, etc.\n const t = peek();\n if (t && t.type === \"Op\" && (t.value === \"=\" || t.value === \"+=\" || t.value === \"-=\" || t.value === \"*=\" || t.value === \"/=\" || t.value === \"%=\")) {\n const op = next().value;\n const right = parseExpression();\n return { type: \"AssignExpr\", op, left: expr, right };\n }\n return expr;\n }\n\n const ast = parseTopLevel();\n return ast;\n}\n\n// ---------------------------------------------------------------------------\n// AST tree-walking evaluator\n// ---------------------------------------------------------------------------\nexport const _FORBIDDEN_PROPS = { __proto__: 1, constructor: 1, prototype: 1, alert: 1, confirm: 1, prompt: 1 };\n\n// Foreign-realm accessor names (VULNERABILITY F4 / NOJS-239). Reading any of\n// these off a raw DOM element (e.g. an