@plentico/pattr 0.0.10 → 0.0.12
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 +32 -0
- package/package.json +1 -1
- package/pattr.js +101 -12
- package/pattr.min.js +1 -1
package/README.md
CHANGED
|
@@ -359,6 +359,38 @@ Pattr supports hydrating server-side rendered loops. Add `p-for-key` attributes
|
|
|
359
359
|
|
|
360
360
|
Modifiers extend directive functionality using colon syntax:
|
|
361
361
|
|
|
362
|
+
### Sync Scope (2-Way Binding)
|
|
363
|
+
|
|
364
|
+
Use `:sync` with `p-scope` to enable bidirectional data flow between parent and child scopes. Changes to local variables propagate back to the parent:
|
|
365
|
+
|
|
366
|
+
```html
|
|
367
|
+
<div>
|
|
368
|
+
Parent count: <span p-text="count"></span>
|
|
369
|
+
<button p-on:click="count++">+</button>
|
|
370
|
+
</div>
|
|
371
|
+
|
|
372
|
+
<!-- With :sync, changes to childCount update parent count too -->
|
|
373
|
+
<section p-scope:sync="childCount = count">
|
|
374
|
+
<div>Synced count: <span p-text="childCount"></span></div>
|
|
375
|
+
<button p-on:click="childCount++">+ (updates parent)</button>
|
|
376
|
+
</section>
|
|
377
|
+
|
|
378
|
+
<!-- Without :sync (default), changes stay local -->
|
|
379
|
+
<section p-scope="localCount = count">
|
|
380
|
+
<div>Local count: <span p-text="localCount"></span></div>
|
|
381
|
+
<button p-on:click="localCount++">+ (stays local)</button>
|
|
382
|
+
</section>
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
Works with multiple variables and computed expressions:
|
|
386
|
+
|
|
387
|
+
```html
|
|
388
|
+
<section p-scope:sync="x = a; y = b * 2">
|
|
389
|
+
<input p-model="x" /> <!-- Updates parent 'a' -->
|
|
390
|
+
<input p-model="y" /> <!-- Updates parent 'b' -->
|
|
391
|
+
</section>
|
|
392
|
+
```
|
|
393
|
+
|
|
362
394
|
### Trim Text
|
|
363
395
|
|
|
364
396
|
Limit text length and add ellipsis:
|
package/package.json
CHANGED
package/pattr.js
CHANGED
|
@@ -288,7 +288,9 @@ window.Pattr = {
|
|
|
288
288
|
|
|
289
289
|
buildScopeData(el, parentData) {
|
|
290
290
|
let currentData = parentData;
|
|
291
|
-
|
|
291
|
+
// Check for p-scope attribute (with or without modifiers like p-scope:sync)
|
|
292
|
+
const pScopeAttr = Array.from(el.attributes).find(attr => attr.name.startsWith('p-scope'));
|
|
293
|
+
if (pScopeAttr) {
|
|
292
294
|
// Use explicit p-id if provided, otherwise generate from DOM position
|
|
293
295
|
const dataId = el.getAttribute('p-id') || this.generateDomPathId(el);
|
|
294
296
|
if (!parentData._p_children) {
|
|
@@ -298,7 +300,7 @@ window.Pattr = {
|
|
|
298
300
|
parentData._p_children[dataId] = {};
|
|
299
301
|
}
|
|
300
302
|
currentData = parentData._p_children[dataId];
|
|
301
|
-
currentData._p_scope = el.getAttribute(
|
|
303
|
+
currentData._p_scope = el.getAttribute(pScopeAttr.name);
|
|
302
304
|
// Store the ID for later use
|
|
303
305
|
currentData._p_id = dataId;
|
|
304
306
|
}
|
|
@@ -311,13 +313,17 @@ window.Pattr = {
|
|
|
311
313
|
|
|
312
314
|
// ==================== REACTIVE PROXY ====================
|
|
313
315
|
|
|
314
|
-
observe(data, parentScope) {
|
|
316
|
+
observe(data, parentScope, syncVars = []) {
|
|
315
317
|
const localTarget = data;
|
|
316
318
|
let proxyTarget = localTarget;
|
|
317
319
|
if (parentScope) {
|
|
318
320
|
proxyTarget = Object.create(parentScope._p_target || parentScope);
|
|
319
321
|
Object.assign(proxyTarget, localTarget);
|
|
320
322
|
}
|
|
323
|
+
|
|
324
|
+
const syncVarsSet = new Set(syncVars);
|
|
325
|
+
const parentTarget = parentScope?._p_target || parentScope;
|
|
326
|
+
|
|
321
327
|
const proxy = new Proxy(proxyTarget, {
|
|
322
328
|
get: (target, key) => {
|
|
323
329
|
if (key === '_p_target') {
|
|
@@ -327,6 +333,12 @@ window.Pattr = {
|
|
|
327
333
|
},
|
|
328
334
|
set: (target, key, value) => {
|
|
329
335
|
target[key] = value;
|
|
336
|
+
|
|
337
|
+
// If this is a synced variable, also update the parent scope
|
|
338
|
+
if (syncVarsSet.has(key) && parentTarget && key in parentTarget) {
|
|
339
|
+
parentTarget[key] = value;
|
|
340
|
+
}
|
|
341
|
+
|
|
330
342
|
this.walkDom(this.root, this.data, false);
|
|
331
343
|
return true;
|
|
332
344
|
},
|
|
@@ -339,18 +351,66 @@ window.Pattr = {
|
|
|
339
351
|
|
|
340
352
|
// ==================== SCOPE MANAGEMENT ====================
|
|
341
353
|
|
|
354
|
+
/**
|
|
355
|
+
* Parses p-scope expression to identify output variables (those being assigned)
|
|
356
|
+
*/
|
|
357
|
+
getPScopeOutputVars(pScopeExpr) {
|
|
358
|
+
const outputVars = [];
|
|
359
|
+
const statements = pScopeExpr.split(';').map(s => s.trim()).filter(s => s);
|
|
360
|
+
|
|
361
|
+
statements.forEach(stmt => {
|
|
362
|
+
// Array destructuring: [a, b] = expr
|
|
363
|
+
const arrayMatch = stmt.match(/^\[([^\]]+)\]\s*=\s*(.+)$/);
|
|
364
|
+
if (arrayMatch) {
|
|
365
|
+
const vars = arrayMatch[1].split(',').map(v => v.trim());
|
|
366
|
+
outputVars.push(...vars);
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Object destructuring: {a, b} = expr
|
|
371
|
+
const objMatch = stmt.match(/^\{([^}]+)\}\s*=\s*(.+)$/);
|
|
372
|
+
if (objMatch) {
|
|
373
|
+
const vars = objMatch[1].split(',').map(v => v.trim());
|
|
374
|
+
outputVars.push(...vars);
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Regular variable assignment: var = expr
|
|
379
|
+
const match = stmt.match(/^(\w+)\s*=\s*(.+)$/);
|
|
380
|
+
if (match) {
|
|
381
|
+
outputVars.push(match[1]);
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
return outputVars;
|
|
386
|
+
},
|
|
387
|
+
|
|
342
388
|
/**
|
|
343
389
|
* Creates a new scope for an element with p-scope during hydration
|
|
344
390
|
*/
|
|
345
391
|
initScope(el, parentScope) {
|
|
346
392
|
const dataId = el.getAttribute('p-id') || this.generateDomPathId(el);
|
|
347
393
|
const localRawData = parentScope._p_target._p_children[dataId];
|
|
394
|
+
const pScopeExpr = localRawData._p_scope;
|
|
395
|
+
|
|
396
|
+
// Check for :sync modifier
|
|
397
|
+
const pScopeAttr = Array.from(el.attributes).find(attr => attr.name.startsWith('p-scope'));
|
|
398
|
+
const parsed = this.parseDirectiveModifiers(pScopeAttr?.name || 'p-scope');
|
|
399
|
+
const isSync = parsed.modifiers.sync !== undefined;
|
|
400
|
+
|
|
401
|
+
// Get output variables for sync
|
|
402
|
+
const outputVars = isSync ? this.getPScopeOutputVars(pScopeExpr) : [];
|
|
348
403
|
|
|
349
|
-
// Create new inherited Proxy
|
|
350
|
-
const scope = this.observe(localRawData, parentScope);
|
|
404
|
+
// Create new inherited Proxy with sync configuration
|
|
405
|
+
const scope = this.observe(localRawData, parentScope, outputVars);
|
|
406
|
+
|
|
407
|
+
// Store sync info on element for later use
|
|
408
|
+
if (isSync) {
|
|
409
|
+
el._p_syncVars = new Set(outputVars);
|
|
410
|
+
}
|
|
351
411
|
|
|
352
412
|
// Execute p-scope assignments directly on target to avoid triggering setter
|
|
353
|
-
this.executePScopeStatements(scope,
|
|
413
|
+
this.executePScopeStatements(scope, pScopeExpr);
|
|
354
414
|
|
|
355
415
|
// Initialize parent snapshot so first refresh doesn't think everything changed
|
|
356
416
|
const parentProto = Object.getPrototypeOf(scope._p_target);
|
|
@@ -383,8 +443,11 @@ window.Pattr = {
|
|
|
383
443
|
return parentScope;
|
|
384
444
|
}
|
|
385
445
|
|
|
446
|
+
// Find the p-scope attribute (with or without modifiers)
|
|
447
|
+
const pScopeAttr = Array.from(el.attributes).find(attr => attr.name.startsWith('p-scope'));
|
|
448
|
+
const pScopeExpr = pScopeAttr ? el.getAttribute(pScopeAttr.name) : null;
|
|
449
|
+
|
|
386
450
|
// Check if parent values changed - if so, selectively re-execute p-scope
|
|
387
|
-
const pScopeExpr = el.getAttribute('p-scope');
|
|
388
451
|
if (pScopeExpr && scope._p_target) {
|
|
389
452
|
this.updateScopeFromParent(el, scope, pScopeExpr);
|
|
390
453
|
}
|
|
@@ -674,6 +737,7 @@ window.Pattr = {
|
|
|
674
737
|
const event = attr.name.replace('p-on:', '');
|
|
675
738
|
el.addEventListener(event, () => {
|
|
676
739
|
eval(`with (el._scope) { ${attr.value} }`);
|
|
740
|
+
this.walkDom(this.root, this.data, false);
|
|
677
741
|
this.refreshAllLoops();
|
|
678
742
|
});
|
|
679
743
|
}
|
|
@@ -799,8 +863,31 @@ window.Pattr = {
|
|
|
799
863
|
getTemplateScopePrefix(template) {
|
|
800
864
|
let ancestorKey = '';
|
|
801
865
|
|
|
802
|
-
// First, check if
|
|
803
|
-
//
|
|
866
|
+
// First, check if SSR elements exist after this template
|
|
867
|
+
// If so, extract the scope ID from them to maintain continuity
|
|
868
|
+
let nextSibling = template.nextElementSibling;
|
|
869
|
+
if (nextSibling && nextSibling.hasAttribute && nextSibling.hasAttribute('p-for-key')) {
|
|
870
|
+
const ssrKey = nextSibling.getAttribute('p-for-key');
|
|
871
|
+
// Extract scope prefix from SSR element (e.g., "s4:0" -> "s4:")
|
|
872
|
+
const colonIndex = ssrKey.lastIndexOf(':');
|
|
873
|
+
if (colonIndex > 0) {
|
|
874
|
+
const ssrScopePrefix = ssrKey.substring(0, colonIndex + 1);
|
|
875
|
+
// Extract just the scope ID part (e.g., "s4:" or "s3:0-s4:")
|
|
876
|
+
const lastDashIndex = ssrScopePrefix.lastIndexOf('-');
|
|
877
|
+
if (lastDashIndex >= 0) {
|
|
878
|
+
// Has ancestor prefix, extract both parts
|
|
879
|
+
ancestorKey = ssrScopePrefix.substring(0, lastDashIndex + 1);
|
|
880
|
+
const scopeId = ssrScopePrefix.substring(lastDashIndex + 1, ssrScopePrefix.length - 1);
|
|
881
|
+
template._forScopeId = scopeId;
|
|
882
|
+
} else {
|
|
883
|
+
// No ancestor prefix, just scope ID
|
|
884
|
+
template._forScopeId = ssrScopePrefix.substring(0, ssrScopePrefix.length - 1);
|
|
885
|
+
}
|
|
886
|
+
return ssrScopePrefix;
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
// Check if this template has _forTemplate set (meaning it was rendered by an outer loop)
|
|
804
891
|
if (template._forTemplate) {
|
|
805
892
|
const parentForData = template._forTemplate._forData;
|
|
806
893
|
if (parentForData && parentForData.scopePrefix) {
|
|
@@ -834,7 +921,7 @@ window.Pattr = {
|
|
|
834
921
|
}
|
|
835
922
|
}
|
|
836
923
|
|
|
837
|
-
//
|
|
924
|
+
// Check parent elements for p-for-key
|
|
838
925
|
if (!ancestorKey) {
|
|
839
926
|
let parent = template.parentElement;
|
|
840
927
|
while (parent) {
|
|
@@ -857,7 +944,7 @@ window.Pattr = {
|
|
|
857
944
|
}
|
|
858
945
|
}
|
|
859
946
|
|
|
860
|
-
// Generate a unique scope ID for this template
|
|
947
|
+
// Generate a unique scope ID for this template if not already set from SSR
|
|
861
948
|
if (!template._forScopeId) {
|
|
862
949
|
template._forScopeId = 's' + (this._templateScopeCounter++);
|
|
863
950
|
}
|
|
@@ -1149,7 +1236,9 @@ window.Pattr = {
|
|
|
1149
1236
|
// Determine the scope for this element
|
|
1150
1237
|
let currentScope = parentScope;
|
|
1151
1238
|
|
|
1152
|
-
|
|
1239
|
+
// Check for p-scope attribute (with or without modifiers like p-scope:sync)
|
|
1240
|
+
const hasPScope = Array.from(el.attributes).some(attr => attr.name.startsWith('p-scope'));
|
|
1241
|
+
if (hasPScope) {
|
|
1153
1242
|
if (isHydrating) {
|
|
1154
1243
|
currentScope = this.initScope(el, parentScope);
|
|
1155
1244
|
} else {
|
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 a=String(t);if(r.trim&&r.trim.length>0){const e=parseInt(r.trim[0])||100;a.length>e&&(a=a.substring(0,e)+"...")}e.innerText=a},"p-html":(e,t,r={})=>{let a=t;if(r.allow&&r.allow.length>0){const e=r.allow,t=document.createElement("div");t.innerHTML=a;const s=t=>{if(t.nodeType===Node.ELEMENT_NODE){const r=t.tagName.toLowerCase();if(!e.includes(r)){const e=document.createDocumentFragment();return Array.from(t.childNodes).forEach(t=>{const r=s(t);r&&e.appendChild(r)}),e}const a=t.cloneNode(!1);return Array.from(t.childNodes).forEach(e=>{const t=s(e);t&&a.appendChild(t)}),a}return t.cloneNode()},o=document.createElement("div");Array.from(t.childNodes).forEach(e=>{const t=s(e);t&&o.appendChild(t)}),a=o.innerHTML}if(r.trim&&r.trim.length>0){const e=parseInt(r.trim[0])||100,t=document.createElement("div");t.innerHTML=a;let s=0,o=!1;const n=t=>{if(o)return null;if(t.nodeType===Node.TEXT_NODE){const r=t.textContent,a=e-s;if(r.length<=a)return s+=r.length,t.cloneNode();{o=!0;const e=r.substring(0,a)+"...";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),o)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),o)break}a=i.innerHTML}e.innerHTML=a},"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,r={})=>{if(void 0!==r.replace)return void("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(" ")));let a=new Set,s=new Set;if("string"==typeof t&&t?t.split(" ").forEach(e=>{e&&(a.add(e),s.add(e))}):Array.isArray(t)?t.forEach(e=>{e&&(a.add(e),s.add(e))}):"object"==typeof t&&null!==t&&Object.keys(t).forEach(e=>{s.add(e),t[e]&&a.add(e)}),!e._p_staticClasses){const t=e.className?e.className.split(" ").filter(e=>e):[];e._p_staticClasses=t.filter(e=>!s.has(e))}e._p_dynamicClasses||(e._p_dynamicClasses=new Set),e._p_dynamicClasses=a;const o=[...e._p_staticClasses,...e._p_dynamicClasses];e.className=o.join(" ")},"p-model":(e,t)=>{Array.isArray(t)?e.value=t.join(", "):e.value=t},"p-attr":(e,t,r={})=>{const a=Object.keys(r);if(a.length>0){const r=a[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 a=t[r];null==a||!1===a?e.removeAttribute(r):e.setAttribute(r,String(a))})}},parseDirectiveModifiers(e){const t=e.split(":"),r=t[0],a={};for(let e=1;e<t.length;e++){const r=t[e].split("."),s=r[0],o=r.slice(1);a[s]=o}return{directive:r,modifiers:a}},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 a=Array.from(e.children).indexOf(r);t.unshift(`${r.tagName}[${a}]`)}r=e}return t.join("/")},buildScopeData(e,t){let r=t;if(e.hasAttribute("p-scope")){const a=e.getAttribute("p-id")||this.generateDomPathId(e);t._p_children||(t._p_children={}),t._p_children[a]||(t._p_children[a]={}),r=t._p_children[a],r._p_scope=e.getAttribute("p-scope"),r._p_id=a}let a=e.firstElementChild;for(;a;)this.buildScopeData(a,r),a=a.nextElementSibling},observe(e,t){const r=e;let a=r;t&&(a=Object.create(t._p_target||t),Object.assign(a,r));return new Proxy(a,{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),a=t._p_target._p_children[r],s=this.observe(a,t);this.executePScopeStatements(s,a._p_scope);const o=Object.getPrototypeOf(s._p_target);e._parentSnapshot={};for(let t in o)t.startsWith("_p_")||(e._parentSnapshot[t]=o[t]);const n=s._p_target;e._localSnapshot={};for(let t of Object.keys(n))t.startsWith("_p_")||(e._localSnapshot[t]=n[t]);return s},refreshScope(e,t){let r=e._scope;if(!r)return t;const a=e.getAttribute("p-scope");return a&&r._p_target&&this.updateScopeFromParent(e,r,a),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 arrayDestructMatch=stmt.match(/^\[([^\]]+)\]\s*=\s*(.+)$/);if(arrayDestructMatch){const[,vars,expr]=arrayDestructMatch,varNames=vars.split(",").map(e=>e.trim());try{const value=eval(`with (sequentialScope) { (${expr}) }`);Array.isArray(value)&&varNames.forEach((e,t)=>{target[e]=value[t]})}catch(e){console.error(`Error executing p-scope array destructuring "${stmt}":`,e)}continue}const objDestructMatch=stmt.match(/^\{([^}]+)\}\s*=\s*(.+)$/);if(objDestructMatch){const[,vars,expr]=objDestructMatch,varNames=vars.split(",").map(e=>e.trim());try{const value=eval(`with (sequentialScope) { (${expr}) }`);"object"==typeof value&&null!==value&&varNames.forEach(e=>{target[e]=value[e]})}catch(e){console.error(`Error executing p-scope object destructuring "${stmt}":`,e)}continue}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(/^\[([^\]]+)\]\s*=\s*(.+)$/);if(t){return void t[1].split(",").map(e=>e.trim()).forEach(e=>outputVars.add(e))}const r=e.match(/^\{([^}]+)\}\s*=\s*(.+)$/);if(r){return void r[1].split(",").map(e=>e.trim()).forEach(e=>outputVars.add(e))}const a=e.match(/^(\w+)\s*=\s*(.+)$/);a&&outputVars.add(a[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 arrayMatch=stmt.match(/^\[([^\]]+)\]\s*=\s*(.+)$/);if(arrayMatch){const[,vars,expr]=arrayMatch,varNames=vars.split(",").map(e=>e.trim()),value=eval(`with (sequentialScope) { (${expr}) }`);return void(Array.isArray(value)&&varNames.forEach((e,t)=>{target[e]=value[t],setInThisPass.add(e)}))}const objMatch=stmt.match(/^\{([^}]+)\}\s*=\s*(.+)$/);if(objMatch){const[,vars,expr]=objMatch,varNames=vars.split(",").map(e=>e.trim()),value=eval(`with (sequentialScope) { (${expr}) }`);return void("object"==typeof value&&null!==value&&varNames.forEach(e=>{target[e]=value[e],setInThisPass.add(e)}))}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;if("number"===type||"range"===type)value=""===e.target.value?null:Number(e.target.value);else if("checkbox"===type)value=e.target.checked;else if("radio"===type)value=e.target.checked?e.target.value:void 0;else{const currentValue=eval(`with (el._scope) { ${modelAttr} }`);value=Array.isArray(currentValue)?e.target.value.split(",").map(e=>e.trim()).filter(e=>e):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 a=e.getAttribute("p-for"),s=a.match(/^(?:const|let)?\s*(.+?)\s+(of|in)\s+(.+)$/);if(!s)return void console.error(`Invalid p-for expression: ${a}`);const[,o,n,i]=s;r?this.hydrateLoop(e,t,o,i):this.refreshLoop(e,t,o,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 a=e.renderedElements.indexOf(r);if(a>=0){t=(e.scopePrefix||"")+a+"-";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 a=document.createDocumentFragment();r.forEach(e=>a.appendChild(e)),lastInserted.parentNode.insertBefore(a,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),a=Array.from(t.children);a.forEach(e=>{e._scope=r,this.setForTemplateRecursive(e,template),"TEMPLATE"!==e.tagName&&e.setAttribute("p-for-key",scopePrefix+index)});const s=document.createDocumentFragment();a.forEach(e=>s.appendChild(e)),lastInsertedElement.parentNode.insertBefore(s,lastInsertedElement.nextSibling),forData.renderedElements.push(...a),a.forEach(e=>{this.walkDom(e,r,!0)});const o=a[a.length-1];o&&(lastInsertedElement=this.findLastRenderedSibling(o)),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 a={};if((t=t.trim()).startsWith("[")){const e=t.slice(1,-1).split(",").map(e=>e.trim());Array.isArray(r)?e.forEach((e,t)=>a[e]=r[t]):a[e[0]]=r}else if(t.startsWith("{")){t.slice(1,-1).split(",").map(e=>e.trim()).forEach(e=>{const[t,s]=e.split(":").map(e=>e.trim());a[s||t]=r[t]})}else a[t]=r;if(!e)return console.error("parentScope is undefined in createLoopScope"),new Proxy({},{get:()=>{},set:()=>!1});const s=e._p_target||e;if(!s||"object"!=typeof s)return console.error("Invalid parentTarget:",s),new Proxy(a,{get:(e,t)=>e[t],set:(e,t,r)=>(e[t]=r,!0)});const o=Object.create(s);Object.assign(o,a);const n=new Proxy(o,{get:(e,t)=>e[t],set:(e,t,r)=>(t in a?e[t]=r:s[t]=r,!0)});return n._p_target=o,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();
|
|
7
|
+
window.Pattr={_templateScopeCounter:0,directives:{"p-text":(e,t,r={})=>{let a=String(t);if(r.trim&&r.trim.length>0){const e=parseInt(r.trim[0])||100;a.length>e&&(a=a.substring(0,e)+"...")}e.innerText=a},"p-html":(e,t,r={})=>{let a=t;if(r.allow&&r.allow.length>0){const e=r.allow,t=document.createElement("div");t.innerHTML=a;const s=t=>{if(t.nodeType===Node.ELEMENT_NODE){const r=t.tagName.toLowerCase();if(!e.includes(r)){const e=document.createDocumentFragment();return Array.from(t.childNodes).forEach(t=>{const r=s(t);r&&e.appendChild(r)}),e}const a=t.cloneNode(!1);return Array.from(t.childNodes).forEach(e=>{const t=s(e);t&&a.appendChild(t)}),a}return t.cloneNode()},o=document.createElement("div");Array.from(t.childNodes).forEach(e=>{const t=s(e);t&&o.appendChild(t)}),a=o.innerHTML}if(r.trim&&r.trim.length>0){const e=parseInt(r.trim[0])||100,t=document.createElement("div");t.innerHTML=a;let s=0,o=!1;const n=t=>{if(o)return null;if(t.nodeType===Node.TEXT_NODE){const r=t.textContent,a=e-s;if(r.length<=a)return s+=r.length,t.cloneNode();{o=!0;const e=r.substring(0,a)+"...";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),o)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),o)break}a=i.innerHTML}e.innerHTML=a},"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,r={})=>{if(void 0!==r.replace)return void("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(" ")));let a=new Set,s=new Set;if("string"==typeof t&&t?t.split(" ").forEach(e=>{e&&(a.add(e),s.add(e))}):Array.isArray(t)?t.forEach(e=>{e&&(a.add(e),s.add(e))}):"object"==typeof t&&null!==t&&Object.keys(t).forEach(e=>{s.add(e),t[e]&&a.add(e)}),!e._p_staticClasses){const t=e.className?e.className.split(" ").filter(e=>e):[];e._p_staticClasses=t.filter(e=>!s.has(e))}e._p_dynamicClasses||(e._p_dynamicClasses=new Set),e._p_dynamicClasses=a;const o=[...e._p_staticClasses,...e._p_dynamicClasses];e.className=o.join(" ")},"p-model":(e,t)=>{Array.isArray(t)?e.value=t.join(", "):e.value=t},"p-attr":(e,t,r={})=>{const a=Object.keys(r);if(a.length>0){const r=a[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 a=t[r];null==a||!1===a?e.removeAttribute(r):e.setAttribute(r,String(a))})}},parseDirectiveModifiers(e){const t=e.split(":"),r=t[0],a={};for(let e=1;e<t.length;e++){const r=t[e].split("."),s=r[0],o=r.slice(1);a[s]=o}return{directive:r,modifiers:a}},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 a=Array.from(e.children).indexOf(r);t.unshift(`${r.tagName}[${a}]`)}r=e}return t.join("/")},buildScopeData(e,t){let r=t;const a=Array.from(e.attributes).find(e=>e.name.startsWith("p-scope"));if(a){const s=e.getAttribute("p-id")||this.generateDomPathId(e);t._p_children||(t._p_children={}),t._p_children[s]||(t._p_children[s]={}),r=t._p_children[s],r._p_scope=e.getAttribute(a.name),r._p_id=s}let s=e.firstElementChild;for(;s;)this.buildScopeData(s,r),s=s.nextElementSibling},observe(e,t,r=[]){const a=e;let s=a;t&&(s=Object.create(t._p_target||t),Object.assign(s,a));const o=new Set(r),n=t?._p_target||t;return new Proxy(s,{get:(e,t)=>"_p_target"===t?e:e[t],set:(e,t,r)=>(e[t]=r,o.has(t)&&n&&t in n&&(n[t]=r),this.walkDom(this.root,this.data,!1),!0),has:(e,t)=>t in e})},getPScopeOutputVars(e){const t=[];return e.split(";").map(e=>e.trim()).filter(e=>e).forEach(e=>{const r=e.match(/^\[([^\]]+)\]\s*=\s*(.+)$/);if(r){const e=r[1].split(",").map(e=>e.trim());return void t.push(...e)}const a=e.match(/^\{([^}]+)\}\s*=\s*(.+)$/);if(a){const e=a[1].split(",").map(e=>e.trim());return void t.push(...e)}const s=e.match(/^(\w+)\s*=\s*(.+)$/);s&&t.push(s[1])}),t},initScope(e,t){const r=e.getAttribute("p-id")||this.generateDomPathId(e),a=t._p_target._p_children[r],s=a._p_scope,o=Array.from(e.attributes).find(e=>e.name.startsWith("p-scope")),n=void 0!==this.parseDirectiveModifiers(o?.name||"p-scope").modifiers.sync,i=n?this.getPScopeOutputVars(s):[],c=this.observe(a,t,i);n&&(e._p_syncVars=new Set(i)),this.executePScopeStatements(c,s);const l=Object.getPrototypeOf(c._p_target);e._parentSnapshot={};for(let t in l)t.startsWith("_p_")||(e._parentSnapshot[t]=l[t]);const p=c._p_target;e._localSnapshot={};for(let t of Object.keys(p))t.startsWith("_p_")||(e._localSnapshot[t]=p[t]);return c},refreshScope(e,t){let r=e._scope;if(!r)return t;const a=Array.from(e.attributes).find(e=>e.name.startsWith("p-scope")),s=a?e.getAttribute(a.name):null;return s&&r._p_target&&this.updateScopeFromParent(e,r,s),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 arrayDestructMatch=stmt.match(/^\[([^\]]+)\]\s*=\s*(.+)$/);if(arrayDestructMatch){const[,vars,expr]=arrayDestructMatch,varNames=vars.split(",").map(e=>e.trim());try{const value=eval(`with (sequentialScope) { (${expr}) }`);Array.isArray(value)&&varNames.forEach((e,t)=>{target[e]=value[t]})}catch(e){console.error(`Error executing p-scope array destructuring "${stmt}":`,e)}continue}const objDestructMatch=stmt.match(/^\{([^}]+)\}\s*=\s*(.+)$/);if(objDestructMatch){const[,vars,expr]=objDestructMatch,varNames=vars.split(",").map(e=>e.trim());try{const value=eval(`with (sequentialScope) { (${expr}) }`);"object"==typeof value&&null!==value&&varNames.forEach(e=>{target[e]=value[e]})}catch(e){console.error(`Error executing p-scope object destructuring "${stmt}":`,e)}continue}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(/^\[([^\]]+)\]\s*=\s*(.+)$/);if(t){return void t[1].split(",").map(e=>e.trim()).forEach(e=>outputVars.add(e))}const r=e.match(/^\{([^}]+)\}\s*=\s*(.+)$/);if(r){return void r[1].split(",").map(e=>e.trim()).forEach(e=>outputVars.add(e))}const a=e.match(/^(\w+)\s*=\s*(.+)$/);a&&outputVars.add(a[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 arrayMatch=stmt.match(/^\[([^\]]+)\]\s*=\s*(.+)$/);if(arrayMatch){const[,vars,expr]=arrayMatch,varNames=vars.split(",").map(e=>e.trim()),value=eval(`with (sequentialScope) { (${expr}) }`);return void(Array.isArray(value)&&varNames.forEach((e,t)=>{target[e]=value[t],setInThisPass.add(e)}))}const objMatch=stmt.match(/^\{([^}]+)\}\s*=\s*(.+)$/);if(objMatch){const[,vars,expr]=objMatch,varNames=vars.split(",").map(e=>e.trim()),value=eval(`with (sequentialScope) { (${expr}) }`);return void("object"==typeof value&&null!==value&&varNames.forEach(e=>{target[e]=value[e],setInThisPass.add(e)}))}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.walkDom(this.root,this.data,!1),this.refreshAllLoops()})}})},registerModelBinding(el){const modelAttr=el.getAttribute("p-model");modelAttr&&(el.addEventListener("input",e=>{let value;const type=e.target.type;if("number"===type||"range"===type)value=""===e.target.value?null:Number(e.target.value);else if("checkbox"===type)value=e.target.checked;else if("radio"===type)value=e.target.checked?e.target.value:void 0;else{const currentValue=eval(`with (el._scope) { ${modelAttr} }`);value=Array.isArray(currentValue)?e.target.value.split(",").map(e=>e.trim()).filter(e=>e):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 a=e.getAttribute("p-for"),s=a.match(/^(?:const|let)?\s*(.+?)\s+(of|in)\s+(.+)$/);if(!s)return void console.error(`Invalid p-for expression: ${a}`);const[,o,n,i]=s;r?this.hydrateLoop(e,t,o,i):this.refreshLoop(e,t,o,i)},getTemplateScopePrefix(e){let t="",r=e.nextElementSibling;if(r&&r.hasAttribute&&r.hasAttribute("p-for-key")){const a=r.getAttribute("p-for-key"),s=a.lastIndexOf(":");if(s>0){const r=a.substring(0,s+1),o=r.lastIndexOf("-");if(o>=0){t=r.substring(0,o+1);const a=r.substring(o+1,r.length-1);e._forScopeId=a}else e._forScopeId=r.substring(0,r.length-1);return r}}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 a=e.renderedElements.indexOf(r);if(a>=0){t=(e.scopePrefix||"")+a+"-";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 a=document.createDocumentFragment();r.forEach(e=>a.appendChild(e)),lastInserted.parentNode.insertBefore(a,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),a=Array.from(t.children);a.forEach(e=>{e._scope=r,this.setForTemplateRecursive(e,template),"TEMPLATE"!==e.tagName&&e.setAttribute("p-for-key",scopePrefix+index)});const s=document.createDocumentFragment();a.forEach(e=>s.appendChild(e)),lastInsertedElement.parentNode.insertBefore(s,lastInsertedElement.nextSibling),forData.renderedElements.push(...a),a.forEach(e=>{this.walkDom(e,r,!0)});const o=a[a.length-1];o&&(lastInsertedElement=this.findLastRenderedSibling(o)),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 a={};if((t=t.trim()).startsWith("[")){const e=t.slice(1,-1).split(",").map(e=>e.trim());Array.isArray(r)?e.forEach((e,t)=>a[e]=r[t]):a[e[0]]=r}else if(t.startsWith("{")){t.slice(1,-1).split(",").map(e=>e.trim()).forEach(e=>{const[t,s]=e.split(":").map(e=>e.trim());a[s||t]=r[t]})}else a[t]=r;if(!e)return console.error("parentScope is undefined in createLoopScope"),new Proxy({},{get:()=>{},set:()=>!1});const s=e._p_target||e;if(!s||"object"!=typeof s)return console.error("Invalid parentTarget:",s),new Proxy(a,{get:(e,t)=>e[t],set:(e,t,r)=>(e[t]=r,!0)});const o=Object.create(s);Object.assign(o,a);const n=new Proxy(o,{get:(e,t)=>e[t],set:(e,t,r)=>(t in a?e[t]=r:s[t]=r,!0)});return n._p_target=o,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;const hasPScope=Array.from(el.attributes).some(e=>e.name.startsWith("p-scope"));hasPScope&&(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();
|