@panphora/sapjs 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/dist/sap.js +40 -19
- package/dist/sap.min.js +5 -5
- package/package.json +6 -1
- package/src/mutation-bridge.js +15 -13
- package/src/pass.js +2 -1
- package/src/platform.js +45 -11
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
Sap is a tiny reactive layer for hand-written HTML files. You declare state, formulas, and paints as plain attributes; Sap rebuilds everything from the live DOM on every change. The DOM is the only store, so the file you save *is* the app: open it in any browser, it runs.
|
|
6
6
|
|
|
7
7
|
```html
|
|
8
|
-
<script src="https://cdn.jsdelivr.net/npm/sapjs/dist/sap.min.js"></script>
|
|
8
|
+
<script src="https://cdn.jsdelivr.net/npm/@panphora/sapjs/dist/sap.min.js"></script>
|
|
9
9
|
|
|
10
10
|
<main sap>
|
|
11
11
|
<input type="number" bind="qty" value="3">
|
|
@@ -47,17 +47,17 @@ Reads are expressions over `state` (the nearest scope) and `item` (the nearest r
|
|
|
47
47
|
Drop-in (auto-mounts every `[sap]` on load):
|
|
48
48
|
|
|
49
49
|
```html
|
|
50
|
-
<script src="https://cdn.jsdelivr.net/npm/sapjs/dist/sap.min.js"></script>
|
|
50
|
+
<script src="https://cdn.jsdelivr.net/npm/@panphora/sapjs/dist/sap.min.js"></script>
|
|
51
51
|
```
|
|
52
52
|
|
|
53
53
|
Or as a module:
|
|
54
54
|
|
|
55
55
|
```js
|
|
56
|
-
import Sap from "sapjs";
|
|
56
|
+
import Sap from "@panphora/sapjs";
|
|
57
57
|
```
|
|
58
58
|
|
|
59
59
|
```bash
|
|
60
|
-
npm install sapjs
|
|
60
|
+
npm install @panphora/sapjs
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
Importing the module also auto-mounts every `[sap]` on `DOMContentLoaded`. Call `Sap.mount(rootOrSelector)` to mount a root added later, or `Sap.mount()` to rescan; `Sap.config({ formats })` registers custom formats as a config-object alternative to `Sap.formats.x`.
|
|
@@ -81,7 +81,7 @@ That regenerates `docs/sapjs-explained.html` and refreshes the inlined engine fr
|
|
|
81
81
|
A complete todo app, with no JavaScript at all. Copy it into a `.html` file and open it.
|
|
82
82
|
|
|
83
83
|
```html
|
|
84
|
-
<script src="https://cdn.jsdelivr.net/npm/sapjs/dist/sap.min.js"></script>
|
|
84
|
+
<script src="https://cdn.jsdelivr.net/npm/@panphora/sapjs/dist/sap.min.js"></script>
|
|
85
85
|
|
|
86
86
|
<main sap>
|
|
87
87
|
<form trigger-add="todos">
|
package/dist/sap.js
CHANGED
|
@@ -458,20 +458,43 @@ ${src}` : `"use strict"; return (${src});`;
|
|
|
458
458
|
}
|
|
459
459
|
|
|
460
460
|
// src/platform.js
|
|
461
|
+
function capability(name) {
|
|
462
|
+
if (typeof window === "undefined") return null;
|
|
463
|
+
const clay = window.clay, hyperclay = window.hyperclay;
|
|
464
|
+
if (name === "consent") return clay?.confirm || hyperclay?.consent || null;
|
|
465
|
+
return clay?.[name] || hyperclay?.[name] || null;
|
|
466
|
+
}
|
|
467
|
+
var MUTATION_READY = ["clay:mutation-ready", "hyperclay:mutation-ready"];
|
|
468
|
+
var LIVESYNC_APPLIED = ["clay:sync-applied", "hyperclay:livesync-applied"];
|
|
469
|
+
function onPlatformEvent(target, names, handler) {
|
|
470
|
+
let claimedBy = null;
|
|
471
|
+
const wrapped = (event) => {
|
|
472
|
+
if (claimedBy !== null && claimedBy !== event.type) return;
|
|
473
|
+
claimedBy = event.type;
|
|
474
|
+
queueMicrotask(() => {
|
|
475
|
+
claimedBy = null;
|
|
476
|
+
});
|
|
477
|
+
handler(event);
|
|
478
|
+
};
|
|
479
|
+
for (const name of names) target.addEventListener(name, wrapped);
|
|
480
|
+
return () => {
|
|
481
|
+
for (const name of names) target.removeEventListener(name, wrapped);
|
|
482
|
+
};
|
|
483
|
+
}
|
|
461
484
|
function regionSkipsSave(el) {
|
|
462
|
-
const region =
|
|
485
|
+
const region = capability("region");
|
|
463
486
|
if (!region || typeof region.resolveRegionPolicy !== "function" || typeof region.skipForPolicy !== "function") {
|
|
464
487
|
return false;
|
|
465
488
|
}
|
|
466
489
|
return region.skipForPolicy(region.resolveRegionPolicy(el), void 0, ["no-save", "freeze"]);
|
|
467
490
|
}
|
|
468
491
|
function platformConsent() {
|
|
469
|
-
const
|
|
470
|
-
return
|
|
492
|
+
const fn = capability("consent");
|
|
493
|
+
return typeof fn === "function" ? fn : null;
|
|
471
494
|
}
|
|
472
495
|
function batch(label, fn) {
|
|
473
496
|
if (typeof fn !== "function") throw new Error("Sap.batch(label, fn): fn must be a function");
|
|
474
|
-
const u =
|
|
497
|
+
const u = capability("undo");
|
|
475
498
|
if (u && u.flush) u.flush();
|
|
476
499
|
const r = fn();
|
|
477
500
|
if (r && typeof r.then === "function") throw new Error("Sap.batch fn must be synchronous (it returned a promise)");
|
|
@@ -491,14 +514,14 @@ ${src}` : `"use strict"; return (${src});`;
|
|
|
491
514
|
runtime2.remountIfPresent();
|
|
492
515
|
}
|
|
493
516
|
};
|
|
494
|
-
document
|
|
495
|
-
const undo =
|
|
517
|
+
onPlatformEvent(document, LIVESYNC_APPLIED, refreshConnected);
|
|
518
|
+
const undo = capability("undo");
|
|
496
519
|
if (undo && typeof undo.on === "function") {
|
|
497
520
|
undo.on("undo", refreshConnected);
|
|
498
521
|
undo.on("redo", refreshConnected);
|
|
499
522
|
} else {
|
|
500
|
-
document
|
|
501
|
-
const u =
|
|
523
|
+
onPlatformEvent(document, MUTATION_READY, () => {
|
|
524
|
+
const u = capability("undo");
|
|
502
525
|
if (u && typeof u.on === "function") {
|
|
503
526
|
u.on("undo", refreshConnected);
|
|
504
527
|
u.on("redo", refreshConnected);
|
|
@@ -871,7 +894,7 @@ ${src}` : `"use strict"; return (${src});`;
|
|
|
871
894
|
}
|
|
872
895
|
},
|
|
873
896
|
suppress(fn) {
|
|
874
|
-
const u =
|
|
897
|
+
const u = capability("undo");
|
|
875
898
|
if (u && u.pause) u.pause();
|
|
876
899
|
try {
|
|
877
900
|
return fn();
|
|
@@ -935,7 +958,7 @@ ${src}` : `"use strict"; return (${src});`;
|
|
|
935
958
|
}
|
|
936
959
|
function withDomMutationPaused(fn) {
|
|
937
960
|
if (activeSource) return activeSource.suppress(fn);
|
|
938
|
-
const u =
|
|
961
|
+
const u = capability("undo");
|
|
939
962
|
if (u && u.pause && u.resume) {
|
|
940
963
|
u.pause();
|
|
941
964
|
try {
|
|
@@ -954,24 +977,22 @@ ${src}` : `"use strict"; return (${src});`;
|
|
|
954
977
|
function installMutationBridge(runtime2) {
|
|
955
978
|
if (installed) return;
|
|
956
979
|
if (typeof window === "undefined" || typeof document === "undefined") return;
|
|
957
|
-
const
|
|
958
|
-
const M = w.hyperclay && w.hyperclay.Mutation;
|
|
980
|
+
const M = capability("Mutation");
|
|
959
981
|
if (M && typeof M.onAnyChange === "function") {
|
|
960
982
|
activate(hubSource(M), runtime2);
|
|
961
983
|
return;
|
|
962
984
|
}
|
|
963
985
|
if (typeof MutationObserver === "function") {
|
|
964
986
|
activate(nativeSource(), runtime2);
|
|
965
|
-
lateHubListener = function onLateHub() {
|
|
966
|
-
const M2 =
|
|
987
|
+
lateHubListener = onPlatformEvent(document, MUTATION_READY, function onLateHub() {
|
|
988
|
+
const M2 = capability("Mutation");
|
|
967
989
|
if (!M2) return;
|
|
968
|
-
|
|
990
|
+
lateHubListener();
|
|
969
991
|
lateHubListener = null;
|
|
970
992
|
if (activeSource) activeSource.teardown();
|
|
971
993
|
installed = false;
|
|
972
994
|
activate(hubSource(M2), runtime2);
|
|
973
|
-
};
|
|
974
|
-
document.addEventListener("hyperclay:mutation-ready", lateHubListener);
|
|
995
|
+
});
|
|
975
996
|
}
|
|
976
997
|
}
|
|
977
998
|
function resetMutationBridge() {
|
|
@@ -982,7 +1003,7 @@ ${src}` : `"use strict"; return (${src});`;
|
|
|
982
1003
|
}
|
|
983
1004
|
}
|
|
984
1005
|
if (lateHubListener && typeof document !== "undefined") {
|
|
985
|
-
|
|
1006
|
+
lateHubListener();
|
|
986
1007
|
}
|
|
987
1008
|
activeSource = null;
|
|
988
1009
|
installed = false;
|
|
@@ -1306,7 +1327,7 @@ ${src}` : `"use strict"; return (${src});`;
|
|
|
1306
1327
|
return 0;
|
|
1307
1328
|
}
|
|
1308
1329
|
case "when": {
|
|
1309
|
-
const ov =
|
|
1330
|
+
const ov = capability("optionVisibility");
|
|
1310
1331
|
if (ov && ov._started) {
|
|
1311
1332
|
if (el._sapWhen && el.hidden) {
|
|
1312
1333
|
el.hidden = false;
|
package/dist/sap.min.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
var Sap=(()=>{var
|
|
2
|
-
${t}`:`"use strict"; return (${t});`;r={fn:new Function(...
|
|
3
|
-
`+(e[0]==="W"?"sap \u26A0 ":"sap \u2717 ")+e+" "+s];return
|
|
4
|
-
`)),f}error(e,n,r={}){let s=this.block(e,n,r);return this.errors.push(s),s}warn(e,n,r={}){let s=this.block(e,n,r);return this.warnings.push(s),s}report(){return{ok:this.errors.length===0&&!this.halted,app:this.appName,halted:this.halted,haltReason:this.haltReason,errors:this.errors.slice(),warnings:this.warnings.slice()}}};function Y(t,e){t&&t.getAttribute("sap-error")!==e&&t.setAttribute("sap-error",e)}function St(t){t&&t.hasAttribute("sap-error")&&t.removeAttribute("sap-error")}function ft(t,e){let n=null,r=3;for(let s of e){let f=je(t,s);f<r&&(r=f,n=s)}return n}function je(t,e){let n=t.length,r=e.length,s=Array.from({length:n+1},(f,u)=>[u,...new Array(r).fill(0)]);for(let f=0;f<=r;f++)s[0][f]=f;for(let f=1;f<=n;f++)for(let u=1;u<=r;u++)s[f][u]=Math.min(s[f-1][u]+1,s[f][u-1]+1,s[f-1][u-1]+(t[f-1]===e[u-1]?0:1));return s[n][r]}var j=null,$t=!1,X=null;function Zt(t){let e=null;return{kind:"hub",subscribe(n){let r=t.onAnyChange({debounce:0,require:"observed"},n);typeof r=="function"&&(e=r)},suppress(n){t.pause();try{return n()}finally{t.resume()}},teardown(){if(e){try{e()}catch{}e=null}}}}function Ve(){let t=null;return{kind:"native",subscribe(e){t=new MutationObserver(r=>e(Ue(r)));let n=typeof document<"u"&&document.body||typeof document<"u"&&document.documentElement;n&&t.observe(n,{childList:!0,subtree:!0,attributes:!0,characterData:!0})},suppress(e){let n=typeof window<"u"&&window.hyperclay&&window.hyperclay.undo;n&&n.pause&&n.pause();try{return e()}finally{t&&t.takeRecords(),n&&n.resume&&n.resume()}},teardown(){t&&(t.disconnect(),t=null)}}}function Ue(t){let e=[];for(let n of t)if(n.type==="attributes"){let r=n.target;r&&r.nodeType===1&&!xt(r)&&e.push({type:"attribute",element:r})}else if(n.type==="characterData"){let r=n.target&&n.target.parentElement;r&&!xt(r)&&e.push({type:"characterData",element:r})}else if(n.type==="childList"){for(let r of n.addedNodes)if(!(r.nodeType!==1||xt(r))&&(e.push({type:"add",element:r}),r.querySelectorAll))for(let s of r.querySelectorAll("*"))xt(s)||e.push({type:"add",element:s});for(let r of n.removedNodes)r.nodeType===1&&e.push({type:"remove",element:r,parent:n.target})}return e}function xt(t){return!!(t.closest&&t.closest("[sap-ignore],[template]"))}function Ge(t,e){t.pruneDisconnected();let n=new Set,r=!1;for(let s of e){let f=s.type==="remove"?s.parent:s.element,u=f&&t.appFor(f);u&&n.add(u),s.type==="add"&&s.element&&s.element.nodeType===1&&s.element.hasAttribute&&s.element.hasAttribute("sap")&&!(t.isRegistered&&t.isRegistered(s.element))&&(r=!0)}r&&t.remountIfPresent();for(let s of n)s.root.isConnected&&t.schedule(s,"mutation")}function te(t){if(j)return j.suppress(t);let e=typeof window<"u"&&window.hyperclay&&window.hyperclay.undo;if(e&&e.pause&&e.resume){e.pause();try{return t()}finally{e.resume()}}return t()}function Lt(t,e){$t=!0,j=t,t.subscribe(n=>Ge(e,n))}function Ct(t){if($t||typeof window>"u"||typeof document>"u")return;let e=window,n=e.hyperclay&&e.hyperclay.Mutation;if(n&&typeof n.onAnyChange=="function"){Lt(Zt(n),t);return}typeof MutationObserver=="function"&&(Lt(Ve(),t),X=function(){let s=e.hyperclay&&e.hyperclay.Mutation;s&&(document.removeEventListener("hyperclay:mutation-ready",X),X=null,j&&j.teardown(),$t=!1,Lt(Zt(s),t))},document.addEventListener("hyperclay:mutation-ready",X))}function ee(){if(j)try{j.teardown()}catch{}X&&typeof document<"u"&&document.removeEventListener("hyperclay:mutation-ready",X),j=null,$t=!1,X=null}var oe=new Set(At);function rt(t,e={}){if(e.breaker){t.diag.error("E26",t.root,{problem:"recompute loop halted after 50 passes; recent triggers: "+(t._breakerRing||[]).join(", ")}),Y(t.root,"loop");return}te(()=>Ye(t,e))}function Ye(t,e){let n=t.root,r={},s=[],f=[],u=[],T=[],E=[],y=new Set;t.diag.errors=[];let l={root:r,state:r,item:null,ownerEl:n,projection:!1};function w(o,i){let h=o.getAttribute(i.name);return h==null&&(h=i.default!=null?i.default:i.type==="num"?"0":i.type==="bool"?"false":"",E.push({el:o,name:i.name,value:mt(G(h,i.type),i.type)})),G(h,i.type)}function k(o,i){let h=F(o.getAttribute("state")||"");o._sapStateDecls=h;for(let p of h){if(p.type==="string"&&oe.has(p.name)&&typeof o[p.name]=="boolean"){i[p.name]=o.hasAttribute(p.name);continue}if(p.transient){i[p.name]=ht(o,p);continue}i[p.name]=w(o,p)}}function L(o,i){if(!o.hasAttribute("bind"))return;let h=o.getAttribute("bind"),p=i.item||i.state,v=R(o);v.kind==="radio"?(h in p||(p[h]=""),o.checked&&(p[h]=o.value)):p[h]=v.read(),o.hasAttribute("transient")&&(o.removeAttribute("value"),o.removeAttribute("checked"))}function b(o,i){for(let h of o.attributes){let p=h.name,v=h.value;if(p.startsWith("calc:"))s.push({owner:i.item||i.state,ownerEl:i.ownerEl,name:p.slice(5),entry:D(v),el:o,ctx:i});else if(p==="text"||p.startsWith("text:"))f.push({el:o,kind:"text",fmt:p.includes(":")?p.slice(5):null,entry:D(v),ctx:i});else if(p==="show")f.push({el:o,kind:"show",entry:D(v),ctx:i});else if(p.startsWith("attr:"))f.push({el:o,kind:"attr",arg:p.slice(5),entry:D(v),ctx:i});else if(p.startsWith("class:"))f.push({el:o,kind:"class",arg:p.slice(6),entry:D(v),ctx:i});else if(p.startsWith("css:"))f.push({el:o,kind:"css",arg:p.slice(4),entry:D(v),ctx:i});else if(p==="effect")u.push({el:o,entry:D(v,!0),ctx:i});else if(p==="invalid")T.push({el:o,entry:D(v),ctx:i});else if(p.startsWith("show-when:")||p.startsWith("option:")){let N=p.slice(p.indexOf(":")+1);y.add(N),f.push({el:o,kind:"when",match:"show",field:N,values:v.split("|"),ctx:i})}else if(p.startsWith("hide-when:")){let N=p.slice(p.indexOf(":")+1);y.add(N),f.push({el:o,kind:"when",match:"hide",field:N,values:v.split("|"),ctx:i})}else if(p.startsWith("option-not:")){let N=p.slice(p.indexOf(":")+1);y.add(N),f.push({el:o,kind:"when",match:"show-not",field:N,values:v.split("|"),ctx:i})}}i.projection&&o.hasAttribute("bind")&&f.push({el:o,kind:"projection",field:o.getAttribute("bind"),ctx:i})}function g(o,i,h){Z(i,"row"),Object.defineProperty(o,"$key",{value:i.id,enumerable:!1}),Object.defineProperty(o,"$index",{value:h,enumerable:!1}),Object.defineProperty(o,"$el",{value:i,enumerable:!1})}function a(o,i,h){k(o,i),o._sapScope=i,!h.projection&&o.hasAttribute("bind")&&L(o,h),b(o,h),o.hasAttribute("items")?x(o,i,h):A(o,i,h)}function A(o,i,h){W(o,(p,v)=>{v?p.hasAttribute("scope")?S(p,i,h):p.hasAttribute("items")?(b(p,h),x(p,i,h)):p.hasAttribute("detail")&&M(p,i,h):(h.projection||L(p,h),b(p,h))})}function S(o,i,h){let p=o.getAttribute("scope"),v={};i[p]=v;let N={root:h.root,state:v,item:null,ownerEl:o,projection:h.projection};a(o,v,N)}function x(o,i,h){let p=o.getAttribute("items"),v=[];i[p]=v,o._sapList=v,O(o).forEach((N,ot)=>{let C={};g(C,N,ot);let q={root:h.root,state:h.state,item:C,ownerEl:N,projection:h.projection};a(N,C,q),v.push(C)})}function $(o,i){let h=String(o).split("."),p=i.state;for(let v of h)p=p?.[v];if(p===void 0){p=i.root;for(let v of h)p=p?.[v]}return p}function M(o,i,h){for(let Q of F(o.getAttribute("state")||""))Q.name in i||(i[Q.name]=w(o,Q));o._sapScope=i;let p=o.getAttribute("detail")||"",v=/^([\w.]+)\s+by\s+(.+)$/.exec(p);if(!v){o.hidden=!0,o._sapDetailRow=null;return}let N=v[1],ot=v[2].trim(),C=$(N,h),q;try{q=H(D(ot),{state:i,item:h.item,el:o,root:h.root})}catch{q=void 0}let J=Array.isArray(C)?C.find(Q=>Q&&String(Q.$key)===String(q)):null;if(o._sapDetailRow=J?J.$el:null,!J){o.hidden||(o.hidden=!0);return}o.hidden&&(o.hidden=!1);let Ot={root:h.root,state:i,item:J,ownerEl:J.$el,projection:!0};b(o,Ot),A(o,J,Ot)}if(a(n,r,l),E.length&&y.size)for(let o of E)y.has(o.name)&&o.el.getAttribute(o.name)==null&&o.el.setAttribute(o.name,o.value);let m=new Map;for(let o of s){let i=m.get(o.owner);i||(i={ownerEl:o.ownerEl,list:[]},m.set(o.owner,i)),i.list.push(o)}let d=[...m.values()].sort((o,i)=>re(i.ownerEl)-re(o.ownerEl));for(let o of d){let i;try{i=Vt(o.list)}catch(h){t.diag.error("E07",o.list[0].el,{problem:h.message}),i=o.list}for(let h of i)try{h.owner[h.name]=H(h.entry,{state:h.ctx.state,item:h.ctx.item,el:h.el,root:h.ctx.root}),St(h.el)}catch(p){h.owner[h.name]=void 0,t.diag.error("E24",h.el,{attr:"calc:"+h.name,expr:h.entry.src,problem:String(p.message)}),Y(h.el,p.sapCode||"E24")}}let c=0;for(let o of f)c+=Je(o,t);for(let o of u)Qe(o,t);for(let o of T)Ze(o);t._state=r,t._stats={fields:Object.keys(r).filter(o=>typeof r[o]!="function").length,calcs:s.length,paints:f.length,lists:m.size,writes:c},t._lastPass={trigger:e.trigger||"refresh",writes:c},t._debug&&console.log(`sap pass \xB7 trigger: ${t._lastPass.trigger} \xB7 writes ${c}/${f.length}`)}function Xe(t){return t.kind==="text"?t.fmt?"text:"+t.fmt:"text":t.kind==="attr"?"attr:"+t.arg:t.kind==="class"?"class:"+t.arg:t.kind==="css"?"css:"+t.arg:t.kind==="when"?(t.match==="hide"?"hide-when:":t.match==="show-not"?"option-not:":"show-when:")+t.field:t.kind}function Mt(t,e,n,r){let s=t;for(;s&&s.nodeType===1;){if(s.hasAttribute&&s.hasAttribute(e)){let f=n.includes(s.getAttribute(e));if(r?!f:f)return!0}s=s.parentElement}return!1}function Ke(t,e){if(t===t.ownerDocument.activeElement)return 0;let n=R(t),r=n.read(),s=e??"";return String(r)!==String(s)?(n.write(s,{silent:!0}),1):0}function Je(t,e){if(t.kind==="projection")return Ke(t.el,t.ctx.item?t.ctx.item[t.field]:"");if(t.kind==="when")return ne(t,null);let n={state:t.ctx.state,item:t.ctx.item,el:t.el,root:t.ctx.root};try{let r=H(t.entry,n),s=ne(t,r);return St(t.el),s}catch(r){return e.diag.error(r.sapCode==="E22"?"E22":"E24",t.el,{attr:Xe(t),expr:t.entry&&t.entry.src,problem:String(r.message)}),Y(t.el,r.sapCode||"E24"),0}}function ne(t,e){let n=t.el;switch(t.kind){case"text":{let r=t.fmt!=null?qt(t.fmt,e):e==null?"":String(e);return n.textContent!==r?(n.textContent=r,1):0}case"show":{let r=!e;return n.hidden!==r?(n.hidden=r,1):0}case"when":{let r=typeof window<"u"&&window.hyperclay&&window.hyperclay.optionVisibility;if(r&&r._started)return n._sapWhen&&n.hidden?(n.hidden=!1,n._sapWhen=!1,1):0;let s;t.match==="hide"?s=!Mt(n,t.field,t.values,!1):t.match==="show-not"?s=Mt(n,t.field,t.values,!0):s=Mt(n,t.field,t.values,!1),n._sapWhen=!0;let f=!s;return n.hidden!==f?(n.hidden=f,1):0}case"attr":{let r=t.arg;if(oe.has(r)){let f=n.hasAttribute(r);return e&&!f?(n.setAttribute(r,""),1):!e&&f?(n.removeAttribute(r),1):0}let s=typeof e=="boolean"?String(e):e==null?"":String(e);return n.getAttribute(r)!==s?(n.setAttribute(r,s),1):0}case"class":{let r=!!e;return n.classList.contains(t.arg)!==r?(n.classList.toggle(t.arg,r),1):0}case"css":{let r=e==null?"":String(e);return n.style.getPropertyValue("--"+t.arg)!==r?(n.style.setProperty("--"+t.arg,r),1):0}default:return 0}}function Qe(t,e){try{H(t.entry,{state:t.ctx.state,item:t.ctx.item,el:t.el,root:t.ctx.root}),St(t.el)}catch(n){e.diag.error("E24",t.el,{attr:"effect",expr:t.entry&&t.entry.src,problem:String(n.message)}),Y(t.el,"E24")}}function Ze(t){let e="";try{let n=H(t.entry,{state:t.ctx.state,item:t.ctx.item,el:t.el,root:t.ctx.root});e=typeof n=="string"?n:""}catch{e=""}typeof t.el.setCustomValidity=="function"&&t.el.setCustomValidity(e)}function re(t){let e=0,n=t;for(;n&&!(n.hasAttribute&&n.hasAttribute("sap"));)n=n.parentElement,e++;return e}var tn=new Set(At),se=new Set(["calc","text","attr","class","css","set","move","sort","option","option-not","show-when","hide-when","editmode"]),en=new Set(["show-when","hide-when","option","option-not"]),nn=/\b(?:state|item|root)\.|===|!==|&&|\|\||[()]|^\s*!/,ie=new Set(["sap","scope","items","item","template","bind","show","effect","invalid","detail","state","transient","confirm","default","persist","sortable","trigger-add","trigger-remove","trigger-reset","sap-ignore","sap-error","no-save","no-watch","no-undo","no-trigger-autosave","freeze","mutations-ignore","save-remove","save-ignore","save-freeze"]);function rn(t){return t.tagName==="INPUT"||t.tagName==="SELECT"||t.tagName==="TEXTAREA"}function Rt(t){return t.hasAttribute("bind")}function on(t,e){e(t);for(let r of t.children)n(r);function n(r){if(r.nodeType===1&&!((r.hasAttribute("sap-ignore")||r.hasAttribute("template"))&&r.hasAttribute("sap-ignore"))){e(r);for(let s of r.children)n(s)}}}function kt(t,e){let n=!1,r=(u,T,E)=>{e.error(u,T,E),n=!0},s=t.ownerDocument.createTreeWalker(t,4),f;for(;f=s.nextNode();)if(/\{\{.*\}\}/.test(f.nodeValue||"")){r("E02",f.parentElement||t,{problem:"moustaches in text content are not a Sap feature",fix:'move the expression onto the element: text="state.x"'});break}return on(t,u=>{let T=u.closest&&u.closest("[detail]");for(let E of[...u.attributes]){let y=E.name,l=Qt(y);if(l){r("E01",u,{attr:y,expr:E.value,problem:`"${y}" is a foreign-dialect attribute; Sap has no ${y.split(/[:=]/)[0]} directive`,fix:l+" \u2014 or wrap the subtree in sap-ignore if intentional"});continue}if(y.includes(":")){let w=y.slice(0,y.indexOf(":"));if(se.has(w)){if(en.has(w)&&nn.test(E.value)){let k=y.slice(y.indexOf(":")+1);e.warn("W04",u,{attr:y,expr:E.value,problem:`${w}: compares the "${k}" attribute against a literal value, not a JS expression`,fix:`use show="${E.value}" for an expression, or a literal like ${w}:${k}="overview"`})}}else{let k=ft(w,[...se]);e.warn("W03",u,{attr:y,problem:`unknown "${w}:" attribute`,didYouMean:k?`${k}:`:null})}}else if(!ie.has(y)&&!ct.has(y)&&!tn.has(y)&&!/^(data-|aria-|on)/.test(y)&&!(y in u)){let w=ft(y,[...ie]);w&&e.warn("W03",u,{attr:y,problem:`unknown "${y}" attribute`,didYouMean:w})}y==="attr:hidden"&&e.warn("W03",u,{attr:y,problem:"attr:hidden paints the wrong attribute",fix:'use show="expr"'}),(y==="attr:value"||y==="attr:checked"||y==="attr:selected")&&Rt(u)&&r("E30",u,{attr:y,problem:"attr: on a bound control collides with persist ownership",fix:"remove it; bind owns this attribute"}),y==="effect"&&Rt(u)&&/\b(value|checked)\s*=[^=]/.test(E.value)&&r("E30",u,{attr:y,expr:E.value,problem:"effect writes value/checked on a bound control (writes a stale value with no synthetic event)",fix:"write through onclick + Sap(this) instead"}),(y==="text"||y.startsWith("text:")||y==="show")&&rn(u)&&(y==="show"||r("E18",u,{attr:y,problem:"paint belongs on output elements, not form controls",fix:"move the paint to an <output> or <span>"}))}if(u.hasAttribute("state")){let E=F(u.getAttribute("state")),y=new Set;for(let l of E)l.name.includes("-")&&r("E08",u,{attr:"state",problem:`field "${l.name}" is not a valid identifier (the hyphen parses as subtraction)`,fix:`rename to ${l.name.replace(/-/g,"")}`}),y.has(l.name)&&r("E04",u,{attr:"state",problem:`field "${l.name}" is declared twice in one scope`}),y.add(l.name),vt.has(l.name)&&r("E05",u,{attr:"state",problem:`"${l.name}" is a reserved Sap word`}),ct.has(l.name)&&r("E06",u,{attr:"state",problem:`"${l.name}" is a global HTML attribute name; pick another field name`}),l.name==="open"&&u.tagName==="DIALOG"&&r("E33",u,{attr:"state",problem:"dialog open is transient",fix:"use showModal() \u2014 declare state= only on <details> or popovers"})}if(Rt(u)){if(u.hasAttribute("-"),u.tagName==="INPUT"){let E=(u.getAttribute("type")||"text").toLowerCase();E==="file"&&r("E32",u,{attr:"bind",problem:"files never serialize into an HTML file",fix:"use no-save + effect instead of bind"}),E==="password"&&!u.hasAttribute("transient")&&r("E31",u,{attr:"bind",problem:"a password must never serialize into a world-readable file",fix:"add transient to the password input"})}else if(u.tagName!=="SELECT"&&u.tagName!=="TEXTAREA"){let E=u.getAttribute("contenteditable");!(E!=null&&E!=="false")&&u.children.length>0&&r("E20",u,{attr:"bind",problem:"bind on a container element is not a control; a write would overwrite its children",fix:"bind a control (input/select/textarea), a contenteditable, or an empty text leaf"})}}if(u.hasAttribute("transient")&&u.hasAttribute("persist")&&r("E34",u,{problem:"transient and persist are opposite: persist forces the live value into the saved bytes, leaking exactly what transient drops",fix:"remove one \u2014 transient to keep it out of the file, or persist to save it"}),u.hasAttribute("item")&&!u.hasAttribute("template")){let E=u.parentElement;(!E||!E.hasAttribute("items"))&&r("E10",u,{problem:"an [item] must be a direct child of an [items] list",fix:'wrap it in a container with items="name"'})}if(u.hasAttribute("items")&&T&&r("E17",u,{attr:"items",problem:"nested items inside a detail panel is a v1 mount error (nested collections ship in v1.1)"}),u.hasAttribute("items")){let E=t.querySelector(`[trigger-add="${u.getAttribute("items")}"]`);!pt(u)&&O(u).length===0&&E&&r("E17",u,{attr:"items",problem:`trigger-add targets items="${u.getAttribute("items")}" but it has no [item template] to clone`})}}),n&&(e.halted=!0,e.haltReason=e.errors[0]?e.errors[0].code:"halt",Y(t,e.haltReason)),!n}var ae="sap-styles",ue="[item][template]{display:none!important}[hidden]{display:none!important}[sap-error]{outline:2px solid #e5484d;outline-offset:1px}",Dt=new WeakSet;function sn(t){if(Dt.has(t))return;let e=t.defaultView,n=e&&e.CSSStyleSheet;if(n&&"adoptedStyleSheets"in t)try{let r=new n;r.replaceSync(ue),t.adoptedStyleSheets=[...t.adoptedStyleSheets,r],Dt.add(t);return}catch{}if(!t.getElementById(ae)){let r=t.createElement("style");r.id=ae,r.textContent=ue,(t.head||t.documentElement).appendChild(r)}Dt.add(t)}var an=0;function ce(){return typeof performance<"u"&&performance.now?performance.now():0}function It(t){let e=t.id||t.getAttribute("sap")||`app-${an++}`,n=new nt(e),r={root:t,name:e,diag:n,_state:null,_stats:null,_lastPass:null,_passes:0,_broken:!1,_mountMs:0,_mountWrites:0};if(!kt(t,n))return r.halted=!0,r;sn(t.ownerDocument);for(let u of t.querySelectorAll("textarea[data-value]"))wt(u);let f=ce();return rt(r,{trigger:"mount"}),r._mountMs=ce()-f,r._mountWrites=r._stats?r._stats.writes:0,r._mountWrites>0&&n.warn("W30",t,{problem:`${r._mountWrites} paint(s) ran at mount; the saved file was out of sync with its declared state`,fix:"re-save once so the file mounts clean (a settled file writes nothing)"}),r}function un(t,e){let n=null;return W(t,r=>{!n&&r.getAttribute("scope")===e&&(n=r)}),n}function fe(t,e,n){let r=I(t)||e;for(;r;){for(let s of F(r.getAttribute("state")||""))if(s.name===n)return{el:r,decl:s};if(r.hasAttribute("sap"))break;r=I(r.parentElement)}return null}function cn(t){return typeof t=="number"?"num":typeof t=="boolean"?"bool":"string"}function le(t){function e(b){let g=t.appFor(b);if(!g)return null;let a=it(b);if(a)return a._sapDetailRow?{ctxEl:a._sapDetailRow,appRec:g,isRow:!0}:{ctxEl:I(a.parentElement)||g.root,appRec:g,isRow:!1};let A=dt(b);if(A&&A.hasAttribute("item"))return{ctxEl:A,appRec:g,isRow:!0};let S=I(b)||g.root;return{ctxEl:S,appRec:g,isRow:S.hasAttribute("item")}}function n(b){let g=typeof b=="string"?document.querySelector(b):b;if(!g||g.nodeType!==1)return null;let a=e(g);return a?L(a.ctxEl,a.appRec,a.isRow):null}function r(b,g,a){let A=tt(b,a);if(A)return R(A).read();let S=st(b,a);if(S)return O(S).map(m=>L(m,g,!0));let x=un(b,a);if(x)return L(x,g,!1);let $=fe(b,g.root,a);if($)return $.decl.transient?ht($.el,$.decl):G($.el.getAttribute(a),$.decl.type);let M=b._sapScope;return M?M[a]:void 0}function s(b,g,a,A,S){A.transient?Tt(b,A,a):b.setAttribute(g,mt(a,A.type)),t.schedule(S,"set:"+g)}function f(b,g){let a=new Set,A=I(b)||g;for(;A;){for(let S of F(A.getAttribute("state")||""))a.add(S.name);if(A.hasAttribute("sap"))break;A=I(A.parentElement)}return W(b,S=>{let x=S.getAttribute("bind");x&&a.add(x)}),a}function u(b,g,a,A){let S=tt(b,a);if(S){R(S).write(A);return}let x=b;for(;x;){for(let h of x.attributes)if(h.name==="calc:"+a){g.diag.error("E16",x,{attr:"calc:"+a,problem:`"${a}" is a computed field; computed fields are read-only`});return}if(x.hasAttribute("sap"))break;x=x.parentElement}let $=fe(b,g.root,a);if($){s($.el,a,A,$.decl,g);return}let M=f(b,g.root);M.delete(a);let m=a.length>=4?ft(a,[...M]):null;if(m){g.diag.error("E12",b,{key:a,problem:`unknown state key "${a}"`,didYouMean:m});return}if(vt.has(a)||ct.has(a)){g.diag.error("E15",b,{key:a,problem:`cannot write undeclared field "${a}" \u2014 it is a reserved/global name and cannot become state`,fix:"pick a different field name, or declare it explicitly in a state= attribute"});return}let d=g.root,c=cn(A),o=d.getAttribute("state")||"",i=c==="string"?a:`${a}:${c}`;d.setAttribute("state",o?`${o} ${i}`:i),s(d,a,A,{name:a,type:c,transient:!1},g)}function T(b){let g=a=>{a.id&&(a.id="",Z(a,"row")),a.hasAttribute("sap-error")&&a.removeAttribute("sap-error")};b.id="",Z(b,"row"),b.querySelectorAll("[id]").forEach(g)}function E(b,g,a){let A=at(g.root,b,a);if(!A)return g.diag.error("E17",b,{problem:`cannot resolve a list named "${a}" from here`}),null;let S=pt(A);if(!S)return g.diag.error("E17",A,{problem:`items="${a}" has no [item template] to clone`}),null;let x=S.cloneNode(!0);return x.removeAttribute("template"),T(x),A.appendChild(x),t.schedule(g,"trigger-add:"+a),L(x,g,!0)}function y(b,g){W(b,a=>{if(a.hasAttribute("bind")){let A=R(a),S=a.getAttribute("default");A.kind==="checkbox"||A.kind==="radio"?A.write(S!=null?S===""||S==="true"||a.value===S:a.hasAttribute("checked")):A.write(S??(a.getAttribute("value")||""))}});for(let a of F(b.getAttribute("state")||"")){let A=a.default!=null?a.default:a.type==="num"?"0":a.type==="bool"?"false":"";a.transient?Tt(b,a,G(A,a.type)):b.setAttribute(a.name,A)}t.schedule(g,"trigger-reset")}function l(b,g){if(!b.isConnected)throw new Error("stale row handle: $remove() on a removed row");b.remove(),t.schedule(g,"trigger-remove")}function w(b,g,a){if(!b.isConnected)throw new Error("stale row handle: $move() on a removed row");let A=null;if(a&&a.nodeType===1?A=a:typeof a=="string"&&(A=at(g.root,b,a)),!A){g.diag.error("E17",b,{problem:`$move() cannot resolve target "${a}"`});return}t.moveInto(A,b),t.schedule(g,"trigger-move")}function k(b){let g=b.parentElement;return g?O(g).indexOf(b):-1}function L(b,g,a){let A={get $el(){return b},get $key(){return Z(b,a?"row":"scope")},get $index(){return k(b)},$add(x){return E(b,g,x)},$reset(){return y(b,g),S},$remove(){l(b,g)},$move(x){w(b,g,x)},get $appRec(){return g}},S=new Proxy(A,{get(x,$){return typeof $=="symbol"||$[0]==="$"?x[$]:r(b,g,$)},set(x,$,M){return typeof $=="string"&&$[0]==="$"?(x[$]=M,!0):(u(b,g,$,M),!0)},has(x,$){return typeof $=="string"&&$[0]==="$"?$ in x:r(b,g,$)!==void 0}});return S}return{Sap:n,contextElFor:e,readField:r,writeField:u,makeProxy:L}}var fn=["trigger-add","trigger-remove","trigger-reset"];function ln(t){if(t.nodeType!==1)return!1;for(let e of t.attributes){let n=e.name;if(n.startsWith("set:")||n.startsWith("move:")||n.startsWith("sort:")||fn.includes(n))return!0}return!1}function dn(t,e){let n=t,r=e.parentElement;for(;n&&n!==r;){if(n.nodeType===1&&ln(n)&&!(n.tagName==="FORM"&&n.hasAttribute("trigger-add")))return n;n=n.parentElement}return null}function de(t,e){let{Sap:n}=e;function r(m,d){let c=null,o=it(m);if(o&&o._sapDetailRow)c=o._sapDetailRow._sapScope||null;else{let p=dt(m);p&&p.hasAttribute("item")&&(c=p._sapScope||null)}return{state:(I(m)||d.root)._sapScope||d._state||{},item:c,el:m,root:d._state||{}}}function s(m,d,c){if(!m.hasAttribute("confirm")){c();return}let o=m.getAttribute("confirm"),i=o,h=D(o);if(!h.error)try{let N=H(h,r(m,d));(typeof N=="string"||typeof N=="number")&&(i=String(N))}catch{}let p=Yt();if(!p){window.confirm(i)&&c();return}if(m._sapGatePending)return;m._sapGatePending=!0;let v=()=>{m._sapGatePending=!1};p(i,()=>{v(),c()}).catch(v)}function f(m,d){for(let c of m.attributes){if(!c.name.startsWith("set:"))continue;let o=c.name.slice(4),i=D(c.value);if(i.error){d.diag.error("E08",m,{attr:c.name,expr:c.value,problem:String(i.error.message)});continue}let h;try{h=H(i,r(m,d))}catch(v){d.diag.error("E24",m,{attr:c.name,expr:c.value,problem:String(v.message)});continue}let p=n(m);p&&(p[o]=h)}}function u(m,d){let c=m.closest("[item]");if(!c)return;let o=O(c.parentElement),i=o.indexOf(c);d==="up"&&i>0?t.placeBefore(c,o[i-1]):d==="down"&&i<o.length-1&&t.placeBefore(o[i+1],c)}function T(m,d,c){if(d)return at(c.root,m,d);let o=m;for(;o;){if(o.hasAttribute&&o.hasAttribute("items"))return o;o=o.parentElement}let i=I(m)||c.root,h=[];return W(i,p=>{p.hasAttribute("items")&&h.push(p)}),h[0]||null}function E(m,d){let c=m==null||m==="",o=d==null||d==="";if(c&&o)return 0;if(c)return 1;if(o)return-1;let i=Number(m),h=Number(d);return!Number.isNaN(i)&&!Number.isNaN(h)?i-h:String(m)<String(d)?-1:String(m)>String(d)?1:0}function y(m,d,c,o){let i=T(m,c,o);if(!i){o.diag.error("E17",m,{attr:"sort:"+d,problem:`no items list resolvable for sort:${d}`});return}let h=O(i),p=C=>C._sapScope?C._sapScope[d]:void 0,v=[...h].sort((C,q)=>E(p(C),p(q))),ot=v.every((C,q)=>C===h[q])?[...v].reverse():v;for(let C of ot)t.moveInto(i,C);t.schedule(o,"sort:"+d)}function l(m){if(!m||m.hidden)return;let d=m.querySelector("[bind]:not([type=hidden]), [contenteditable]");d&&typeof d.focus=="function"&&d.focus()}function w(m,d){s(m,d,()=>{let c=n(m);for(let o of[...m.attributes]){let i=o.name;if(i==="trigger-remove")c&&c.$remove();else if(i==="trigger-reset")c&&c.$reset();else if(i==="trigger-add"){let h=c&&c.$add(o.value);h&&l(h.$el)}else i==="move:up"?(u(m,"up"),t.schedule(d,"move:up")):i==="move:down"?(u(m,"down"),t.schedule(d,"move:down")):i.startsWith("move:to")?c&&c.$move(o.value):i.startsWith("sort:")&&y(m,i.slice(5),o.value,d)}[...m.attributes].some(o=>o.name.startsWith("set:"))&&f(m,d)})}function k(m){let d=m.target;if(!d||d.nodeType!==1)return;let c=t.appFor(d);if(!c)return;let o=dn(d,c.root);o&&w(o,c)}function L(m){let d=R(m),c=m.getAttribute("default");d.kind==="checkbox"?d.write(c===""||c==="true"):d.write(c??"")}function b(m,d,c){let o=n(m);if(!o)return;let i=o.$add(c);if(!i)return;let h=[];W(m,p=>{p.hasAttribute("bind")&&h.push(p)});for(let p of h){let v=tt(i.$el,p.getAttribute("bind"));v&&R(v).write(R(p).read())}h.forEach(L),h.length?typeof h[0].focus=="function"&&h[0].focus():l(i.$el)}function g(m){let d=m.target;if(!d||!d.hasAttribute||!d.hasAttribute("trigger-add"))return;let c=t.appFor(d);c&&(m.preventDefault(),s(d,c,()=>b(d,c,d.getAttribute("trigger-add"))))}function a(m){let d=m.target,c=t.appFor(d);c&&setTimeout(()=>{d.querySelectorAll("[bind]").forEach(o=>{o.dispatchEvent(new Event("input",{bubbles:!0})),o.dispatchEvent(new Event("change",{bubbles:!0}))}),t.schedule(c,"reset")},0)}function A(m){let d=t.appFor(m.target);d&&t.schedule(d,"toggle")}function S(m){let d=m.target;if(!d||d.nodeType!==1)return;let c=t.appFor(d);if(!c)return;d.hasAttribute("persist")&&!yt(d)&&et(d);let o=it(d);if(o&&o._sapDetailRow&&d.hasAttribute("bind")){let i=tt(o._sapDetailRow,d.getAttribute("bind"));if(i&&i!==d){R(i).write(R(d).read());return}}t.schedule(c,m.type+" "+(d.id||d.tagName.toLowerCase()))}function x(m){let d=m.detail||{};for(let c of[d.from,d.to,m.target]){if(!c||c.nodeType!==1)continue;let o=t.appFor(c);o&&t.schedule(o,"sorted")}}function $(m){if(m.key!=="ArrowUp"&&m.key!=="ArrowDown")return;let d=m.target;if(!d||d.nodeType!==1||d.tagName!=="INPUT"||!d.hasAttribute("bind")||!d.hasAttribute("step"))return;let c=(d.getAttribute("type")||"text").toLowerCase();if(c!=="text"&&c!=="tel"&&c!=="search"||!t.appFor(d))return;m.preventDefault();let i=P(d.getAttribute("step"))||1,h=m.key==="ArrowUp"?1:-1,p=P(d.value)+i*(m.shiftKey?10:1)*h,v=d.getAttribute("min"),N=d.getAttribute("max");v!==null&&v!==""&&p<P(v)&&(p=P(v)),N!==null&&N!==""&&p>P(N)&&(p=P(N)),R(d).write(String(Number(p.toFixed(2))))}function M(m=document){m.addEventListener("click",k),m.addEventListener("submit",g),m.addEventListener("reset",a),m.addEventListener("toggle",A,!0),m.addEventListener("input",S),m.addEventListener("change",S),m.addEventListener("clay:sorted",x),m.addEventListener("keydown",$)}return{install:M}}var pe=/^(set:|move:|sort:|trigger-add$|trigger-remove$|trigger-reset$)/;function Wt(t){return`${t.tagName.toLowerCase()}[sap]`}function pn(t){let e=0;for(let n of t.querySelectorAll("*"))for(let r of n.attributes)if(pe.test(r.name)){e++;break}for(let n of t.attributes)if(pe.test(n.name)){e++;break}return e}function me(t){let e=[],n=r=>{for(let s of r.attributes){let f=s.name;(f.startsWith("calc:")||f==="text"||f.startsWith("text:")||f==="show"||f.startsWith("attr:")||f.startsWith("class:")||f.startsWith("css:")||f==="effect"||f==="invalid"||f.startsWith("set:")||f==="detail")&&e.push({el:r,attr:f,src:s.value})}};n(t);for(let r of t.querySelectorAll("*"))n(r);return e}function he(t){function e(l){let w=l._stats||{},k=l.root;return{root:Wt(k),fields:w.fields||0,calcs:w.calcs||0,paints:w.paints||0,actions:pn(k),lists:(k.hasAttribute("items")?1:0)+k.querySelectorAll("[items]").length,rows:k.querySelectorAll("[item]:not([template])").length,warnings:l.diag.warnings.length,errors:l.diag.errors.length,mountWrites:l._mountWrites||0,ms:Math.round((l._mountMs||0)*10)/10,passes:l._passes||0,lastPass:l._lastPass||null,ok:l.diag.errors.length===0&&!l.halted}}function n(){let l=t.apps().map(e);return{ok:l.every(w=>w.ok),apps:l}}function r(l){let w=e(l);return w.ok?`sap \u2713 ${w.root} \xB7 fields ${w.fields} \xB7 calcs ${w.calcs} \xB7 paints ${w.paints} \xB7 actions ${w.actions} \xB7 lists ${w.lists} \xB7 rows ${w.rows} \xB7 warnings ${w.warnings} \xB7 mount writes ${w.mountWrites} \xB7 ${w.ms}ms`:`sap \u2717 ${w.root} \xB7 errors ${w.errors} \xB7 warnings ${w.warnings} \xB7 ${w.ms}ms`}function s(){for(let l of t.apps()){let w=r(l);l.diag.errors.length||l.halted?console.error(w):console.log(w)}}function f(){let l=[],w=[];for(let k of t.apps())l.push(...k.diag.errors),w.push(...k.diag.warnings);return{ok:l.length===0,errors:l,warnings:w,counts:{errors:l.length,warnings:w.length}}}function u(l){return l?typeof l=="string"?document.querySelector(l):l.nodeType===1?l:null:null}function T(l,w){let k=u(l);if(!k)return console.warn("sap why: element not found"),null;let L=[`sap why <${k.tagName.toLowerCase()}> \u2014 ${Et(k)}`],b=[...k.attributes].filter(a=>/^(bind|text|show|effect|invalid|detail|calc:|text:|attr:|class:|css:|set:|state|items|scope)/.test(a.name));for(let a of b)L.push(` ${a.name}="${a.value}"`);let g=k;for(;g&&!g._sapScope;)g=g.parentElement;if(g&&g._sapScope){let a=g._sapScope,A=w?[w]:Object.keys(a);L.push(" reads:");for(let S of A){let x=a[S];typeof x!="function"&&L.push(` ${S} = ${JSON.stringify(Array.isArray(x)?`[${x.length} rows]`:x)}`)}}return L.push(" serialization: textContent and value/checked attributes save with the file; reads live from properties"),console.log(L.join(`
|
|
5
|
-
`)),null}function E(l){let w=l===void 0?!0:!!l;for(let k of t.apps())k._debug=w;return console.log(`sap debug ${w?"on":"off"} \u2014 per-pass headers will ${w?"":"no longer "}log`),w}function y(){let l=[],w=0;for(let a of t.apps()){let A=a.root,S=
|
|
1
|
+
var Sap=(()=>{var _t=Object.defineProperty;var Se=Object.getOwnPropertyDescriptor;var xe=Object.getOwnPropertyNames;var $e=Object.prototype.hasOwnProperty;var ke=(t,e)=>{for(var n in e)_t(t,n,{get:e[n],enumerable:!0})},Ne=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of xe(e))!$e.call(t,s)&&s!==n&&_t(t,s,{get:()=>e[s],enumerable:!(r=Se(e,s))||r.enumerable});return t};var Te=t=>Ne(_t({},"__esModule",{value:!0}),t);var kn={};ke(kn,{Sap:()=>_,default:()=>$n,mount:()=>Ee,mountAll:()=>dt,runtime:()=>K});function zt(t){let e=!1,n=new Set,r=[];function s(l){l&&(r.push(l),r.length>8&&r.shift())}function u(l,w){s(w),!l._broken&&(n.add(l),!e&&(e=!0,queueMicrotask(c)))}function c(){e=!1;let l=[...n];n.clear();for(let w of l)T(w)}function T(l){if(!l._broken){if(l._passes=(l._passes||0)+1,l._passes>50){l._broken=!0,l._breakerRing=r.slice(),t(l,{breaker:!0});return}l._resetArmed||(l._resetArmed=!0,setTimeout(()=>{l._passes=0,l._resetArmed=!1},0)),t(l)}}function E(l,w){s(w),T(l)}function y(l){l._broken=!1,l._passes=0}return{schedule:u,runNow:E,rearm:y,ring:r}}var _e=0;function Z(t,e="sap"){return t.id||(t.id=`${e}-${(_e++).toString(36)}`),t.id}function Le(t){return t.hasAttribute("scope")||t.hasAttribute("items")||t.hasAttribute("detail")}function Ce(t){return t.hasAttribute("sap-ignore")||t.hasAttribute("template")}function F(t,e){for(let r of t.children)n(r);function n(r){if(r.nodeType!==1||Ce(r))return;let s=Le(r);if(e(r,s),!s)for(let u of r.children)n(u)}}function I(t){let e=t;for(;e;){if(e.hasAttribute("sap")||e.hasAttribute("scope")||e.hasAttribute("item")||e.hasAttribute("detail"))return e;e=e.parentElement}return null}function pt(t){let e=t;for(;e;){if(e.hasAttribute("detail")||e.hasAttribute("item"))return e;e=e.parentElement}return null}function at(t){let e=t;for(;e;){if(e.hasAttribute("detail"))return e;e=e.parentElement}return null}function tt(t,e){let n=null;return F(t,r=>{!n&&r.getAttribute("bind")===e&&(n=r)}),n}function it(t,e){let n=null;return F(t,r=>{!n&&r.getAttribute("items")===e&&(n=r)}),n}function O(t){return[...t.children].filter(e=>e.hasAttribute("item")&&!e.hasAttribute("template"))}function mt(t){return[...t.children].find(e=>e.hasAttribute("item")&&e.hasAttribute("template"))||null}function P(t){let e=[];if(!t)return e;for(let n of t.trim().split(/\s+/)){if(!n)continue;let r=n,s,u=r.indexOf("=");u>=0&&(s=r.slice(u+1),r=r.slice(0,u));let c=r.split(":"),T=c[0],E="string",y=!1;for(let l of c.slice(1))l==="num"?E="num":l==="bool"?E="bool":l==="transient"&&(y=!0);e.push({name:T,type:E,default:s,transient:y})}return e}function G(t,e){if(e==="num"){if(t==null||t==="")return 0;let n=Number(t);return Number.isNaN(n)?0:n}return e==="bool"?t==="true"||t===""||t==="1":t??""}function ht(t,e){return e==="bool"?t?"true":"false":t==null?"":String(t)}function bt(t,e){let n=t._sapTransient||(t._sapTransient={});if(!(e.name in n)){let r=t.getAttribute(e.name);r==null&&(r=e.default!=null?e.default:e.type==="num"?"0":e.type==="bool"?"false":""),n[e.name]=G(r,e.type)}return t.hasAttribute(e.name)&&t.removeAttribute(e.name),n[e.name]}function Lt(t,e,n){(t._sapTransient||(t._sapTransient={}))[e.name]=n,t.hasAttribute(e.name)&&t.removeAttribute(e.name)}function ut(t,e,n){let r=String(n).split("."),s=r[r.length-1];if(r.length===1){let T=Me(e,s);return T||(t.getAttribute("items")===s?t:t.querySelector(`[items="${s}"]`))}let u=r[r.length-2],c=[...t.querySelectorAll(`[scope="${u}"]`)].find(T=>it(T,s));return c?it(c,s):null}function Me(t,e){let n=I(t);for(;n;){if(n.getAttribute("items")===e)return n;let r=it(n,e);if(r)return r;n=I(n.parentElement)}return null}function B(t){if(typeof t=="number")return t;if(t==null||t==="")return 0;let e=Number(typeof t=="string"?t.replace(/[$,%\s]/g,""):t);return Number.isNaN(e)?0:e}function gt(t,e){return Array.isArray(t)?e==null?t:t.map(n=>typeof e=="function"?e(n):n?.[e]):[]}function Ht(t,e){return gt(t,e).reduce((n,r)=>n+B(r),0)}function De(t,e){return Array.isArray(t)?e==null?t.length:t.reduce((n,r)=>n+(e(r)?1:0),0):0}function Re(t,e){let n=gt(t,e);return n.length?Ht(t,e)/n.length:0}function Ie(t,e){let n=gt(t,e).filter(r=>r!=null&&r!=="");return n.length?n.every(r=>typeof r=="number"||!Number.isNaN(Number(r)))?Math.min(...n.map(Number)):n.reduce((r,s)=>String(r)<=String(s)?r:s):0}function We(t,e){let n=gt(t,e).filter(r=>r!=null&&r!=="");return n.length?n.every(r=>typeof r=="number"||!Number.isNaN(Number(r)))?Math.max(...n.map(Number)):n.reduce((r,s)=>String(r)>=String(s)?r:s):0}function Fe(t,e,n){let r=B(t)===1?e:n??e+"s";return`${t} ${r}`}function Pe(t,e){let n=new Date(t),r=new Date(e);return Number.isNaN(+n)||Number.isNaN(+r)?0:Math.round((r-n)/864e5)}function H(t){return typeof t=="number"?t:Number(t)}var ct={usd:t=>"$"+H(t).toLocaleString("en-US",{maximumFractionDigits:0}),usd2:t=>"$"+H(t).toLocaleString("en-US",{minimumFractionDigits:2,maximumFractionDigits:2}),pct:t=>Math.round(H(t))+"%",pct1:t=>H(t).toFixed(1)+"%",int:t=>Math.round(H(t)).toLocaleString("en-US"),num:t=>H(t).toLocaleString("en-US"),num2:t=>H(t).toLocaleString("en-US",{minimumFractionDigits:2,maximumFractionDigits:2}),compact:t=>H(t).toLocaleString("en-US",{notation:"compact"}),date:t=>{let e=new Date(t);return Number.isNaN(+e)?String(t):e.toLocaleDateString("en-US")},clock:t=>{let e=H(t),n=Math.floor(e/100),r=n%10,s=Math.floor(n/10)%60;return`${Math.floor(n/600)}:${String(s).padStart(2,"0")}.${r}`}},Oe=new Set(["usd","usd2","pct","pct1","int","num","num2","compact","clock"]);function jt(t,e){if(!t)return e==null?"":String(e);let n=ct[t];if(typeof n!="function"){let r=new Error(`unknown format "${t}"`);throw r.sapCode="E22",r}if(Oe.has(t)){let r=typeof e=="number"?e:Number(e);if(!Number.isFinite(r)){let s=new Error(`format "${t}" received ${typeof e=="string"?`"${e}"`:e}`);throw s.sapCode="E22",s}}return n(e)}function Ut(){return[ct,B,Ht,De,Re,Ie,We,Fe,Pe]}var Be=["state","item","el","root","fmt","num","sum","count","avg","min","max","plural","days"],wt=new Map,Vt=/\bstate\.([a-z][a-z0-9_]*)/g,Yt=/\bitem\.([a-z][a-z0-9_]*)/g;function qe(t){let e=new Set,n=new Set,r;for(Vt.lastIndex=0;r=Vt.exec(t);)e.add(r[1]);for(Yt.lastIndex=0;r=Yt.exec(t);)n.add(r[1]);return{state:e,item:n}}function R(t,e=!1){let n=(e?"!":"")+t,r=wt.get(n);if(r)return r;wt.size>512&&wt.clear();try{let s=e?`"use strict";
|
|
2
|
+
${t}`:`"use strict"; return (${t});`;r={fn:new Function(...Be,s),src:t,statement:e,deps:qe(t),error:null}}catch(s){r={fn:null,src:t,statement:e,deps:{state:new Set,item:new Set},error:s}}return wt.set(n,r),r}function j(t,e){if(t.error)throw t.error;return t.fn(e.state,e.item,e.el??null,e.root,...Ut())}function Gt(t){let e=new Map;for(let c of t)e.set(c.name,c);let n=[],r=new Map,s=[];function u(c){let T=r.get(c.name);if(T!==1){if(T===0){let E=[...s.slice(s.indexOf(c.name)),c.name].join(" -> "),y=new Error(`calc: cycle: ${E}`);throw y.sapCode="E07",y.cycle=E,y}r.set(c.name,0),s.push(c.name);for(let E of c.entry.deps.state){let y=e.get(E);y&&y!==c&&u(y)}for(let E of c.entry.deps.item){let y=e.get(E);y&&y!==c&&u(y)}s.pop(),r.set(c.name,1),n.push(c)}}for(let c of t)u(c);return n}function Xt(t){return(t.getAttribute("type")||"text").toLowerCase()}function et(t){let e=t.tagName;if(e==="INPUT"){let n=Xt(t);n==="checkbox"||n==="radio"?t.checked?t.setAttribute("checked",""):t.removeAttribute("checked"):t.setAttribute("value",t.value);return}if(e==="TEXTAREA"){t.setAttribute("data-value",t.value);return}if(e==="SELECT"){let n=t.options;for(let r=0;r<n.length;r++)n[r].selected?n[r].setAttribute("selected",""):n[r].removeAttribute("selected")}}function Kt(t,e=t){let n=t.tagName;if(n==="INPUT"){let r=Xt(t);r==="checkbox"||r==="radio"?e.checked?t.setAttribute("checked",""):t.removeAttribute("checked"):t.setAttribute("value",e.value);return}if(n==="TEXTAREA"){t.textContent=e.value,t.removeAttribute("data-value");return}if(n==="SELECT"){let r=t.options,s=e.options;for(let u=0;u<r.length;u++)s[u]&&s[u].selected?r[u].setAttribute("selected",""):r[u].removeAttribute("selected")}}function yt(t){t.tagName!=="TEXTAREA"||!t.hasAttribute("data-value")||t!==t.ownerDocument.activeElement&&(t.textContent=t.getAttribute("data-value"),t.removeAttribute("data-value"))}function W(t){if(typeof window>"u")return null;let e=window.clay,n=window.hyperclay;return t==="consent"?e?.confirm||n?.consent||null:e?.[t]||n?.[t]||null}var Ct=["clay:mutation-ready","hyperclay:mutation-ready"],ze=["clay:sync-applied","hyperclay:livesync-applied"];function At(t,e,n){let r=null,s=u=>{r!==null&&r!==u.type||(r=u.type,queueMicrotask(()=>{r=null}),n(u))};for(let u of e)t.addEventListener(u,s);return()=>{for(let u of e)t.removeEventListener(u,s)}}function vt(t){let e=W("region");return!e||typeof e.resolveRegionPolicy!="function"||typeof e.skipForPolicy!="function"?!1:e.skipForPolicy(e.resolveRegionPolicy(t),void 0,["no-save","freeze"])}function Jt(){let t=W("consent");return typeof t=="function"?t:null}function Qt(t,e){if(typeof e!="function")throw new Error("Sap.batch(label, fn): fn must be a function");let n=W("undo");n&&n.flush&&n.flush();let r=e();if(r&&typeof r.then=="function")throw new Error("Sap.batch fn must be synchronous (it returned a promise)");return n&&n.commitCaptured&&n.commitCaptured(t),r}function Zt(t){if(typeof window>"u"||typeof document>"u")return;let e=()=>{let r=!1;for(let s of t.apps())s.root.isConnected?t.runNow(s,"platform"):r=!0;r&&(t.pruneDisconnected(),t.remountIfPresent())};At(document,ze,e);let n=W("undo");n&&typeof n.on=="function"?(n.on("undo",e),n.on("redo",e)):At(document,Ct,()=>{let r=W("undo");r&&typeof r.on=="function"&&(r.on("undo",e),r.on("redo",e))})}function He(t){let e=t.tagName;if(e==="INPUT"){let n=(t.getAttribute("type")||"text").toLowerCase();return n==="checkbox"?"checkbox":n==="radio"?"radio":n==="number"||n==="range"?"number":n==="hidden"?"hidden":"text"}return e==="TEXTAREA"?"text":e==="SELECT"?t.multiple?"select-multiple":"select-one":"leaf"}function je(t){if(t.validity&&t.validity.badInput)return t.hasAttribute("value")?Number(t.getAttribute("value"))||0:t._sapLastGood??0;if(t.value==="")return 0;let e=Number(t.value);return Number.isNaN(e)?t._sapLastGood??0:(t._sapLastGood=e,e)}function Ue(t,e){switch(e){case"checkbox":return t.checked;case"radio":return t.checked?t.value:void 0;case"number":return je(t);case"select-one":return t.value;case"select-multiple":return[...t.selectedOptions].map(n=>n.value);case"hidden":case"text":return t.value;case"leaf":return t.textContent;default:return t.value}}function Ve(t,e,n){switch(e){case"checkbox":t.checked=!!n;break;case"radio":t.checked=String(t.value)===String(n);break;case"number":case"text":case"hidden":t.value=n==null?"":String(n);break;case"select-one":t.value=n==null?"":String(n);break;case"select-multiple":{let r=new Set((Array.isArray(n)?n:[n]).map(String));for(let s of t.options)s.selected=r.has(s.value);break}case"leaf":t.textContent=n==null?"":String(n);break;default:t.value=n==null?"":String(n)}}function te(t){if(t.hasAttribute("transient")){t.removeAttribute("value"),t.removeAttribute("checked"),t.removeAttribute("data-value");return}vt(t)||et(t)}function Ye(t){t.dispatchEvent(new Event("input",{bubbles:!0})),t.dispatchEvent(new Event("change",{bubbles:!0}))}function D(t){let e=He(t);return{el:t,kind:e,read(){return Ue(t,e)},write(n,r={}){Ve(t,e,n),r.noMirror||te(t),r.silent||Ye(t)},mirror(){te(t)}}}var Mt={E01:{slug:"foreign-dialect",behavior:"HALT"},E02:{slug:"moustaches-in-text",behavior:"HALT"},E04:{slug:"duplicate-declaration",behavior:"HALT"},E05:{slug:"reserved-name",behavior:"HALT"},E06:{slug:"global-html-name",behavior:"HALT"},E07:{slug:"calc-cycle",behavior:"ERR"},E08:{slug:"expression-syntax",behavior:"HALT"},E10:{slug:"orphan-item",behavior:"HALT"},E12:{slug:"unknown-state-key",behavior:"ERR"},E15:{slug:"write-to-undeclared-key",behavior:"ERR"},E16:{slug:"write-to-computed-field",behavior:"ERR"},E17:{slug:"unresolvable-action-target",behavior:"HALT"},E18:{slug:"paint-on-form-control",behavior:"HALT"},E20:{slug:"bind-on-non-control",behavior:"HALT"},E22:{slug:"format-type",behavior:"ERR"},E24:{slug:"runtime-expression-throw",behavior:"ERR"},E26:{slug:"circuit-breaker",behavior:"HALT"},E30:{slug:"attr-on-bound-control",behavior:"HALT"},E31:{slug:"password-without-transient",behavior:"HALT"},E32:{slug:"file-bind",behavior:"HALT"},E33:{slug:"dialog-state-open",behavior:"HALT"},E34:{slug:"transient-persist-collision",behavior:"HALT"},W03:{slug:"unknown-colon-attribute",behavior:"WARN"},W04:{slug:"expression-in-when",behavior:"WARN"},W30:{slug:"nonzero-mount-writes",behavior:"WARN"}},Et=["disabled","readonly","required","checked","selected","multiple","open","inert","autofocus","novalidate","hidden"],ft=new Set(["title","style","hidden","lang","dir","translate","class","id","nonce","autofocus","tabindex","contenteditable","spellcheck","draggable","role","slot","part","name","value","type","itemscope","itemtype","itemid","itemprop","itemref"]),St=new Set(["sap","scope","items","item","template","bind","calc","text","show","attr","set","trigger","move","sort","detail","effect","invalid","confirm","transient","default","persist","sortable","editmode"]);function xt(t){if(!t||t.nodeType!==1)return"?";if(t.id)return`${t.tagName.toLowerCase()}#${t.id}`;let e=[],n=t,r=0;for(;n&&n.nodeType===1&&r<4;){let s=n.tagName.toLowerCase();if(n.hasAttribute("sap"))s+="[sap]";else if(n.parentElement){let u=[...n.parentElement.children].filter(c=>c.tagName===n.tagName);u.length>1&&(s+=`:nth-of-type(${u.indexOf(n)+1})`)}if(e.unshift(s),n.hasAttribute("sap"))break;n=n.parentElement,r++}return e.join(" > ")}function ee(t){let e=t.toLowerCase();return/^x-text$|^x-html$/.test(e)?'use text="state.x" (Sap paints text only)':/^x-show$|^v-show$|^v-if$/.test(e)?'use show="expr" (toggles native hidden)':/^x-model$|^v-model$/.test(e)?'use bind="field"':/^@|^x-on:|^v-on:|^on:/.test(e)?"use native onclick + Sap(this) (no event directive)":/^:|^x-bind:class$|^v-bind:class$/.test(e)?'use class:NAME="expr" or attr:NAME="expr"':/^x-for$|^v-for$/.test(e)?'use items="name" on the container and item template on the row':/^x-data$/.test(e)?"declare state with state= / bind / items (no JS state object)":/^x-init$/.test(e)?"use a <script> or onclick + Sap(this)":/^bind:/.test(e)?'use bind="field"; one-way paint is attr:NAME="expr"':/^x-/.test(e)||/^v-/.test(e)?"this is a foreign-dialect attribute; Sap has no x-/v- attributes":null}var nt=class{constructor(e){this.appName=e,this.errors=[],this.warnings=[],this.halted=!1,this.haltReason=null}block(e,n,r={}){let s=(Mt[e]||{}).slug||"error",u={code:e,slug:s,el:xt(n),attr:r.attr||null,expr:r.expr||null,key:r.key||null,problem:r.problem||"",didYouMean:r.didYouMean||null,fix:r.fix||null,phase:r.phase||"runtime"},c=[`
|
|
3
|
+
`+(e[0]==="W"?"sap \u26A0 ":"sap \u2717 ")+e+" "+s];return c.push(` at ${u.el}${u.attr?` \u2014 ${u.attr}="${u.expr??""}"`:""}`),u.problem&&c.push(` problem: ${u.problem}`),u.didYouMean&&c.push(` did you mean: ${u.didYouMean}`),u.fix&&c.push(` fix: ${u.fix}`),console.error(c.join(`
|
|
4
|
+
`)),u}error(e,n,r={}){let s=this.block(e,n,r);return this.errors.push(s),s}warn(e,n,r={}){let s=this.block(e,n,r);return this.warnings.push(s),s}report(){return{ok:this.errors.length===0&&!this.halted,app:this.appName,halted:this.halted,haltReason:this.haltReason,errors:this.errors.slice(),warnings:this.warnings.slice()}}};function X(t,e){t&&t.getAttribute("sap-error")!==e&&t.setAttribute("sap-error",e)}function $t(t){t&&t.hasAttribute("sap-error")&&t.removeAttribute("sap-error")}function lt(t,e){let n=null,r=3;for(let s of e){let u=Ge(t,s);u<r&&(r=u,n=s)}return n}function Ge(t,e){let n=t.length,r=e.length,s=Array.from({length:n+1},(u,c)=>[c,...new Array(r).fill(0)]);for(let u=0;u<=r;u++)s[0][u]=u;for(let u=1;u<=n;u++)for(let c=1;c<=r;c++)s[u][c]=Math.min(s[u-1][c]+1,s[u][c-1]+1,s[u-1][c-1]+(t[u-1]===e[c-1]?0:1));return s[n][r]}var U=null,Nt=!1,rt=null;function ne(t){let e=null;return{kind:"hub",subscribe(n){let r=t.onAnyChange({debounce:0,require:"observed"},n);typeof r=="function"&&(e=r)},suppress(n){t.pause();try{return n()}finally{t.resume()}},teardown(){if(e){try{e()}catch{}e=null}}}}function Xe(){let t=null;return{kind:"native",subscribe(e){t=new MutationObserver(r=>e(Ke(r)));let n=typeof document<"u"&&document.body||typeof document<"u"&&document.documentElement;n&&t.observe(n,{childList:!0,subtree:!0,attributes:!0,characterData:!0})},suppress(e){let n=W("undo");n&&n.pause&&n.pause();try{return e()}finally{t&&t.takeRecords(),n&&n.resume&&n.resume()}},teardown(){t&&(t.disconnect(),t=null)}}}function Ke(t){let e=[];for(let n of t)if(n.type==="attributes"){let r=n.target;r&&r.nodeType===1&&!kt(r)&&e.push({type:"attribute",element:r})}else if(n.type==="characterData"){let r=n.target&&n.target.parentElement;r&&!kt(r)&&e.push({type:"characterData",element:r})}else if(n.type==="childList"){for(let r of n.addedNodes)if(!(r.nodeType!==1||kt(r))&&(e.push({type:"add",element:r}),r.querySelectorAll))for(let s of r.querySelectorAll("*"))kt(s)||e.push({type:"add",element:s});for(let r of n.removedNodes)r.nodeType===1&&e.push({type:"remove",element:r,parent:n.target})}return e}function kt(t){return!!(t.closest&&t.closest("[sap-ignore],[template]"))}function Je(t,e){t.pruneDisconnected();let n=new Set,r=!1;for(let s of e){let u=s.type==="remove"?s.parent:s.element,c=u&&t.appFor(u);c&&n.add(c),s.type==="add"&&s.element&&s.element.nodeType===1&&s.element.hasAttribute&&s.element.hasAttribute("sap")&&!(t.isRegistered&&t.isRegistered(s.element))&&(r=!0)}r&&t.remountIfPresent();for(let s of n)s.root.isConnected&&t.schedule(s,"mutation")}function re(t){if(U)return U.suppress(t);let e=W("undo");if(e&&e.pause&&e.resume){e.pause();try{return t()}finally{e.resume()}}return t()}function Dt(t,e){Nt=!0,U=t,t.subscribe(n=>Je(e,n))}function Rt(t){if(Nt||typeof window>"u"||typeof document>"u")return;let e=W("Mutation");if(e&&typeof e.onAnyChange=="function"){Dt(ne(e),t);return}typeof MutationObserver=="function"&&(Dt(Xe(),t),rt=At(document,Ct,function(){let r=W("Mutation");r&&(rt(),rt=null,U&&U.teardown(),Nt=!1,Dt(ne(r),t))}))}function oe(){if(U)try{U.teardown()}catch{}rt&&typeof document<"u"&&rt(),U=null,Nt=!1,rt=null}var ae=new Set(Et);function ot(t,e={}){if(e.breaker){t.diag.error("E26",t.root,{problem:"recompute loop halted after 50 passes; recent triggers: "+(t._breakerRing||[]).join(", ")}),X(t.root,"loop");return}re(()=>Qe(t,e))}function Qe(t,e){let n=t.root,r={},s=[],u=[],c=[],T=[],E=[],y=new Set;t.diag.errors=[];let l={root:r,state:r,item:null,ownerEl:n,projection:!1};function w(o,i){let h=o.getAttribute(i.name);return h==null&&(h=i.default!=null?i.default:i.type==="num"?"0":i.type==="bool"?"false":"",E.push({el:o,name:i.name,value:ht(G(h,i.type),i.type)})),G(h,i.type)}function k(o,i){let h=P(o.getAttribute("state")||"");o._sapStateDecls=h;for(let p of h){if(p.type==="string"&&ae.has(p.name)&&typeof o[p.name]=="boolean"){i[p.name]=o.hasAttribute(p.name);continue}if(p.transient){i[p.name]=bt(o,p);continue}i[p.name]=w(o,p)}}function L(o,i){if(!o.hasAttribute("bind"))return;let h=o.getAttribute("bind"),p=i.item||i.state,v=D(o);v.kind==="radio"?(h in p||(p[h]=""),o.checked&&(p[h]=o.value)):p[h]=v.read(),o.hasAttribute("transient")&&(o.removeAttribute("value"),o.removeAttribute("checked"))}function b(o,i){for(let h of o.attributes){let p=h.name,v=h.value;if(p.startsWith("calc:"))s.push({owner:i.item||i.state,ownerEl:i.ownerEl,name:p.slice(5),entry:R(v),el:o,ctx:i});else if(p==="text"||p.startsWith("text:"))u.push({el:o,kind:"text",fmt:p.includes(":")?p.slice(5):null,entry:R(v),ctx:i});else if(p==="show")u.push({el:o,kind:"show",entry:R(v),ctx:i});else if(p.startsWith("attr:"))u.push({el:o,kind:"attr",arg:p.slice(5),entry:R(v),ctx:i});else if(p.startsWith("class:"))u.push({el:o,kind:"class",arg:p.slice(6),entry:R(v),ctx:i});else if(p.startsWith("css:"))u.push({el:o,kind:"css",arg:p.slice(4),entry:R(v),ctx:i});else if(p==="effect")c.push({el:o,entry:R(v,!0),ctx:i});else if(p==="invalid")T.push({el:o,entry:R(v),ctx:i});else if(p.startsWith("show-when:")||p.startsWith("option:")){let N=p.slice(p.indexOf(":")+1);y.add(N),u.push({el:o,kind:"when",match:"show",field:N,values:v.split("|"),ctx:i})}else if(p.startsWith("hide-when:")){let N=p.slice(p.indexOf(":")+1);y.add(N),u.push({el:o,kind:"when",match:"hide",field:N,values:v.split("|"),ctx:i})}else if(p.startsWith("option-not:")){let N=p.slice(p.indexOf(":")+1);y.add(N),u.push({el:o,kind:"when",match:"show-not",field:N,values:v.split("|"),ctx:i})}}i.projection&&o.hasAttribute("bind")&&u.push({el:o,kind:"projection",field:o.getAttribute("bind"),ctx:i})}function g(o,i,h){Z(i,"row"),Object.defineProperty(o,"$key",{value:i.id,enumerable:!1}),Object.defineProperty(o,"$index",{value:h,enumerable:!1}),Object.defineProperty(o,"$el",{value:i,enumerable:!1})}function a(o,i,h){k(o,i),o._sapScope=i,!h.projection&&o.hasAttribute("bind")&&L(o,h),b(o,h),o.hasAttribute("items")?x(o,i,h):A(o,i,h)}function A(o,i,h){F(o,(p,v)=>{v?p.hasAttribute("scope")?S(p,i,h):p.hasAttribute("items")?(b(p,h),x(p,i,h)):p.hasAttribute("detail")&&M(p,i,h):(h.projection||L(p,h),b(p,h))})}function S(o,i,h){let p=o.getAttribute("scope"),v={};i[p]=v;let N={root:h.root,state:v,item:null,ownerEl:o,projection:h.projection};a(o,v,N)}function x(o,i,h){let p=o.getAttribute("items"),v=[];i[p]=v,o._sapList=v,O(o).forEach((N,st)=>{let C={};g(C,N,st);let z={root:h.root,state:h.state,item:C,ownerEl:N,projection:h.projection};a(N,C,z),v.push(C)})}function $(o,i){let h=String(o).split("."),p=i.state;for(let v of h)p=p?.[v];if(p===void 0){p=i.root;for(let v of h)p=p?.[v]}return p}function M(o,i,h){for(let Q of P(o.getAttribute("state")||""))Q.name in i||(i[Q.name]=w(o,Q));o._sapScope=i;let p=o.getAttribute("detail")||"",v=/^([\w.]+)\s+by\s+(.+)$/.exec(p);if(!v){o.hidden=!0,o._sapDetailRow=null;return}let N=v[1],st=v[2].trim(),C=$(N,h),z;try{z=j(R(st),{state:i,item:h.item,el:o,root:h.root})}catch{z=void 0}let J=Array.isArray(C)?C.find(Q=>Q&&String(Q.$key)===String(z)):null;if(o._sapDetailRow=J?J.$el:null,!J){o.hidden||(o.hidden=!0);return}o.hidden&&(o.hidden=!1);let qt={root:h.root,state:i,item:J,ownerEl:J.$el,projection:!0};b(o,qt),A(o,J,qt)}if(a(n,r,l),E.length&&y.size)for(let o of E)y.has(o.name)&&o.el.getAttribute(o.name)==null&&o.el.setAttribute(o.name,o.value);let m=new Map;for(let o of s){let i=m.get(o.owner);i||(i={ownerEl:o.ownerEl,list:[]},m.set(o.owner,i)),i.list.push(o)}let d=[...m.values()].sort((o,i)=>ie(i.ownerEl)-ie(o.ownerEl));for(let o of d){let i;try{i=Gt(o.list)}catch(h){t.diag.error("E07",o.list[0].el,{problem:h.message}),i=o.list}for(let h of i)try{h.owner[h.name]=j(h.entry,{state:h.ctx.state,item:h.ctx.item,el:h.el,root:h.ctx.root}),$t(h.el)}catch(p){h.owner[h.name]=void 0,t.diag.error("E24",h.el,{attr:"calc:"+h.name,expr:h.entry.src,problem:String(p.message)}),X(h.el,p.sapCode||"E24")}}let f=0;for(let o of u)f+=en(o,t);for(let o of c)nn(o,t);for(let o of T)rn(o);t._state=r,t._stats={fields:Object.keys(r).filter(o=>typeof r[o]!="function").length,calcs:s.length,paints:u.length,lists:m.size,writes:f},t._lastPass={trigger:e.trigger||"refresh",writes:f},t._debug&&console.log(`sap pass \xB7 trigger: ${t._lastPass.trigger} \xB7 writes ${f}/${u.length}`)}function Ze(t){return t.kind==="text"?t.fmt?"text:"+t.fmt:"text":t.kind==="attr"?"attr:"+t.arg:t.kind==="class"?"class:"+t.arg:t.kind==="css"?"css:"+t.arg:t.kind==="when"?(t.match==="hide"?"hide-when:":t.match==="show-not"?"option-not:":"show-when:")+t.field:t.kind}function It(t,e,n,r){let s=t;for(;s&&s.nodeType===1;){if(s.hasAttribute&&s.hasAttribute(e)){let u=n.includes(s.getAttribute(e));if(r?!u:u)return!0}s=s.parentElement}return!1}function tn(t,e){if(t===t.ownerDocument.activeElement)return 0;let n=D(t),r=n.read(),s=e??"";return String(r)!==String(s)?(n.write(s,{silent:!0}),1):0}function en(t,e){if(t.kind==="projection")return tn(t.el,t.ctx.item?t.ctx.item[t.field]:"");if(t.kind==="when")return se(t,null);let n={state:t.ctx.state,item:t.ctx.item,el:t.el,root:t.ctx.root};try{let r=j(t.entry,n),s=se(t,r);return $t(t.el),s}catch(r){return e.diag.error(r.sapCode==="E22"?"E22":"E24",t.el,{attr:Ze(t),expr:t.entry&&t.entry.src,problem:String(r.message)}),X(t.el,r.sapCode||"E24"),0}}function se(t,e){let n=t.el;switch(t.kind){case"text":{let r=t.fmt!=null?jt(t.fmt,e):e==null?"":String(e);return n.textContent!==r?(n.textContent=r,1):0}case"show":{let r=!e;return n.hidden!==r?(n.hidden=r,1):0}case"when":{let r=W("optionVisibility");if(r&&r._started)return n._sapWhen&&n.hidden?(n.hidden=!1,n._sapWhen=!1,1):0;let s;t.match==="hide"?s=!It(n,t.field,t.values,!1):t.match==="show-not"?s=It(n,t.field,t.values,!0):s=It(n,t.field,t.values,!1),n._sapWhen=!0;let u=!s;return n.hidden!==u?(n.hidden=u,1):0}case"attr":{let r=t.arg;if(ae.has(r)){let u=n.hasAttribute(r);return e&&!u?(n.setAttribute(r,""),1):!e&&u?(n.removeAttribute(r),1):0}let s=typeof e=="boolean"?String(e):e==null?"":String(e);return n.getAttribute(r)!==s?(n.setAttribute(r,s),1):0}case"class":{let r=!!e;return n.classList.contains(t.arg)!==r?(n.classList.toggle(t.arg,r),1):0}case"css":{let r=e==null?"":String(e);return n.style.getPropertyValue("--"+t.arg)!==r?(n.style.setProperty("--"+t.arg,r),1):0}default:return 0}}function nn(t,e){try{j(t.entry,{state:t.ctx.state,item:t.ctx.item,el:t.el,root:t.ctx.root}),$t(t.el)}catch(n){e.diag.error("E24",t.el,{attr:"effect",expr:t.entry&&t.entry.src,problem:String(n.message)}),X(t.el,"E24")}}function rn(t){let e="";try{let n=j(t.entry,{state:t.ctx.state,item:t.ctx.item,el:t.el,root:t.ctx.root});e=typeof n=="string"?n:""}catch{e=""}typeof t.el.setCustomValidity=="function"&&t.el.setCustomValidity(e)}function ie(t){let e=0,n=t;for(;n&&!(n.hasAttribute&&n.hasAttribute("sap"));)n=n.parentElement,e++;return e}var on=new Set(Et),ue=new Set(["calc","text","attr","class","css","set","move","sort","option","option-not","show-when","hide-when","editmode"]),sn=new Set(["show-when","hide-when","option","option-not"]),an=/\b(?:state|item|root)\.|===|!==|&&|\|\||[()]|^\s*!/,ce=new Set(["sap","scope","items","item","template","bind","show","effect","invalid","detail","state","transient","confirm","default","persist","sortable","trigger-add","trigger-remove","trigger-reset","sap-ignore","sap-error","no-save","no-watch","no-undo","no-trigger-autosave","freeze","mutations-ignore","save-remove","save-ignore","save-freeze"]);function un(t){return t.tagName==="INPUT"||t.tagName==="SELECT"||t.tagName==="TEXTAREA"}function Wt(t){return t.hasAttribute("bind")}function cn(t,e){e(t);for(let r of t.children)n(r);function n(r){if(r.nodeType===1&&!((r.hasAttribute("sap-ignore")||r.hasAttribute("template"))&&r.hasAttribute("sap-ignore"))){e(r);for(let s of r.children)n(s)}}}function Tt(t,e){let n=!1,r=(c,T,E)=>{e.error(c,T,E),n=!0},s=t.ownerDocument.createTreeWalker(t,4),u;for(;u=s.nextNode();)if(/\{\{.*\}\}/.test(u.nodeValue||"")){r("E02",u.parentElement||t,{problem:"moustaches in text content are not a Sap feature",fix:'move the expression onto the element: text="state.x"'});break}return cn(t,c=>{let T=c.closest&&c.closest("[detail]");for(let E of[...c.attributes]){let y=E.name,l=ee(y);if(l){r("E01",c,{attr:y,expr:E.value,problem:`"${y}" is a foreign-dialect attribute; Sap has no ${y.split(/[:=]/)[0]} directive`,fix:l+" \u2014 or wrap the subtree in sap-ignore if intentional"});continue}if(y.includes(":")){let w=y.slice(0,y.indexOf(":"));if(ue.has(w)){if(sn.has(w)&&an.test(E.value)){let k=y.slice(y.indexOf(":")+1);e.warn("W04",c,{attr:y,expr:E.value,problem:`${w}: compares the "${k}" attribute against a literal value, not a JS expression`,fix:`use show="${E.value}" for an expression, or a literal like ${w}:${k}="overview"`})}}else{let k=lt(w,[...ue]);e.warn("W03",c,{attr:y,problem:`unknown "${w}:" attribute`,didYouMean:k?`${k}:`:null})}}else if(!ce.has(y)&&!ft.has(y)&&!on.has(y)&&!/^(data-|aria-|on)/.test(y)&&!(y in c)){let w=lt(y,[...ce]);w&&e.warn("W03",c,{attr:y,problem:`unknown "${y}" attribute`,didYouMean:w})}y==="attr:hidden"&&e.warn("W03",c,{attr:y,problem:"attr:hidden paints the wrong attribute",fix:'use show="expr"'}),(y==="attr:value"||y==="attr:checked"||y==="attr:selected")&&Wt(c)&&r("E30",c,{attr:y,problem:"attr: on a bound control collides with persist ownership",fix:"remove it; bind owns this attribute"}),y==="effect"&&Wt(c)&&/\b(value|checked)\s*=[^=]/.test(E.value)&&r("E30",c,{attr:y,expr:E.value,problem:"effect writes value/checked on a bound control (writes a stale value with no synthetic event)",fix:"write through onclick + Sap(this) instead"}),(y==="text"||y.startsWith("text:")||y==="show")&&un(c)&&(y==="show"||r("E18",c,{attr:y,problem:"paint belongs on output elements, not form controls",fix:"move the paint to an <output> or <span>"}))}if(c.hasAttribute("state")){let E=P(c.getAttribute("state")),y=new Set;for(let l of E)l.name.includes("-")&&r("E08",c,{attr:"state",problem:`field "${l.name}" is not a valid identifier (the hyphen parses as subtraction)`,fix:`rename to ${l.name.replace(/-/g,"")}`}),y.has(l.name)&&r("E04",c,{attr:"state",problem:`field "${l.name}" is declared twice in one scope`}),y.add(l.name),St.has(l.name)&&r("E05",c,{attr:"state",problem:`"${l.name}" is a reserved Sap word`}),ft.has(l.name)&&r("E06",c,{attr:"state",problem:`"${l.name}" is a global HTML attribute name; pick another field name`}),l.name==="open"&&c.tagName==="DIALOG"&&r("E33",c,{attr:"state",problem:"dialog open is transient",fix:"use showModal() \u2014 declare state= only on <details> or popovers"})}if(Wt(c)){if(c.hasAttribute("-"),c.tagName==="INPUT"){let E=(c.getAttribute("type")||"text").toLowerCase();E==="file"&&r("E32",c,{attr:"bind",problem:"files never serialize into an HTML file",fix:"use no-save + effect instead of bind"}),E==="password"&&!c.hasAttribute("transient")&&r("E31",c,{attr:"bind",problem:"a password must never serialize into a world-readable file",fix:"add transient to the password input"})}else if(c.tagName!=="SELECT"&&c.tagName!=="TEXTAREA"){let E=c.getAttribute("contenteditable");!(E!=null&&E!=="false")&&c.children.length>0&&r("E20",c,{attr:"bind",problem:"bind on a container element is not a control; a write would overwrite its children",fix:"bind a control (input/select/textarea), a contenteditable, or an empty text leaf"})}}if(c.hasAttribute("transient")&&c.hasAttribute("persist")&&r("E34",c,{problem:"transient and persist are opposite: persist forces the live value into the saved bytes, leaking exactly what transient drops",fix:"remove one \u2014 transient to keep it out of the file, or persist to save it"}),c.hasAttribute("item")&&!c.hasAttribute("template")){let E=c.parentElement;(!E||!E.hasAttribute("items"))&&r("E10",c,{problem:"an [item] must be a direct child of an [items] list",fix:'wrap it in a container with items="name"'})}if(c.hasAttribute("items")&&T&&r("E17",c,{attr:"items",problem:"nested items inside a detail panel is a v1 mount error (nested collections ship in v1.1)"}),c.hasAttribute("items")){let E=t.querySelector(`[trigger-add="${c.getAttribute("items")}"]`);!mt(c)&&O(c).length===0&&E&&r("E17",c,{attr:"items",problem:`trigger-add targets items="${c.getAttribute("items")}" but it has no [item template] to clone`})}}),n&&(e.halted=!0,e.haltReason=e.errors[0]?e.errors[0].code:"halt",X(t,e.haltReason)),!n}var fe="sap-styles",le="[item][template]{display:none!important}[hidden]{display:none!important}[sap-error]{outline:2px solid #e5484d;outline-offset:1px}",Ft=new WeakSet;function fn(t){if(Ft.has(t))return;let e=t.defaultView,n=e&&e.CSSStyleSheet;if(n&&"adoptedStyleSheets"in t)try{let r=new n;r.replaceSync(le),t.adoptedStyleSheets=[...t.adoptedStyleSheets,r],Ft.add(t);return}catch{}if(!t.getElementById(fe)){let r=t.createElement("style");r.id=fe,r.textContent=le,(t.head||t.documentElement).appendChild(r)}Ft.add(t)}var ln=0;function de(){return typeof performance<"u"&&performance.now?performance.now():0}function Pt(t){let e=t.id||t.getAttribute("sap")||`app-${ln++}`,n=new nt(e),r={root:t,name:e,diag:n,_state:null,_stats:null,_lastPass:null,_passes:0,_broken:!1,_mountMs:0,_mountWrites:0};if(!Tt(t,n))return r.halted=!0,r;fn(t.ownerDocument);for(let c of t.querySelectorAll("textarea[data-value]"))yt(c);let u=de();return ot(r,{trigger:"mount"}),r._mountMs=de()-u,r._mountWrites=r._stats?r._stats.writes:0,r._mountWrites>0&&n.warn("W30",t,{problem:`${r._mountWrites} paint(s) ran at mount; the saved file was out of sync with its declared state`,fix:"re-save once so the file mounts clean (a settled file writes nothing)"}),r}function dn(t,e){let n=null;return F(t,r=>{!n&&r.getAttribute("scope")===e&&(n=r)}),n}function pe(t,e,n){let r=I(t)||e;for(;r;){for(let s of P(r.getAttribute("state")||""))if(s.name===n)return{el:r,decl:s};if(r.hasAttribute("sap"))break;r=I(r.parentElement)}return null}function pn(t){return typeof t=="number"?"num":typeof t=="boolean"?"bool":"string"}function me(t){function e(b){let g=t.appFor(b);if(!g)return null;let a=at(b);if(a)return a._sapDetailRow?{ctxEl:a._sapDetailRow,appRec:g,isRow:!0}:{ctxEl:I(a.parentElement)||g.root,appRec:g,isRow:!1};let A=pt(b);if(A&&A.hasAttribute("item"))return{ctxEl:A,appRec:g,isRow:!0};let S=I(b)||g.root;return{ctxEl:S,appRec:g,isRow:S.hasAttribute("item")}}function n(b){let g=typeof b=="string"?document.querySelector(b):b;if(!g||g.nodeType!==1)return null;let a=e(g);return a?L(a.ctxEl,a.appRec,a.isRow):null}function r(b,g,a){let A=tt(b,a);if(A)return D(A).read();let S=it(b,a);if(S)return O(S).map(m=>L(m,g,!0));let x=dn(b,a);if(x)return L(x,g,!1);let $=pe(b,g.root,a);if($)return $.decl.transient?bt($.el,$.decl):G($.el.getAttribute(a),$.decl.type);let M=b._sapScope;return M?M[a]:void 0}function s(b,g,a,A,S){A.transient?Lt(b,A,a):b.setAttribute(g,ht(a,A.type)),t.schedule(S,"set:"+g)}function u(b,g){let a=new Set,A=I(b)||g;for(;A;){for(let S of P(A.getAttribute("state")||""))a.add(S.name);if(A.hasAttribute("sap"))break;A=I(A.parentElement)}return F(b,S=>{let x=S.getAttribute("bind");x&&a.add(x)}),a}function c(b,g,a,A){let S=tt(b,a);if(S){D(S).write(A);return}let x=b;for(;x;){for(let h of x.attributes)if(h.name==="calc:"+a){g.diag.error("E16",x,{attr:"calc:"+a,problem:`"${a}" is a computed field; computed fields are read-only`});return}if(x.hasAttribute("sap"))break;x=x.parentElement}let $=pe(b,g.root,a);if($){s($.el,a,A,$.decl,g);return}let M=u(b,g.root);M.delete(a);let m=a.length>=4?lt(a,[...M]):null;if(m){g.diag.error("E12",b,{key:a,problem:`unknown state key "${a}"`,didYouMean:m});return}if(St.has(a)||ft.has(a)){g.diag.error("E15",b,{key:a,problem:`cannot write undeclared field "${a}" \u2014 it is a reserved/global name and cannot become state`,fix:"pick a different field name, or declare it explicitly in a state= attribute"});return}let d=g.root,f=pn(A),o=d.getAttribute("state")||"",i=f==="string"?a:`${a}:${f}`;d.setAttribute("state",o?`${o} ${i}`:i),s(d,a,A,{name:a,type:f,transient:!1},g)}function T(b){let g=a=>{a.id&&(a.id="",Z(a,"row")),a.hasAttribute("sap-error")&&a.removeAttribute("sap-error")};b.id="",Z(b,"row"),b.querySelectorAll("[id]").forEach(g)}function E(b,g,a){let A=ut(g.root,b,a);if(!A)return g.diag.error("E17",b,{problem:`cannot resolve a list named "${a}" from here`}),null;let S=mt(A);if(!S)return g.diag.error("E17",A,{problem:`items="${a}" has no [item template] to clone`}),null;let x=S.cloneNode(!0);return x.removeAttribute("template"),T(x),A.appendChild(x),t.schedule(g,"trigger-add:"+a),L(x,g,!0)}function y(b,g){F(b,a=>{if(a.hasAttribute("bind")){let A=D(a),S=a.getAttribute("default");A.kind==="checkbox"||A.kind==="radio"?A.write(S!=null?S===""||S==="true"||a.value===S:a.hasAttribute("checked")):A.write(S??(a.getAttribute("value")||""))}});for(let a of P(b.getAttribute("state")||"")){let A=a.default!=null?a.default:a.type==="num"?"0":a.type==="bool"?"false":"";a.transient?Lt(b,a,G(A,a.type)):b.setAttribute(a.name,A)}t.schedule(g,"trigger-reset")}function l(b,g){if(!b.isConnected)throw new Error("stale row handle: $remove() on a removed row");b.remove(),t.schedule(g,"trigger-remove")}function w(b,g,a){if(!b.isConnected)throw new Error("stale row handle: $move() on a removed row");let A=null;if(a&&a.nodeType===1?A=a:typeof a=="string"&&(A=ut(g.root,b,a)),!A){g.diag.error("E17",b,{problem:`$move() cannot resolve target "${a}"`});return}t.moveInto(A,b),t.schedule(g,"trigger-move")}function k(b){let g=b.parentElement;return g?O(g).indexOf(b):-1}function L(b,g,a){let A={get $el(){return b},get $key(){return Z(b,a?"row":"scope")},get $index(){return k(b)},$add(x){return E(b,g,x)},$reset(){return y(b,g),S},$remove(){l(b,g)},$move(x){w(b,g,x)},get $appRec(){return g}},S=new Proxy(A,{get(x,$){return typeof $=="symbol"||$[0]==="$"?x[$]:r(b,g,$)},set(x,$,M){return typeof $=="string"&&$[0]==="$"?(x[$]=M,!0):(c(b,g,$,M),!0)},has(x,$){return typeof $=="string"&&$[0]==="$"?$ in x:r(b,g,$)!==void 0}});return S}return{Sap:n,contextElFor:e,readField:r,writeField:c,makeProxy:L}}var mn=["trigger-add","trigger-remove","trigger-reset"];function hn(t){if(t.nodeType!==1)return!1;for(let e of t.attributes){let n=e.name;if(n.startsWith("set:")||n.startsWith("move:")||n.startsWith("sort:")||mn.includes(n))return!0}return!1}function bn(t,e){let n=t,r=e.parentElement;for(;n&&n!==r;){if(n.nodeType===1&&hn(n)&&!(n.tagName==="FORM"&&n.hasAttribute("trigger-add")))return n;n=n.parentElement}return null}function he(t,e){let{Sap:n}=e;function r(m,d){let f=null,o=at(m);if(o&&o._sapDetailRow)f=o._sapDetailRow._sapScope||null;else{let p=pt(m);p&&p.hasAttribute("item")&&(f=p._sapScope||null)}return{state:(I(m)||d.root)._sapScope||d._state||{},item:f,el:m,root:d._state||{}}}function s(m,d,f){if(!m.hasAttribute("confirm")){f();return}let o=m.getAttribute("confirm"),i=o,h=R(o);if(!h.error)try{let N=j(h,r(m,d));(typeof N=="string"||typeof N=="number")&&(i=String(N))}catch{}let p=Jt();if(!p){window.confirm(i)&&f();return}if(m._sapGatePending)return;m._sapGatePending=!0;let v=()=>{m._sapGatePending=!1};p(i,()=>{v(),f()}).catch(v)}function u(m,d){for(let f of m.attributes){if(!f.name.startsWith("set:"))continue;let o=f.name.slice(4),i=R(f.value);if(i.error){d.diag.error("E08",m,{attr:f.name,expr:f.value,problem:String(i.error.message)});continue}let h;try{h=j(i,r(m,d))}catch(v){d.diag.error("E24",m,{attr:f.name,expr:f.value,problem:String(v.message)});continue}let p=n(m);p&&(p[o]=h)}}function c(m,d){let f=m.closest("[item]");if(!f)return;let o=O(f.parentElement),i=o.indexOf(f);d==="up"&&i>0?t.placeBefore(f,o[i-1]):d==="down"&&i<o.length-1&&t.placeBefore(o[i+1],f)}function T(m,d,f){if(d)return ut(f.root,m,d);let o=m;for(;o;){if(o.hasAttribute&&o.hasAttribute("items"))return o;o=o.parentElement}let i=I(m)||f.root,h=[];return F(i,p=>{p.hasAttribute("items")&&h.push(p)}),h[0]||null}function E(m,d){let f=m==null||m==="",o=d==null||d==="";if(f&&o)return 0;if(f)return 1;if(o)return-1;let i=Number(m),h=Number(d);return!Number.isNaN(i)&&!Number.isNaN(h)?i-h:String(m)<String(d)?-1:String(m)>String(d)?1:0}function y(m,d,f,o){let i=T(m,f,o);if(!i){o.diag.error("E17",m,{attr:"sort:"+d,problem:`no items list resolvable for sort:${d}`});return}let h=O(i),p=C=>C._sapScope?C._sapScope[d]:void 0,v=[...h].sort((C,z)=>E(p(C),p(z))),st=v.every((C,z)=>C===h[z])?[...v].reverse():v;for(let C of st)t.moveInto(i,C);t.schedule(o,"sort:"+d)}function l(m){if(!m||m.hidden)return;let d=m.querySelector("[bind]:not([type=hidden]), [contenteditable]");d&&typeof d.focus=="function"&&d.focus()}function w(m,d){s(m,d,()=>{let f=n(m);for(let o of[...m.attributes]){let i=o.name;if(i==="trigger-remove")f&&f.$remove();else if(i==="trigger-reset")f&&f.$reset();else if(i==="trigger-add"){let h=f&&f.$add(o.value);h&&l(h.$el)}else i==="move:up"?(c(m,"up"),t.schedule(d,"move:up")):i==="move:down"?(c(m,"down"),t.schedule(d,"move:down")):i.startsWith("move:to")?f&&f.$move(o.value):i.startsWith("sort:")&&y(m,i.slice(5),o.value,d)}[...m.attributes].some(o=>o.name.startsWith("set:"))&&u(m,d)})}function k(m){let d=m.target;if(!d||d.nodeType!==1)return;let f=t.appFor(d);if(!f)return;let o=bn(d,f.root);o&&w(o,f)}function L(m){let d=D(m),f=m.getAttribute("default");d.kind==="checkbox"?d.write(f===""||f==="true"):d.write(f??"")}function b(m,d,f){let o=n(m);if(!o)return;let i=o.$add(f);if(!i)return;let h=[];F(m,p=>{p.hasAttribute("bind")&&h.push(p)});for(let p of h){let v=tt(i.$el,p.getAttribute("bind"));v&&D(v).write(D(p).read())}h.forEach(L),h.length?typeof h[0].focus=="function"&&h[0].focus():l(i.$el)}function g(m){let d=m.target;if(!d||!d.hasAttribute||!d.hasAttribute("trigger-add"))return;let f=t.appFor(d);f&&(m.preventDefault(),s(d,f,()=>b(d,f,d.getAttribute("trigger-add"))))}function a(m){let d=m.target,f=t.appFor(d);f&&setTimeout(()=>{d.querySelectorAll("[bind]").forEach(o=>{o.dispatchEvent(new Event("input",{bubbles:!0})),o.dispatchEvent(new Event("change",{bubbles:!0}))}),t.schedule(f,"reset")},0)}function A(m){let d=t.appFor(m.target);d&&t.schedule(d,"toggle")}function S(m){let d=m.target;if(!d||d.nodeType!==1)return;let f=t.appFor(d);if(!f)return;d.hasAttribute("persist")&&!vt(d)&&et(d);let o=at(d);if(o&&o._sapDetailRow&&d.hasAttribute("bind")){let i=tt(o._sapDetailRow,d.getAttribute("bind"));if(i&&i!==d){D(i).write(D(d).read());return}}t.schedule(f,m.type+" "+(d.id||d.tagName.toLowerCase()))}function x(m){let d=m.detail||{};for(let f of[d.from,d.to,m.target]){if(!f||f.nodeType!==1)continue;let o=t.appFor(f);o&&t.schedule(o,"sorted")}}function $(m){if(m.key!=="ArrowUp"&&m.key!=="ArrowDown")return;let d=m.target;if(!d||d.nodeType!==1||d.tagName!=="INPUT"||!d.hasAttribute("bind")||!d.hasAttribute("step"))return;let f=(d.getAttribute("type")||"text").toLowerCase();if(f!=="text"&&f!=="tel"&&f!=="search"||!t.appFor(d))return;m.preventDefault();let i=B(d.getAttribute("step"))||1,h=m.key==="ArrowUp"?1:-1,p=B(d.value)+i*(m.shiftKey?10:1)*h,v=d.getAttribute("min"),N=d.getAttribute("max");v!==null&&v!==""&&p<B(v)&&(p=B(v)),N!==null&&N!==""&&p>B(N)&&(p=B(N)),D(d).write(String(Number(p.toFixed(2))))}function M(m=document){m.addEventListener("click",k),m.addEventListener("submit",g),m.addEventListener("reset",a),m.addEventListener("toggle",A,!0),m.addEventListener("input",S),m.addEventListener("change",S),m.addEventListener("clay:sorted",x),m.addEventListener("keydown",$)}return{install:M}}var be=/^(set:|move:|sort:|trigger-add$|trigger-remove$|trigger-reset$)/;function Ot(t){return`${t.tagName.toLowerCase()}[sap]`}function gn(t){let e=0;for(let n of t.querySelectorAll("*"))for(let r of n.attributes)if(be.test(r.name)){e++;break}for(let n of t.attributes)if(be.test(n.name)){e++;break}return e}function ge(t){let e=[],n=r=>{for(let s of r.attributes){let u=s.name;(u.startsWith("calc:")||u==="text"||u.startsWith("text:")||u==="show"||u.startsWith("attr:")||u.startsWith("class:")||u.startsWith("css:")||u==="effect"||u==="invalid"||u.startsWith("set:")||u==="detail")&&e.push({el:r,attr:u,src:s.value})}};n(t);for(let r of t.querySelectorAll("*"))n(r);return e}function we(t){function e(l){let w=l._stats||{},k=l.root;return{root:Ot(k),fields:w.fields||0,calcs:w.calcs||0,paints:w.paints||0,actions:gn(k),lists:(k.hasAttribute("items")?1:0)+k.querySelectorAll("[items]").length,rows:k.querySelectorAll("[item]:not([template])").length,warnings:l.diag.warnings.length,errors:l.diag.errors.length,mountWrites:l._mountWrites||0,ms:Math.round((l._mountMs||0)*10)/10,passes:l._passes||0,lastPass:l._lastPass||null,ok:l.diag.errors.length===0&&!l.halted}}function n(){let l=t.apps().map(e);return{ok:l.every(w=>w.ok),apps:l}}function r(l){let w=e(l);return w.ok?`sap \u2713 ${w.root} \xB7 fields ${w.fields} \xB7 calcs ${w.calcs} \xB7 paints ${w.paints} \xB7 actions ${w.actions} \xB7 lists ${w.lists} \xB7 rows ${w.rows} \xB7 warnings ${w.warnings} \xB7 mount writes ${w.mountWrites} \xB7 ${w.ms}ms`:`sap \u2717 ${w.root} \xB7 errors ${w.errors} \xB7 warnings ${w.warnings} \xB7 ${w.ms}ms`}function s(){for(let l of t.apps()){let w=r(l);l.diag.errors.length||l.halted?console.error(w):console.log(w)}}function u(){let l=[],w=[];for(let k of t.apps())l.push(...k.diag.errors),w.push(...k.diag.warnings);return{ok:l.length===0,errors:l,warnings:w,counts:{errors:l.length,warnings:w.length}}}function c(l){return l?typeof l=="string"?document.querySelector(l):l.nodeType===1?l:null:null}function T(l,w){let k=c(l);if(!k)return console.warn("sap why: element not found"),null;let L=[`sap why <${k.tagName.toLowerCase()}> \u2014 ${xt(k)}`],b=[...k.attributes].filter(a=>/^(bind|text|show|effect|invalid|detail|calc:|text:|attr:|class:|css:|set:|state|items|scope)/.test(a.name));for(let a of b)L.push(` ${a.name}="${a.value}"`);let g=k;for(;g&&!g._sapScope;)g=g.parentElement;if(g&&g._sapScope){let a=g._sapScope,A=w?[w]:Object.keys(a);L.push(" reads:");for(let S of A){let x=a[S];typeof x!="function"&&L.push(` ${S} = ${JSON.stringify(Array.isArray(x)?`[${x.length} rows]`:x)}`)}}return L.push(" serialization: textContent and value/checked attributes save with the file; reads live from properties"),console.log(L.join(`
|
|
5
|
+
`)),null}function E(l){let w=l===void 0?!0:!!l;for(let k of t.apps())k._debug=w;return console.log(`sap debug ${w?"on":"off"} \u2014 per-pass headers will ${w?"":"no longer "}log`),w}function y(){let l=[],w=0;for(let a of t.apps()){let A=a.root,S=Ot(A);w++;let x=new nt(a.name);Tt(A,x);for(let f of x.errors)l.push({code:f.code,severity:"error",message:`${S}: ${f.code} ${f.slug} at ${f.el}`});for(let f of x.warnings)l.push({code:f.code,severity:"warn",message:`${S}: ${f.code} ${f.slug} at ${f.el}`});w++,ot(a,{trigger:"doctor-dryrun"});let $=a._stats?a._stats.writes:0;$>0&&l.push({code:"W30",severity:"warn",message:`${S}: dry recompute wrote ${$} cells on a quiet page; an element is stale or non-idempotent`}),w++;let M=ge(A).map(f=>f.src).join(" \0 "),m=[];for(let f of[A,...A.querySelectorAll("[state]")])for(let o of P(f.getAttribute("state")||""))m.push(o.name);for(let f of m)new RegExp(`(state|item)\\.${f}\\b`).test(M)||l.push({code:"I33",severity:"info",message:`${S}: field "${f}" is declared but read nowhere (dead state)`});w++;let d=new Map;for(let f of A.querySelectorAll("[id]"))d.set(f.id,(d.get(f.id)||0)+1);for(let[f,o]of d)o>1&&l.push({code:"W32",severity:"warn",message:`${S}: duplicate id "${f}" (${o} elements) \u2014 clone/edit collision`});w++;for(let f of ge(A))f.src.length>120&&l.push({code:"I33",severity:"info",message:`${S}: ${f.attr} expression is ${f.src.length} chars; consider a calc: field`});w++;for(let f of A.querySelectorAll("[text], [text\\:num]")){let o=f.textContent||"";/\.\d{5,}/.test(o)&&l.push({code:"I33",severity:"info",message:`${S}: ${xt(f)} paints >4 decimals; consider text:FMT`})}}let k=l.filter(a=>a.severity==="error").length,L=l.filter(a=>a.severity==="warn").length,b=l.filter(a=>a.severity==="info").length;console.log(`sap doctor \xB7 ${t.apps().map(a=>Ot(a.root)).join(", ")} \xB7 errors ${k} \xB7 warnings ${L} \xB7 info ${b} \xB7 ${w} checks \xB7 0ms`);for(let a of l)(a.severity==="error"?console.error:a.severity==="warn"?console.warn:console.info)(` ${a.severity==="error"?"\u2717":a.severity==="warn"?"\u26A0":"\xB7"} ${a.message}`);return l}return{status:n,report:u,why:T,debug:E,doctor:y,greenLine:r,printGreenLines:s,REGISTRY:Mt}}var wn="0.2.0",q=new Map,V=[],Bt=zt(ot);function yn(t){let e=t;for(;e;){if(e.nodeType===1&&e.hasAttribute&&e.hasAttribute("sap"))return q.get(e)||null;e=e.parentNode&&e.parentNode.host?e.parentNode.host:e.parentElement}return null}function An(){for(let t=V.length-1;t>=0;t--){let e=V[t];e.root.isConnected||(q.delete(e.root),V.splice(t,1))}}function vn(t,e){if(t.moveBefore)try{t.moveBefore(e,null);return}catch{}t.appendChild(e)}function En(t,e){let n=e.parentElement;if(n){if(n.moveBefore)try{n.moveBefore(t,e);return}catch{}n.insertBefore(t,e)}}var K={apps:()=>V.slice(),appFor:yn,isRegistered:t=>q.has(t),pruneDisconnected:An,schedule:(t,e)=>Bt.schedule(t,e),runNow:(t,e)=>Bt.runNow(t,e),moveInto:vn,placeBefore:En,remountIfPresent:()=>dt()},Ae=me(K),Sn=he(K,Ae),Y=we(K),ye=!1;function ve(){ye||(ye=!0,Sn.install(document),Zt(K))}function dt(t=document){ve(),Rt(K);let e=t.querySelectorAll("[sap]"),n=[];for(let r of e){if(q.has(r))continue;let s=Pt(r);q.set(r,s),V.push(s),n.push(s)}for(let r of n){let s=Y.greenLine(r);r.diag.errors.length||r.halted?console.error(s):console.log(s)}return n}function Ee(t){if(ve(),Rt(K),!t)return dt();let e=typeof t=="string"?document.querySelector(t):t;if(!e)return null;if(q.has(e))return q.get(e);let n=Pt(e);q.set(e,n),V.push(n);let r=Y.greenLine(n);return n.diag.errors.length||n.halted?console.error(r):console.log(r),n}var _=Ae.Sap;_.version=wn;_.refresh=function(){for(let e of V)e.root.isConnected&&Bt.runNow(e,"refresh")};_.batch=Qt;_.status=Y.status;_.report=Y.report;_.why=Y.why;_.debug=Y.debug;_.doctor=Y.doctor;_.greenLine=Y.greenLine;_.formats=ct;_.serializeControlToAttributes=et;_.finalizeControlForSave=Kt;_.rehydrateControlFromAttributes=yt;_.mount=Ee;_.config=function(e={}){return e.formats&&Object.assign(ct,e.formats),_};_._registry=q;_._reset=function(){q.clear(),V.length=0,oe()};function xn(){typeof document>"u"||(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>dt()):dt())}xn();typeof window<"u"&&(window.Sap=_);var $n=_;return Te(kn);})();
|
|
6
6
|
if (typeof window !== 'undefined' && Sap && Sap.default) window.Sap = Sap.default;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@panphora/sapjs",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "A reactive HTML library where the DOM is the only state store. Structure-first, one write path, transient rebuild. An Alpine alternative built for malleable, self-saving HTML.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/sap.js",
|
|
@@ -48,5 +48,10 @@
|
|
|
48
48
|
"esbuild": "^0.21.0",
|
|
49
49
|
"jest": "^29.7.0",
|
|
50
50
|
"jest-environment-jsdom": "^29.7.0"
|
|
51
|
+
},
|
|
52
|
+
"hyper": {
|
|
53
|
+
"kind": "lib",
|
|
54
|
+
"status": "active",
|
|
55
|
+
"url": "https://saphtml.com"
|
|
51
56
|
}
|
|
52
57
|
}
|
package/src/mutation-bridge.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
// means, not just its own delegated events. Two interchangeable backends sit behind
|
|
3
3
|
// one MutationSource seam:
|
|
4
4
|
//
|
|
5
|
-
// - hubSource: when
|
|
6
|
-
//
|
|
5
|
+
// - hubSource: when the platform's Mutation hub is present, ride its central,
|
|
6
|
+
// pause-gated observer. The hub already coordinates the
|
|
7
7
|
// live-sync / undo pause windows, so we inherit them for free.
|
|
8
8
|
// - nativeSource: standalone, a sapjs-owned MutationObserver on document.body.
|
|
9
9
|
//
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
// late (teardown + re-subscribe), so the two never observe at once. Degrades to a
|
|
19
19
|
// plain no-op when neither a hub nor a MutationObserver is present.
|
|
20
20
|
|
|
21
|
+
import { capability, MUTATION_READY, onPlatformEvent } from "./platform.js";
|
|
22
|
+
|
|
21
23
|
let activeSource = null;
|
|
22
24
|
let installed = false;
|
|
23
25
|
let lateHubListener = null;
|
|
@@ -55,7 +57,7 @@ function nativeSource() {
|
|
|
55
57
|
suppress(fn) {
|
|
56
58
|
// No hub to gate us: drop our own pass's records before the browser delivers them.
|
|
57
59
|
// Pause undo too if it somehow exists hub-less (presence-guarded; normally it does not).
|
|
58
|
-
const u =
|
|
60
|
+
const u = capability("undo");
|
|
59
61
|
if (u && u.pause) u.pause();
|
|
60
62
|
try {
|
|
61
63
|
return fn();
|
|
@@ -130,7 +132,7 @@ function scheduleAffectedApps(runtime, changes) {
|
|
|
130
132
|
// historical undo-pause-if-present behavior so derived writes still stay off the undo stack.
|
|
131
133
|
export function withDomMutationPaused(fn) {
|
|
132
134
|
if (activeSource) return activeSource.suppress(fn);
|
|
133
|
-
const u =
|
|
135
|
+
const u = capability("undo");
|
|
134
136
|
if (u && u.pause && u.resume) {
|
|
135
137
|
u.pause();
|
|
136
138
|
try { return fn(); } finally { u.resume(); }
|
|
@@ -148,8 +150,7 @@ function activate(source, runtime) {
|
|
|
148
150
|
export function installMutationBridge(runtime) {
|
|
149
151
|
if (installed) return;
|
|
150
152
|
if (typeof window === "undefined" || typeof document === "undefined") return;
|
|
151
|
-
const
|
|
152
|
-
const M = w.hyperclay && w.hyperclay.Mutation;
|
|
153
|
+
const M = capability("Mutation");
|
|
153
154
|
|
|
154
155
|
if (M && typeof M.onAnyChange === "function") { activate(hubSource(M), runtime); return; }
|
|
155
156
|
|
|
@@ -157,22 +158,23 @@ export function installMutationBridge(runtime) {
|
|
|
157
158
|
// a hub is coming: another library may merely own that namespace (e.g. a consent shim)
|
|
158
159
|
// and never publish a Mutation hub, which would strand us in a permanent wait with dead
|
|
159
160
|
// reactivity. So observe natively now and CEDE to a real hub if one arrives later
|
|
160
|
-
// (teardown + re-subscribe on the document-dispatched
|
|
161
|
+
// (teardown + re-subscribe on the document-dispatched mutation-ready — a
|
|
161
162
|
// window-dispatched event is ignored, matching the hub's own target). The handoff is
|
|
162
163
|
// safe: passes are idempotent, so the brief pre-hub native window costs at most a little
|
|
163
164
|
// redundant work, never correctness, and the two never observe simultaneously.
|
|
164
165
|
if (typeof MutationObserver === "function") {
|
|
165
166
|
activate(nativeSource(), runtime);
|
|
166
|
-
lateHubListener
|
|
167
|
-
|
|
167
|
+
// lateHubListener holds the disposer, which unregisters BOTH spellings: dropping
|
|
168
|
+
// only the one that fired would leak the other under whichever client is loaded.
|
|
169
|
+
lateHubListener = onPlatformEvent(document, MUTATION_READY, function onLateHub() {
|
|
170
|
+
const M2 = capability("Mutation");
|
|
168
171
|
if (!M2) return;
|
|
169
|
-
|
|
172
|
+
lateHubListener();
|
|
170
173
|
lateHubListener = null;
|
|
171
174
|
if (activeSource) activeSource.teardown();
|
|
172
175
|
installed = false;
|
|
173
176
|
activate(hubSource(M2), runtime);
|
|
174
|
-
};
|
|
175
|
-
document.addEventListener("hyperclay:mutation-ready", lateHubListener);
|
|
177
|
+
});
|
|
176
178
|
}
|
|
177
179
|
// else: no DOM observer (SSR / ancient) -> event-only, exactly as before.
|
|
178
180
|
}
|
|
@@ -182,7 +184,7 @@ export function installMutationBridge(runtime) {
|
|
|
182
184
|
export function resetMutationBridge() {
|
|
183
185
|
if (activeSource) { try { activeSource.teardown(); } catch { /* ignore */ } }
|
|
184
186
|
if (lateHubListener && typeof document !== "undefined") {
|
|
185
|
-
|
|
187
|
+
lateHubListener();
|
|
186
188
|
}
|
|
187
189
|
activeSource = null;
|
|
188
190
|
installed = false;
|
package/src/pass.js
CHANGED
|
@@ -10,6 +10,7 @@ import { carrierFor } from "./carrier.js";
|
|
|
10
10
|
import { applyFormat } from "./helpers.js";
|
|
11
11
|
import { NATIVE_BOOLEANS, setBeacon, clearBeacon } from "./errors.js";
|
|
12
12
|
import { withDomMutationPaused } from "./mutation-bridge.js";
|
|
13
|
+
import { capability } from "./platform.js";
|
|
13
14
|
|
|
14
15
|
const NATIVE_BOOLEAN_SET = new Set(NATIVE_BOOLEANS);
|
|
15
16
|
|
|
@@ -379,7 +380,7 @@ function applyPaint(p, value) {
|
|
|
379
380
|
return 0;
|
|
380
381
|
}
|
|
381
382
|
case "when": {
|
|
382
|
-
const ov =
|
|
383
|
+
const ov = capability("optionVisibility");
|
|
383
384
|
if (ov && ov._started) {
|
|
384
385
|
// The CSS floor owns visibility now; clear any hidden we set pre-floor.
|
|
385
386
|
if (el._sapWhen && el.hidden) { el.hidden = false; el._sapWhen = false; return 1; }
|
package/src/platform.js
CHANGED
|
@@ -1,7 +1,41 @@
|
|
|
1
|
-
// Optional Hyperclay integration. Everything here degrades to a no-op when
|
|
2
|
-
//
|
|
1
|
+
// Optional Hyperclay integration. Everything here degrades to a no-op when no
|
|
2
|
+
// platform client is present, so Sap runs standalone in any HTML file. When the
|
|
3
3
|
// platform is present, Sap rides its live-sync, undo, and autosave for free.
|
|
4
4
|
|
|
5
|
+
// Two clients provide this API and they spell one of these capabilities
|
|
6
|
+
// differently: hyperclayjs owns window.hyperclay and calls the dialog consent();
|
|
7
|
+
// clayjs owns window.clay and calls it confirm(). Resolving `clay ?? hyperclay` as
|
|
8
|
+
// one namespace would land on clay and then find no consent, so a confirm-gated
|
|
9
|
+
// action would silently drop back to the native window.confirm box. Hence per
|
|
10
|
+
// capability rather than per namespace.
|
|
11
|
+
export function capability(name) {
|
|
12
|
+
if (typeof window === "undefined") return null;
|
|
13
|
+
const clay = window.clay, hyperclay = window.hyperclay;
|
|
14
|
+
if (name === "consent") return clay?.confirm || hyperclay?.consent || null;
|
|
15
|
+
return clay?.[name] || hyperclay?.[name] || null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Both clients dispatch the same moment under different names, and clayjs fires its
|
|
19
|
+
// pair back to back in one tick. A plain listener on each name would run the handler
|
|
20
|
+
// twice per occurrence.
|
|
21
|
+
export const MUTATION_READY = ["clay:mutation-ready", "hyperclay:mutation-ready"];
|
|
22
|
+
export const LIVESYNC_APPLIED = ["clay:sync-applied", "hyperclay:livesync-applied"];
|
|
23
|
+
|
|
24
|
+
// The first name to arrive claims the tick, and only a DIFFERENT name is suppressed
|
|
25
|
+
// while it holds the claim. Guarding on "have I run at all this tick" would collapse
|
|
26
|
+
// two genuine occurrences into one.
|
|
27
|
+
export function onPlatformEvent(target, names, handler) {
|
|
28
|
+
let claimedBy = null;
|
|
29
|
+
const wrapped = (event) => {
|
|
30
|
+
if (claimedBy !== null && claimedBy !== event.type) return;
|
|
31
|
+
claimedBy = event.type;
|
|
32
|
+
queueMicrotask(() => { claimedBy = null; });
|
|
33
|
+
handler(event);
|
|
34
|
+
};
|
|
35
|
+
for (const name of names) target.addEventListener(name, wrapped);
|
|
36
|
+
return () => { for (const name of names) target.removeEventListener(name, wrapped); };
|
|
37
|
+
}
|
|
38
|
+
|
|
5
39
|
// Region-aware save guard. When the platform's region model is present, a control
|
|
6
40
|
// inside a no-save (or frozen) region must not write save-bytes the platform would
|
|
7
41
|
// strip anyway. The control stays fully live and reactive; only the serialization
|
|
@@ -9,7 +43,7 @@
|
|
|
9
43
|
// before — same "use the platform feature if present, degrade otherwise" pattern
|
|
10
44
|
// as the undo and optionVisibility handshakes.
|
|
11
45
|
export function regionSkipsSave(el) {
|
|
12
|
-
const region =
|
|
46
|
+
const region = capability("region");
|
|
13
47
|
if (!region || typeof region.resolveRegionPolicy !== "function" || typeof region.skipForPolicy !== "function") {
|
|
14
48
|
return false;
|
|
15
49
|
}
|
|
@@ -23,13 +57,13 @@ export function regionSkipsSave(el) {
|
|
|
23
57
|
// as the callback and never goes async. Returns null standalone, so Sap falls back
|
|
24
58
|
// to window.confirm — same degrade-otherwise pattern as the region/undo handshakes.
|
|
25
59
|
export function platformConsent() {
|
|
26
|
-
const
|
|
27
|
-
return
|
|
60
|
+
const fn = capability("consent");
|
|
61
|
+
return typeof fn === "function" ? fn : null;
|
|
28
62
|
}
|
|
29
63
|
|
|
30
64
|
export function batch(label, fn) {
|
|
31
65
|
if (typeof fn !== "function") throw new Error("Sap.batch(label, fn): fn must be a function");
|
|
32
|
-
const u =
|
|
66
|
+
const u = capability("undo");
|
|
33
67
|
if (u && u.flush) u.flush();
|
|
34
68
|
const r = fn();
|
|
35
69
|
if (r && typeof r.then === "function") throw new Error("Sap.batch fn must be synchronous (it returned a promise)");
|
|
@@ -57,18 +91,18 @@ export function installBridges(runtime) {
|
|
|
57
91
|
};
|
|
58
92
|
|
|
59
93
|
// M2: livesync-applied must refresh synchronously inside the pause window.
|
|
60
|
-
document
|
|
94
|
+
onPlatformEvent(document, LIVESYNC_APPLIED, refreshConnected);
|
|
61
95
|
|
|
62
96
|
// Undo/redo replay attributes with no events; re-derive after.
|
|
63
|
-
const undo =
|
|
97
|
+
const undo = capability("undo");
|
|
64
98
|
if (undo && typeof undo.on === "function") {
|
|
65
99
|
undo.on("undo", refreshConnected);
|
|
66
100
|
undo.on("redo", refreshConnected);
|
|
67
101
|
} else {
|
|
68
102
|
// Lazy handshake: the platform may load after us. The hub dispatches
|
|
69
|
-
//
|
|
70
|
-
document
|
|
71
|
-
const u =
|
|
103
|
+
// mutation-ready non-bubbling on `document`, so listen there (not window).
|
|
104
|
+
onPlatformEvent(document, MUTATION_READY, () => {
|
|
105
|
+
const u = capability("undo");
|
|
72
106
|
if (u && typeof u.on === "function") {
|
|
73
107
|
u.on("undo", refreshConnected);
|
|
74
108
|
u.on("redo", refreshConnected);
|