@plentico/pattr 0.0.4 → 0.0.6

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 CHANGED
@@ -66,6 +66,33 @@ Both data sources are merged and available throughout your app.
66
66
  <span p-show="!show_menu">Menu is hidden</span>
67
67
  ```
68
68
 
69
+ #### `p-show:pre-scope` - Pre-Scope Conditional Display
70
+
71
+ For use with wrapped components (e.g., from Pico templating), evaluate visibility **before** `p-scope` runs:
72
+
73
+ ```html
74
+ <!-- From Pico: {if age > 10} wraps a component that has p-scope -->
75
+ <div p-show:pre-scope="age > 10" p-scope="double = age * 2">
76
+ Your doubled age is: <span p-text="double"></span>
77
+ </div>
78
+ ```
79
+
80
+ This ensures the conditional from the parent template (`age > 10`) is evaluated using the **parent scope** before the component's `p-scope` executes. Without this, the condition would be evaluated after scoping, which may not be the desired behavior for conditional wrapper components.
81
+
82
+ You can combine both for complex scenarios:
83
+
84
+ ```html
85
+ <!-- Pre-scope: evaluated before p-scope (parent scope context) -->
86
+ <!-- Regular p-show: evaluated after p-scope (scoped context) -->
87
+ <div
88
+ p-show:pre-scope="age > 10"
89
+ p-show="double > 30"
90
+ p-scope="double = age * 2"
91
+ >
92
+ Your doubled age (<span p-text="double"></span>) is greater than 30
93
+ </div>
94
+ ```
95
+
69
96
  ### `p-style` - Set Styles
70
97
 
71
98
  **String syntax:**
@@ -158,7 +185,7 @@ Available events: `click`, `focus`, `blur`, `input`, `change`, `submit`, `keydow
158
185
 
159
186
  ## Scoped Components
160
187
 
161
- Create nested components with isolated reactive state using `p-scope` and `p-id`:
188
+ Create nested components with isolated reactive state using `p-scope` (and optionally `p-id`):
162
189
 
163
190
  ```html
164
191
  <div>
@@ -170,13 +197,15 @@ Create nested components with isolated reactive state using `p-scope` and `p-id`
170
197
  <div>Child count (×2): <span p-text="count"></span></div>
171
198
  <button p-on:click="count++">+</button>
172
199
 
173
- <section p-id="grandchild1" p-scope="count = count + 1;">
200
+ <section p-scope="count = count + 1;">
174
201
  <div>Grandchild count (+1): <span p-text="count"></span></div>
175
202
  <button p-on:click="count++">+</button>
176
203
  </section>
177
204
  </section>
178
205
  ```
179
206
 
207
+ > The `p-id` attribute used to be required to use `p-scope`, now it's optional and can be omitted entirely. It's still available because it could be helpful for debugging purposes, migrating data between elements, or if external tools/scripts need to reference scopes by name.
208
+
180
209
  ### Scoping Example
181
210
 
182
211
  Components down the chain can diverge from the reactivity provided by their Parent components. However, if a Parent component is updated, it will resync all descendant components.
@@ -391,7 +420,7 @@ import Pattr from '@plentico/pattr';
391
420
  </template>
392
421
 
393
422
  <!-- Scoped component -->
394
- <section p-id="stats" p-scope="count = todos.length;">
423
+ <section p-scope="count = todos.length;">
395
424
  <p>Total todos: <span p-text="count"></span></p>
396
425
  </section>
397
426
  </body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plentico/pattr",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "description": "A lightweight reactive framework",
5
5
  "type": "module",
6
6
  "main": "pattr.js",
package/pattr.js CHANGED
@@ -203,10 +203,25 @@ window.Pattr = {
203
203
  this.refreshAllLoops();
204
204
  },
205
205
 
206
+ generateDomPathId(el) {
207
+ const path = [];
208
+ let node = el;
209
+ while (node && node.tagName !== 'HTML') {
210
+ const parent = node.parentElement;
211
+ if (parent) {
212
+ const index = Array.from(parent.children).indexOf(node);
213
+ path.unshift(`${node.tagName}[${index}]`);
214
+ }
215
+ node = parent;
216
+ }
217
+ return path.join('/');
218
+ },
219
+
206
220
  buildScopeData(el, parentData) {
207
221
  let currentData = parentData;
208
222
  if (el.hasAttribute('p-scope')) {
209
- const dataId = el.getAttribute('p-id') || 'missing_p-id';
223
+ // Use explicit p-id if provided, otherwise generate from DOM position
224
+ const dataId = el.getAttribute('p-id') || this.generateDomPathId(el);
210
225
  if (!parentData._p_children) {
211
226
  parentData._p_children = {};
212
227
  }
@@ -215,6 +230,8 @@ window.Pattr = {
215
230
  }
216
231
  currentData = parentData._p_children[dataId];
217
232
  currentData._p_scope = el.getAttribute('p-scope');
233
+ // Store the ID for later use
234
+ currentData._p_id = dataId;
218
235
  }
219
236
  let child = el.firstElementChild;
220
237
  while (child) {
@@ -257,7 +274,7 @@ window.Pattr = {
257
274
  * Creates a new scope for an element with p-scope during hydration
258
275
  */
259
276
  initScope(el, parentScope) {
260
- const dataId = el.getAttribute('p-id');
277
+ const dataId = el.getAttribute('p-id') || this.generateDomPathId(el);
261
278
  const localRawData = parentScope._p_target._p_children[dataId];
262
279
 
263
280
  // Create new inherited Proxy
@@ -958,6 +975,16 @@ window.Pattr = {
958
975
  return;
959
976
  }
960
977
 
978
+ // Evaluate p-show.pre-scope before determining scope (for Pico conditional components)
979
+ const preScopeShowAttr = Array.from(el.attributes).find(attr => {
980
+ const parsed = this.parseDirectiveModifiers(attr.name);
981
+ return parsed.directive === 'p-show' && parsed.modifiers['pre-scope'];
982
+ });
983
+ if (preScopeShowAttr && parentScope) {
984
+ const value = eval(`with (parentScope) { (${preScopeShowAttr.value}) }`);
985
+ this.directives['p-show'](el, value);
986
+ }
987
+
961
988
  // Determine the scope for this element
962
989
  let currentScope = parentScope;
963
990
 
@@ -976,9 +1003,20 @@ window.Pattr = {
976
1003
  this.registerModelBinding(el);
977
1004
  }
978
1005
 
979
- // Evaluate directives
1006
+ // Evaluate directives (skip pre-scope show as it's already handled)
980
1007
  if (currentScope) {
981
- this.evaluateDirectives(el, currentScope);
1008
+ Array.from(el.attributes).forEach(attr => {
1009
+ const parsed = this.parseDirectiveModifiers(attr.name);
1010
+ // Skip pre-scope show, handle regular p-show and other directives
1011
+ if (parsed.directive === 'p-show' && parsed.modifiers['pre-scope']) {
1012
+ return; // Already handled above
1013
+ }
1014
+ if (Object.keys(this.directives).includes(parsed.directive)) {
1015
+ const evalScope = el._scope || currentScope;
1016
+ const value = eval(`with (evalScope) { (${attr.value}) }`);
1017
+ this.directives[parsed.directive](el, value, parsed.modifiers);
1018
+ }
1019
+ });
982
1020
  }
983
1021
 
984
1022
  // Recurse to children
package/pattr.min.js CHANGED
@@ -4,4 +4,4 @@
4
4
  * https://github.com/plentico/pattr
5
5
  * MIT License
6
6
  */
7
- window.Pattr={_templateScopeCounter:0,directives:{"p-text":(e,t,r={})=>{let o=String(t);if(r.trim&&r.trim.length>0){const e=parseInt(r.trim[0])||100;o.length>e&&(o=o.substring(0,e)+"...")}e.innerText=o},"p-html":(e,t,r={})=>{let o=t;if(r.allow&&r.allow.length>0){const e=r.allow,t=document.createElement("div");t.innerHTML=o;const a=t=>{if(t.nodeType===Node.ELEMENT_NODE){const r=t.tagName.toLowerCase();if(!e.includes(r))return document.createTextNode(t.textContent);const o=t.cloneNode(!1);return Array.from(t.childNodes).forEach(e=>{const t=a(e);t&&o.appendChild(t)}),o}return t.cloneNode()},s=document.createElement("div");Array.from(t.childNodes).forEach(e=>{const t=a(e);t&&s.appendChild(t)}),o=s.innerHTML}if(r.trim&&r.trim.length>0){const e=parseInt(r.trim[0])||100,t=document.createElement("div");t.innerHTML=o;let a=0,s=!1;const n=t=>{if(s)return null;if(t.nodeType===Node.TEXT_NODE){const r=t.textContent,o=e-a;if(r.length<=o)return a+=r.length,t.cloneNode();{s=!0;const e=r.substring(0,o)+"...";return document.createTextNode(e)}}if(t.nodeType===Node.ELEMENT_NODE){const e=t.cloneNode(!1);for(let r of t.childNodes){const t=n(r);if(t&&e.appendChild(t),s)break}return e}return t.cloneNode()},i=document.createElement("div");for(let e of t.childNodes){const t=n(e);if(t&&i.appendChild(t),s)break}o=i.innerHTML}e.innerHTML=o},"p-show":(e,t)=>{e.style.display=t?"":"none"},"p-style":(e,t)=>{"string"==typeof t?e.style.cssText=t:"object"==typeof t&&null!==t&&Object.assign(e.style,t)},"p-class":(e,t)=>{"string"==typeof t?e.className=t:Array.isArray(t)?e.className=t.join(" "):"object"==typeof t&&null!==t&&(e.className=Object.keys(t).filter(e=>t[e]).join(" "))},"p-model":(e,t)=>{e.value=t},"p-attr":(e,t,r={})=>{const o=Object.keys(r);if(o.length>0){const r=o[0];null==t||!1===t?e.removeAttribute(r):e.setAttribute(r,String(t))}else"object"==typeof t&&null!==t&&Object.keys(t).forEach(r=>{const o=t[r];null==o||!1===o?e.removeAttribute(r):e.setAttribute(r,String(o))})}},parseDirectiveModifiers(e){const t=e.split(":"),r=t[0],o={};for(let e=1;e<t.length;e++){const r=t[e].split("."),a=r[0],s=r.slice(1);o[a]=s}return{directive:r,modifiers:o}},async start(){this.root=document.documentElement;const e=document.getElementById("p-root-data")?.textContent;try{this.rawData=JSON.parse(e||"{}")}catch(e){console.error("Error parsing root data JSON:",e)}this.buildScopeData(this.root,this.rawData),this.data=this.observe(this.rawData),this.walkDom(this.root,this.data,!0),this.refreshAllLoops()},buildScopeData(e,t){let r=t;if(e.hasAttribute("p-scope")){const o=e.getAttribute("p-id")||"missing_p-id";t._p_children||(t._p_children={}),t._p_children[o]||(t._p_children[o]={}),r=t._p_children[o],r._p_scope=e.getAttribute("p-scope")}let o=e.firstElementChild;for(;o;)this.buildScopeData(o,r),o=o.nextElementSibling},observe(e,t){const r=e;let o=r;t&&(o=Object.create(t._p_target||t),Object.assign(o,r));return new Proxy(o,{get:(e,t)=>"_p_target"===t?e:e[t],set:(e,t,r)=>(e[t]=r,this.walkDom(this.root,this.data,!1),!0),has:(e,t)=>t in e})},initScope(e,t){const r=e.getAttribute("p-id"),o=t._p_target._p_children[r],a=this.observe(o,t);this.executePScopeStatements(a,o._p_scope);const s=Object.getPrototypeOf(a._p_target);e._parentSnapshot={};for(let t in s)t.startsWith("_p_")||(e._parentSnapshot[t]=s[t]);const n=a._p_target;e._localSnapshot={};for(let t of Object.keys(n))t.startsWith("_p_")||(e._localSnapshot[t]=n[t]);return a},refreshScope(e,t){let r=e._scope;if(!r)return t;const o=e.getAttribute("p-scope");return o&&r._p_target&&this.updateScopeFromParent(e,r,o),r},executePScopeStatements(scope,pScopeExpr){const statements=pScopeExpr.split(";").map(e=>e.trim()).filter(e=>e),target=scope._p_target,sequentialScope=new Proxy(target,{get:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)?e[t]:scope[t],has:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)||t in scope});for(const stmt of statements){const match=stmt.match(/^(\w+)\s*=\s*(.+)$/);if(match){const[,varName,expr]=match;try{const value=eval(`with (sequentialScope) { (${expr}) }`);target[varName]=value}catch(e){console.error(`Error executing p-scope statement "${stmt}":`,e)}}}},updateScopeFromParent(el,scope,pScopeExpr){const parentProto=Object.getPrototypeOf(scope._p_target),target=scope._p_target,changedParentVars=new Set,changedLocalVars=new Set;el._parentSnapshot||(el._parentSnapshot={});for(let e in parentProto)e.startsWith("_p_")||(el._parentSnapshot[e]!==parentProto[e]&&changedParentVars.add(e),el._parentSnapshot[e]=parentProto[e]);el._localSnapshot||(el._localSnapshot={});for(let e of Object.keys(target))e.startsWith("_p_")||(el._localSnapshot[e]!==target[e]&&changedLocalVars.add(e),el._localSnapshot[e]=target[e]);const allChangedVars=new Set([...changedParentVars,...changedLocalVars]);if(0!==allChangedVars.size)try{const statements=pScopeExpr.split(";").map(e=>e.trim()).filter(e=>e),outputVars=new Set;statements.forEach(e=>{const t=e.match(/^(\w+)\s*=\s*(.+)$/);t&&outputVars.add(t[1])});const setInThisPass=new Set,sequentialScope=new Proxy(target,{get:(e,t)=>"_p_target"===t||"_p_children"===t||"_p_scope"===t||setInThisPass.has(t)?e[t]:changedParentVars.has(t)?parentProto[t]:changedLocalVars.has(t)||Object.prototype.hasOwnProperty.call(e,t)?e[t]:parentProto[t],set:(e,t,r)=>(e[t]=r,!0),has:(e,t)=>setInThisPass.has(t)||Object.prototype.hasOwnProperty.call(e,t)||t in parentProto});statements.forEach(stmt=>{let shouldExecute=!1;const parts=stmt.split("=");if(parts.length<=1)return;const rhs=parts.slice(1).join("=");if(changedParentVars.forEach(e=>{rhs.includes(e)&&(shouldExecute=!0)}),changedLocalVars.forEach(e=>{!outputVars.has(e)&&rhs.includes(e)&&(shouldExecute=!0)}),shouldExecute||setInThisPass.forEach(e=>{rhs.includes(e)&&(shouldExecute=!0)}),shouldExecute){const match=stmt.match(/^(\w+)\s*=\s*(.+)$/);if(match){const[,varName,expr]=match,value=eval(`with (sequentialScope) { (${expr}) }`);target[varName]=value,setInThisPass.add(varName)}}});for(let e of Object.keys(target))e.startsWith("_p_")||(el._localSnapshot[e]=target[e])}catch(e){console.error("Error re-executing p-scope expression:",e)}},registerEventListeners(el){Array.from(el.attributes).forEach(attr=>{if(attr.name.startsWith("p-on:")){const event=attr.name.replace("p-on:","");el.addEventListener(event,()=>{eval(`with (el._scope) { ${attr.value} }`),this.refreshAllLoops()})}})},registerModelBinding(el){const modelAttr=el.getAttribute("p-model");modelAttr&&(el.addEventListener("input",e=>{let value;const type=e.target.type;value="number"===type||"range"===type?""===e.target.value?null:Number(e.target.value):"checkbox"===type?e.target.checked:"radio"===type?e.target.checked?e.target.value:void 0:e.target.value,void 0!==value&&eval(`with (el._scope) { ${modelAttr} = value }`)}),"checkbox"!==el.type&&"radio"!==el.type||el.addEventListener("change",e=>{let value;value="checkbox"===el.type?e.target.checked:e.target.checked?e.target.value:void 0,void 0!==value&&eval(`with (el._scope) { ${modelAttr} = value }`)}))},evaluateDirectives(el,scope){Array.from(el.attributes).forEach(attr=>{const parsed=this.parseDirectiveModifiers(attr.name);if(Object.keys(this.directives).includes(parsed.directive)){const evalScope=el._scope||scope,value=eval(`with (evalScope) { (${attr.value}) }`);this.directives[parsed.directive](el,value,parsed.modifiers)}})},setForTemplateRecursive(e,t){e._forTemplate=t,Array.from(e.children).forEach(e=>{this.setForTemplateRecursive(e,t)})},refreshAllLoops(e=this.root,t=new Set){if("TEMPLATE"===e.tagName&&e.hasAttribute("p-for")){if(t.has(e))return;return t.add(e),void this.handleFor(e,e._scope||this.data,!1)}let r=e.firstElementChild;for(;r;){const e=r.nextElementSibling;this.refreshAllLoops(r,t),r=e}},handleFor(e,t,r){const o=e.getAttribute("p-for"),a=o.match(/^(?:const|let)?\s*(.+?)\s+(of|in)\s+(.+)$/);if(!a)return void console.error(`Invalid p-for expression: ${o}`);const[,s,n,i]=a;r?this.hydrateLoop(e,t,s,i):this.refreshLoop(e,t,s,i)},getTemplateScopePrefix(e){let t="";if(e._forTemplate){const r=e._forTemplate._forData;if(r&&r.scopePrefix){let r=e.previousElementSibling;for(;r;){if(r.hasAttribute&&r.hasAttribute("p-for-key")){t=r.getAttribute("p-for-key")+"-";break}r=r.previousElementSibling}}}if(!t){let r=e.previousElementSibling;for(;r;){if(r.hasAttribute&&r.hasAttribute("p-for-key")&&r._forTemplate===e._forTemplate){t=r.getAttribute("p-for-key")+"-";break}r=r.previousElementSibling}}if(!t){let r=e.parentElement;for(;r;){if(r.hasAttribute&&r.hasAttribute("p-for-key")){t=r.getAttribute("p-for-key")+"-";break}if(r._forTemplate){const e=r._forTemplate._forData;if(e){const o=e.renderedElements.indexOf(r);if(o>=0){t=(e.scopePrefix||"")+o+"-";break}}}r=r.parentElement}}return e._forScopeId||(e._forScopeId="s"+this._templateScopeCounter++),t+e._forScopeId+":"},hydrateLoop(template,parentScope,varPattern,iterableExpr){template._scope=parentScope;try{const iterable=eval(`with (parentScope) { (${iterableExpr}) }`),scopePrefix=this.getTemplateScopePrefix(template);template._forData={varPattern:varPattern,iterableExpr:iterableExpr,renderedElements:[],scopePrefix:scopePrefix};const existingElementsByKey={};let sibling=template.nextElementSibling;for(;sibling&&sibling.hasAttribute("p-for-key");){const e=sibling.getAttribute("p-for-key"),t=e.startsWith(scopePrefix)||!e.includes(":")&&!e.includes("-");if(t){let t;t=e.startsWith(scopePrefix)?e.substring(scopePrefix.length):e,existingElementsByKey[t]||(existingElementsByKey[t]=[]),existingElementsByKey[t].push(sibling)}sibling=sibling.nextElementSibling}let index=0,lastInserted=template;for(const e of iterable){const t=this.createLoopScope(parentScope,varPattern,e);if(existingElementsByKey[String(index)])existingElementsByKey[String(index)].forEach(e=>{e._scope=t,this.setForTemplateRecursive(e,template),"TEMPLATE"!==e.tagName&&e.setAttribute("p-for-key",scopePrefix+index),this.walkDom(e,t,!0),template._forData.renderedElements.push(e),lastInserted=e});else{const e=template.content.cloneNode(!0),r=Array.from(e.children);r.forEach(e=>{e._scope=t,this.setForTemplateRecursive(e,template),"TEMPLATE"!==e.tagName&&e.setAttribute("p-for-key",scopePrefix+index),this.walkDom(e,t,!0)});const o=document.createDocumentFragment();r.forEach(e=>o.appendChild(e)),lastInserted.parentNode.insertBefore(o,lastInserted.nextSibling),template._forData.renderedElements.push(...r),lastInserted=r[r.length-1]||lastInserted}index++}Object.keys(existingElementsByKey).forEach(e=>{parseInt(e)>=index&&existingElementsByKey[e].forEach(e=>e.remove())})}catch(e){console.error(`Error in p-for hydration: ${iterableExpr}`,e)}},removeLoopElements(e){e.forEach(e=>{if(e._forData&&e._forData.renderedElements&&(this.removeLoopElements(e._forData.renderedElements),e._forData.renderedElements=[]),e.querySelectorAll){e.querySelectorAll("template[p-for]").forEach(e=>{e._forData&&e._forData.renderedElements&&(this.removeLoopElements(e._forData.renderedElements),e._forData.renderedElements=[])})}e.remove()})},refreshLoop(template,parentScope,varPattern,iterableExpr){const forData=template._forData;if(forData)try{const iterable=eval(`with (parentScope._p_target || parentScope) { (${iterableExpr}) }`),scopePrefix=forData.scopePrefix||this.getTemplateScopePrefix(template);this.removeLoopElements(forData.renderedElements),forData.renderedElements=[];let lastInsertedElement=template,index=0;for(const e of iterable){const t=template.content.cloneNode(!0),r=this.createLoopScope(parentScope,forData.varPattern,e),o=Array.from(t.children);o.forEach(e=>{e._scope=r,this.setForTemplateRecursive(e,template),"TEMPLATE"!==e.tagName&&e.setAttribute("p-for-key",scopePrefix+index)});const a=document.createDocumentFragment();o.forEach(e=>a.appendChild(e)),lastInsertedElement.parentNode.insertBefore(a,lastInsertedElement.nextSibling),forData.renderedElements.push(...o),o.forEach(e=>{this.walkDom(e,r,!0)});const s=o[o.length-1];s&&(lastInsertedElement=this.findLastRenderedSibling(s)),index++}}catch(e){console.error(`Error in p-for refresh: ${iterableExpr}`,e)}},findLastRenderedSibling(e){if("TEMPLATE"===e.tagName&&e._forData&&e._forData.renderedElements.length>0){const t=e._forData.renderedElements[e._forData.renderedElements.length-1];return this.findLastRenderedSibling(t)}return e},createLoopScope(e,t,r){const o={};if((t=t.trim()).startsWith("[")){const e=t.slice(1,-1).split(",").map(e=>e.trim());Array.isArray(r)?e.forEach((e,t)=>o[e]=r[t]):o[e[0]]=r}else if(t.startsWith("{")){t.slice(1,-1).split(",").map(e=>e.trim()).forEach(e=>{const[t,a]=e.split(":").map(e=>e.trim());o[a||t]=r[t]})}else o[t]=r;if(!e)return console.error("parentScope is undefined in createLoopScope"),new Proxy({},{get:()=>{},set:()=>!1});const a=e._p_target||e;if(!a||"object"!=typeof a)return console.error("Invalid parentTarget:",a),new Proxy(o,{get:(e,t)=>e[t],set:(e,t,r)=>(e[t]=r,!0)});const s=Object.create(a);Object.assign(s,o);const n=new Proxy(s,{get:(e,t)=>e[t],set:(e,t,r)=>(t in o?e[t]=r:a[t]=r,!0)});return n._p_target=s,n},walkDom(e,t,r=!1){if("TEMPLATE"===e.tagName&&e.hasAttribute("p-for")){if(!r&&e._forTemplate)return;return void this.handleFor(e,t,r)}let o=t;e.hasAttribute("p-scope")&&(o=r?this.initScope(e,t):this.refreshScope(e,t)),r&&(e._scope=o,this.registerEventListeners(e),this.registerModelBinding(e)),o&&this.evaluateDirectives(e,o);const a=Array.from(e.children);for(const e of a)this.walkDom(e,o,r)}},window.Pattr.start();
7
+ window.Pattr={_templateScopeCounter:0,directives:{"p-text":(e,t,r={})=>{let o=String(t);if(r.trim&&r.trim.length>0){const e=parseInt(r.trim[0])||100;o.length>e&&(o=o.substring(0,e)+"...")}e.innerText=o},"p-html":(e,t,r={})=>{let o=t;if(r.allow&&r.allow.length>0){const e=r.allow,t=document.createElement("div");t.innerHTML=o;const a=t=>{if(t.nodeType===Node.ELEMENT_NODE){const r=t.tagName.toLowerCase();if(!e.includes(r))return document.createTextNode(t.textContent);const o=t.cloneNode(!1);return Array.from(t.childNodes).forEach(e=>{const t=a(e);t&&o.appendChild(t)}),o}return t.cloneNode()},s=document.createElement("div");Array.from(t.childNodes).forEach(e=>{const t=a(e);t&&s.appendChild(t)}),o=s.innerHTML}if(r.trim&&r.trim.length>0){const e=parseInt(r.trim[0])||100,t=document.createElement("div");t.innerHTML=o;let a=0,s=!1;const n=t=>{if(s)return null;if(t.nodeType===Node.TEXT_NODE){const r=t.textContent,o=e-a;if(r.length<=o)return a+=r.length,t.cloneNode();{s=!0;const e=r.substring(0,o)+"...";return document.createTextNode(e)}}if(t.nodeType===Node.ELEMENT_NODE){const e=t.cloneNode(!1);for(let r of t.childNodes){const t=n(r);if(t&&e.appendChild(t),s)break}return e}return t.cloneNode()},i=document.createElement("div");for(let e of t.childNodes){const t=n(e);if(t&&i.appendChild(t),s)break}o=i.innerHTML}e.innerHTML=o},"p-show":(e,t)=>{e.style.display=t?"":"none"},"p-style":(e,t)=>{"string"==typeof t?e.style.cssText=t:"object"==typeof t&&null!==t&&Object.assign(e.style,t)},"p-class":(e,t)=>{"string"==typeof t?e.className=t:Array.isArray(t)?e.className=t.join(" "):"object"==typeof t&&null!==t&&(e.className=Object.keys(t).filter(e=>t[e]).join(" "))},"p-model":(e,t)=>{e.value=t},"p-attr":(e,t,r={})=>{const o=Object.keys(r);if(o.length>0){const r=o[0];null==t||!1===t?e.removeAttribute(r):e.setAttribute(r,String(t))}else"object"==typeof t&&null!==t&&Object.keys(t).forEach(r=>{const o=t[r];null==o||!1===o?e.removeAttribute(r):e.setAttribute(r,String(o))})}},parseDirectiveModifiers(e){const t=e.split(":"),r=t[0],o={};for(let e=1;e<t.length;e++){const r=t[e].split("."),a=r[0],s=r.slice(1);o[a]=s}return{directive:r,modifiers:o}},async start(){this.root=document.documentElement;const e=document.getElementById("p-root-data")?.textContent;try{this.rawData=JSON.parse(e||"{}")}catch(e){console.error("Error parsing root data JSON:",e)}this.buildScopeData(this.root,this.rawData),this.data=this.observe(this.rawData),this.walkDom(this.root,this.data,!0),this.refreshAllLoops()},generateDomPathId(e){const t=[];let r=e;for(;r&&"HTML"!==r.tagName;){const e=r.parentElement;if(e){const o=Array.from(e.children).indexOf(r);t.unshift(`${r.tagName}[${o}]`)}r=e}return t.join("/")},buildScopeData(e,t){let r=t;if(e.hasAttribute("p-scope")){const o=e.getAttribute("p-id")||this.generateDomPathId(e);t._p_children||(t._p_children={}),t._p_children[o]||(t._p_children[o]={}),r=t._p_children[o],r._p_scope=e.getAttribute("p-scope"),r._p_id=o}let o=e.firstElementChild;for(;o;)this.buildScopeData(o,r),o=o.nextElementSibling},observe(e,t){const r=e;let o=r;t&&(o=Object.create(t._p_target||t),Object.assign(o,r));return new Proxy(o,{get:(e,t)=>"_p_target"===t?e:e[t],set:(e,t,r)=>(e[t]=r,this.walkDom(this.root,this.data,!1),!0),has:(e,t)=>t in e})},initScope(e,t){const r=e.getAttribute("p-id")||this.generateDomPathId(e),o=t._p_target._p_children[r],a=this.observe(o,t);this.executePScopeStatements(a,o._p_scope);const s=Object.getPrototypeOf(a._p_target);e._parentSnapshot={};for(let t in s)t.startsWith("_p_")||(e._parentSnapshot[t]=s[t]);const n=a._p_target;e._localSnapshot={};for(let t of Object.keys(n))t.startsWith("_p_")||(e._localSnapshot[t]=n[t]);return a},refreshScope(e,t){let r=e._scope;if(!r)return t;const o=e.getAttribute("p-scope");return o&&r._p_target&&this.updateScopeFromParent(e,r,o),r},executePScopeStatements(scope,pScopeExpr){const statements=pScopeExpr.split(";").map(e=>e.trim()).filter(e=>e),target=scope._p_target,sequentialScope=new Proxy(target,{get:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)?e[t]:scope[t],has:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)||t in scope});for(const stmt of statements){const match=stmt.match(/^(\w+)\s*=\s*(.+)$/);if(match){const[,varName,expr]=match;try{const value=eval(`with (sequentialScope) { (${expr}) }`);target[varName]=value}catch(e){console.error(`Error executing p-scope statement "${stmt}":`,e)}}}},updateScopeFromParent(el,scope,pScopeExpr){const parentProto=Object.getPrototypeOf(scope._p_target),target=scope._p_target,changedParentVars=new Set,changedLocalVars=new Set;el._parentSnapshot||(el._parentSnapshot={});for(let e in parentProto)e.startsWith("_p_")||(el._parentSnapshot[e]!==parentProto[e]&&changedParentVars.add(e),el._parentSnapshot[e]=parentProto[e]);el._localSnapshot||(el._localSnapshot={});for(let e of Object.keys(target))e.startsWith("_p_")||(el._localSnapshot[e]!==target[e]&&changedLocalVars.add(e),el._localSnapshot[e]=target[e]);const allChangedVars=new Set([...changedParentVars,...changedLocalVars]);if(0!==allChangedVars.size)try{const statements=pScopeExpr.split(";").map(e=>e.trim()).filter(e=>e),outputVars=new Set;statements.forEach(e=>{const t=e.match(/^(\w+)\s*=\s*(.+)$/);t&&outputVars.add(t[1])});const setInThisPass=new Set,sequentialScope=new Proxy(target,{get:(e,t)=>"_p_target"===t||"_p_children"===t||"_p_scope"===t||setInThisPass.has(t)?e[t]:changedParentVars.has(t)?parentProto[t]:changedLocalVars.has(t)||Object.prototype.hasOwnProperty.call(e,t)?e[t]:parentProto[t],set:(e,t,r)=>(e[t]=r,!0),has:(e,t)=>setInThisPass.has(t)||Object.prototype.hasOwnProperty.call(e,t)||t in parentProto});statements.forEach(stmt=>{let shouldExecute=!1;const parts=stmt.split("=");if(parts.length<=1)return;const rhs=parts.slice(1).join("=");if(changedParentVars.forEach(e=>{rhs.includes(e)&&(shouldExecute=!0)}),changedLocalVars.forEach(e=>{!outputVars.has(e)&&rhs.includes(e)&&(shouldExecute=!0)}),shouldExecute||setInThisPass.forEach(e=>{rhs.includes(e)&&(shouldExecute=!0)}),shouldExecute){const match=stmt.match(/^(\w+)\s*=\s*(.+)$/);if(match){const[,varName,expr]=match,value=eval(`with (sequentialScope) { (${expr}) }`);target[varName]=value,setInThisPass.add(varName)}}});for(let e of Object.keys(target))e.startsWith("_p_")||(el._localSnapshot[e]=target[e])}catch(e){console.error("Error re-executing p-scope expression:",e)}},registerEventListeners(el){Array.from(el.attributes).forEach(attr=>{if(attr.name.startsWith("p-on:")){const event=attr.name.replace("p-on:","");el.addEventListener(event,()=>{eval(`with (el._scope) { ${attr.value} }`),this.refreshAllLoops()})}})},registerModelBinding(el){const modelAttr=el.getAttribute("p-model");modelAttr&&(el.addEventListener("input",e=>{let value;const type=e.target.type;value="number"===type||"range"===type?""===e.target.value?null:Number(e.target.value):"checkbox"===type?e.target.checked:"radio"===type?e.target.checked?e.target.value:void 0:e.target.value,void 0!==value&&eval(`with (el._scope) { ${modelAttr} = value }`)}),"checkbox"!==el.type&&"radio"!==el.type||el.addEventListener("change",e=>{let value;value="checkbox"===el.type?e.target.checked:e.target.checked?e.target.value:void 0,void 0!==value&&eval(`with (el._scope) { ${modelAttr} = value }`)}))},evaluateDirectives(el,scope){Array.from(el.attributes).forEach(attr=>{const parsed=this.parseDirectiveModifiers(attr.name);if(Object.keys(this.directives).includes(parsed.directive)){const evalScope=el._scope||scope,value=eval(`with (evalScope) { (${attr.value}) }`);this.directives[parsed.directive](el,value,parsed.modifiers)}})},setForTemplateRecursive(e,t){e._forTemplate=t,Array.from(e.children).forEach(e=>{this.setForTemplateRecursive(e,t)})},refreshAllLoops(e=this.root,t=new Set){if("TEMPLATE"===e.tagName&&e.hasAttribute("p-for")){if(t.has(e))return;return t.add(e),void this.handleFor(e,e._scope||this.data,!1)}let r=e.firstElementChild;for(;r;){const e=r.nextElementSibling;this.refreshAllLoops(r,t),r=e}},handleFor(e,t,r){const o=e.getAttribute("p-for"),a=o.match(/^(?:const|let)?\s*(.+?)\s+(of|in)\s+(.+)$/);if(!a)return void console.error(`Invalid p-for expression: ${o}`);const[,s,n,i]=a;r?this.hydrateLoop(e,t,s,i):this.refreshLoop(e,t,s,i)},getTemplateScopePrefix(e){let t="";if(e._forTemplate){const r=e._forTemplate._forData;if(r&&r.scopePrefix){let r=e.previousElementSibling;for(;r;){if(r.hasAttribute&&r.hasAttribute("p-for-key")){t=r.getAttribute("p-for-key")+"-";break}r=r.previousElementSibling}}}if(!t){let r=e.previousElementSibling;for(;r;){if(r.hasAttribute&&r.hasAttribute("p-for-key")&&r._forTemplate===e._forTemplate){t=r.getAttribute("p-for-key")+"-";break}r=r.previousElementSibling}}if(!t){let r=e.parentElement;for(;r;){if(r.hasAttribute&&r.hasAttribute("p-for-key")){t=r.getAttribute("p-for-key")+"-";break}if(r._forTemplate){const e=r._forTemplate._forData;if(e){const o=e.renderedElements.indexOf(r);if(o>=0){t=(e.scopePrefix||"")+o+"-";break}}}r=r.parentElement}}return e._forScopeId||(e._forScopeId="s"+this._templateScopeCounter++),t+e._forScopeId+":"},hydrateLoop(template,parentScope,varPattern,iterableExpr){template._scope=parentScope;try{const iterable=eval(`with (parentScope) { (${iterableExpr}) }`),scopePrefix=this.getTemplateScopePrefix(template);template._forData={varPattern:varPattern,iterableExpr:iterableExpr,renderedElements:[],scopePrefix:scopePrefix};const existingElementsByKey={};let sibling=template.nextElementSibling;for(;sibling&&sibling.hasAttribute("p-for-key");){const e=sibling.getAttribute("p-for-key"),t=e.startsWith(scopePrefix)||!e.includes(":")&&!e.includes("-");if(t){let t;t=e.startsWith(scopePrefix)?e.substring(scopePrefix.length):e,existingElementsByKey[t]||(existingElementsByKey[t]=[]),existingElementsByKey[t].push(sibling)}sibling=sibling.nextElementSibling}let index=0,lastInserted=template;for(const e of iterable){const t=this.createLoopScope(parentScope,varPattern,e);if(existingElementsByKey[String(index)])existingElementsByKey[String(index)].forEach(e=>{e._scope=t,this.setForTemplateRecursive(e,template),"TEMPLATE"!==e.tagName&&e.setAttribute("p-for-key",scopePrefix+index),this.walkDom(e,t,!0),template._forData.renderedElements.push(e),lastInserted=e});else{const e=template.content.cloneNode(!0),r=Array.from(e.children);r.forEach(e=>{e._scope=t,this.setForTemplateRecursive(e,template),"TEMPLATE"!==e.tagName&&e.setAttribute("p-for-key",scopePrefix+index),this.walkDom(e,t,!0)});const o=document.createDocumentFragment();r.forEach(e=>o.appendChild(e)),lastInserted.parentNode.insertBefore(o,lastInserted.nextSibling),template._forData.renderedElements.push(...r),lastInserted=r[r.length-1]||lastInserted}index++}Object.keys(existingElementsByKey).forEach(e=>{parseInt(e)>=index&&existingElementsByKey[e].forEach(e=>e.remove())})}catch(e){console.error(`Error in p-for hydration: ${iterableExpr}`,e)}},removeLoopElements(e){e.forEach(e=>{if(e._forData&&e._forData.renderedElements&&(this.removeLoopElements(e._forData.renderedElements),e._forData.renderedElements=[]),e.querySelectorAll){e.querySelectorAll("template[p-for]").forEach(e=>{e._forData&&e._forData.renderedElements&&(this.removeLoopElements(e._forData.renderedElements),e._forData.renderedElements=[])})}e.remove()})},refreshLoop(template,parentScope,varPattern,iterableExpr){const forData=template._forData;if(forData)try{const iterable=eval(`with (parentScope._p_target || parentScope) { (${iterableExpr}) }`),scopePrefix=forData.scopePrefix||this.getTemplateScopePrefix(template);this.removeLoopElements(forData.renderedElements),forData.renderedElements=[];let lastInsertedElement=template,index=0;for(const e of iterable){const t=template.content.cloneNode(!0),r=this.createLoopScope(parentScope,forData.varPattern,e),o=Array.from(t.children);o.forEach(e=>{e._scope=r,this.setForTemplateRecursive(e,template),"TEMPLATE"!==e.tagName&&e.setAttribute("p-for-key",scopePrefix+index)});const a=document.createDocumentFragment();o.forEach(e=>a.appendChild(e)),lastInsertedElement.parentNode.insertBefore(a,lastInsertedElement.nextSibling),forData.renderedElements.push(...o),o.forEach(e=>{this.walkDom(e,r,!0)});const s=o[o.length-1];s&&(lastInsertedElement=this.findLastRenderedSibling(s)),index++}}catch(e){console.error(`Error in p-for refresh: ${iterableExpr}`,e)}},findLastRenderedSibling(e){if("TEMPLATE"===e.tagName&&e._forData&&e._forData.renderedElements.length>0){const t=e._forData.renderedElements[e._forData.renderedElements.length-1];return this.findLastRenderedSibling(t)}return e},createLoopScope(e,t,r){const o={};if((t=t.trim()).startsWith("[")){const e=t.slice(1,-1).split(",").map(e=>e.trim());Array.isArray(r)?e.forEach((e,t)=>o[e]=r[t]):o[e[0]]=r}else if(t.startsWith("{")){t.slice(1,-1).split(",").map(e=>e.trim()).forEach(e=>{const[t,a]=e.split(":").map(e=>e.trim());o[a||t]=r[t]})}else o[t]=r;if(!e)return console.error("parentScope is undefined in createLoopScope"),new Proxy({},{get:()=>{},set:()=>!1});const a=e._p_target||e;if(!a||"object"!=typeof a)return console.error("Invalid parentTarget:",a),new Proxy(o,{get:(e,t)=>e[t],set:(e,t,r)=>(e[t]=r,!0)});const s=Object.create(a);Object.assign(s,o);const n=new Proxy(s,{get:(e,t)=>e[t],set:(e,t,r)=>(t in o?e[t]=r:a[t]=r,!0)});return n._p_target=s,n},walkDom(el,parentScope,isHydrating=!1){if("TEMPLATE"===el.tagName&&el.hasAttribute("p-for")){if(!isHydrating&&el._forTemplate)return;return void this.handleFor(el,parentScope,isHydrating)}const preScopeShowAttr=Array.from(el.attributes).find(e=>{const t=this.parseDirectiveModifiers(e.name);return"p-show"===t.directive&&t.modifiers["pre-scope"]});if(preScopeShowAttr&&parentScope){const value=eval(`with (parentScope) { (${preScopeShowAttr.value}) }`);this.directives["p-show"](el,value)}let currentScope=parentScope;el.hasAttribute("p-scope")&&(currentScope=isHydrating?this.initScope(el,parentScope):this.refreshScope(el,parentScope)),isHydrating&&(el._scope=currentScope,this.registerEventListeners(el),this.registerModelBinding(el)),currentScope&&Array.from(el.attributes).forEach(attr=>{const parsed=this.parseDirectiveModifiers(attr.name);if(("p-show"!==parsed.directive||!parsed.modifiers["pre-scope"])&&Object.keys(this.directives).includes(parsed.directive)){const evalScope=el._scope||currentScope,value=eval(`with (evalScope) { (${attr.value}) }`);this.directives[parsed.directive](el,value,parsed.modifiers)}});const children=Array.from(el.children);for(const e of children)this.walkDom(e,currentScope,isHydrating)}},window.Pattr.start();