@plentico/pattr 0.0.5 → 0.0.7
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 +6 -5
- package/package.json +1 -1
- package/pattr.js +33 -4
- package/pattr.min.js +1 -1
package/README.md
CHANGED
|
@@ -72,7 +72,7 @@ For use with wrapped components (e.g., from Pico templating), evaluate visibilit
|
|
|
72
72
|
|
|
73
73
|
```html
|
|
74
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"
|
|
75
|
+
<div p-show:pre-scope="age > 10" p-scope="double = age * 2">
|
|
76
76
|
Your doubled age is: <span p-text="double"></span>
|
|
77
77
|
</div>
|
|
78
78
|
```
|
|
@@ -88,7 +88,6 @@ You can combine both for complex scenarios:
|
|
|
88
88
|
p-show:pre-scope="age > 10"
|
|
89
89
|
p-show="double > 30"
|
|
90
90
|
p-scope="double = age * 2"
|
|
91
|
-
p-id="two-show-attr-demo"
|
|
92
91
|
>
|
|
93
92
|
Your doubled age (<span p-text="double"></span>) is greater than 30
|
|
94
93
|
</div>
|
|
@@ -186,7 +185,7 @@ Available events: `click`, `focus`, `blur`, `input`, `change`, `submit`, `keydow
|
|
|
186
185
|
|
|
187
186
|
## Scoped Components
|
|
188
187
|
|
|
189
|
-
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`):
|
|
190
189
|
|
|
191
190
|
```html
|
|
192
191
|
<div>
|
|
@@ -198,13 +197,15 @@ Create nested components with isolated reactive state using `p-scope` and `p-id`
|
|
|
198
197
|
<div>Child count (×2): <span p-text="count"></span></div>
|
|
199
198
|
<button p-on:click="count++">+</button>
|
|
200
199
|
|
|
201
|
-
<section p-
|
|
200
|
+
<section p-scope="count = count + 1;">
|
|
202
201
|
<div>Grandchild count (+1): <span p-text="count"></span></div>
|
|
203
202
|
<button p-on:click="count++">+</button>
|
|
204
203
|
</section>
|
|
205
204
|
</section>
|
|
206
205
|
```
|
|
207
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
|
+
|
|
208
209
|
### Scoping Example
|
|
209
210
|
|
|
210
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.
|
|
@@ -419,7 +420,7 @@ import Pattr from '@plentico/pattr';
|
|
|
419
420
|
</template>
|
|
420
421
|
|
|
421
422
|
<!-- Scoped component -->
|
|
422
|
-
<section p-
|
|
423
|
+
<section p-scope="count = todos.length;">
|
|
423
424
|
<p>Total todos: <span p-text="count"></span></p>
|
|
424
425
|
</section>
|
|
425
426
|
</body>
|
package/package.json
CHANGED
package/pattr.js
CHANGED
|
@@ -134,7 +134,12 @@ window.Pattr = {
|
|
|
134
134
|
}
|
|
135
135
|
},
|
|
136
136
|
'p-model': (el, value) => {
|
|
137
|
-
|
|
137
|
+
// If value is an array, join with commas for display
|
|
138
|
+
if (Array.isArray(value)) {
|
|
139
|
+
el.value = value.join(', ');
|
|
140
|
+
} else {
|
|
141
|
+
el.value = value;
|
|
142
|
+
}
|
|
138
143
|
},
|
|
139
144
|
'p-attr': (el, value, modifiers = {}) => {
|
|
140
145
|
// Two usage modes:
|
|
@@ -203,10 +208,25 @@ window.Pattr = {
|
|
|
203
208
|
this.refreshAllLoops();
|
|
204
209
|
},
|
|
205
210
|
|
|
211
|
+
generateDomPathId(el) {
|
|
212
|
+
const path = [];
|
|
213
|
+
let node = el;
|
|
214
|
+
while (node && node.tagName !== 'HTML') {
|
|
215
|
+
const parent = node.parentElement;
|
|
216
|
+
if (parent) {
|
|
217
|
+
const index = Array.from(parent.children).indexOf(node);
|
|
218
|
+
path.unshift(`${node.tagName}[${index}]`);
|
|
219
|
+
}
|
|
220
|
+
node = parent;
|
|
221
|
+
}
|
|
222
|
+
return path.join('/');
|
|
223
|
+
},
|
|
224
|
+
|
|
206
225
|
buildScopeData(el, parentData) {
|
|
207
226
|
let currentData = parentData;
|
|
208
227
|
if (el.hasAttribute('p-scope')) {
|
|
209
|
-
|
|
228
|
+
// Use explicit p-id if provided, otherwise generate from DOM position
|
|
229
|
+
const dataId = el.getAttribute('p-id') || this.generateDomPathId(el);
|
|
210
230
|
if (!parentData._p_children) {
|
|
211
231
|
parentData._p_children = {};
|
|
212
232
|
}
|
|
@@ -215,6 +235,8 @@ window.Pattr = {
|
|
|
215
235
|
}
|
|
216
236
|
currentData = parentData._p_children[dataId];
|
|
217
237
|
currentData._p_scope = el.getAttribute('p-scope');
|
|
238
|
+
// Store the ID for later use
|
|
239
|
+
currentData._p_id = dataId;
|
|
218
240
|
}
|
|
219
241
|
let child = el.firstElementChild;
|
|
220
242
|
while (child) {
|
|
@@ -257,7 +279,7 @@ window.Pattr = {
|
|
|
257
279
|
* Creates a new scope for an element with p-scope during hydration
|
|
258
280
|
*/
|
|
259
281
|
initScope(el, parentScope) {
|
|
260
|
-
const dataId = el.getAttribute('p-id');
|
|
282
|
+
const dataId = el.getAttribute('p-id') || this.generateDomPathId(el);
|
|
261
283
|
const localRawData = parentScope._p_target._p_children[dataId];
|
|
262
284
|
|
|
263
285
|
// Create new inherited Proxy
|
|
@@ -527,7 +549,14 @@ window.Pattr = {
|
|
|
527
549
|
} else if (type === 'radio') {
|
|
528
550
|
value = e.target.checked ? e.target.value : undefined;
|
|
529
551
|
} else {
|
|
530
|
-
value
|
|
552
|
+
// Check if current value is an array
|
|
553
|
+
const currentValue = eval(`with (el._scope) { ${modelAttr} }`);
|
|
554
|
+
if (Array.isArray(currentValue)) {
|
|
555
|
+
// Split by comma and trim, filter out empty strings
|
|
556
|
+
value = e.target.value.split(',').map(s => s.trim()).filter(s => s);
|
|
557
|
+
} else {
|
|
558
|
+
value = e.target.value;
|
|
559
|
+
}
|
|
531
560
|
}
|
|
532
561
|
|
|
533
562
|
if (value !== undefined) {
|
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(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 o=t=>{if(t.nodeType===Node.ELEMENT_NODE){const r=t.tagName.toLowerCase();if(!e.includes(r))return document.createTextNode(t.textContent);const a=t.cloneNode(!1);return Array.from(t.childNodes).forEach(e=>{const t=o(e);t&&a.appendChild(t)}),a}return t.cloneNode()},s=document.createElement("div");Array.from(t.childNodes).forEach(e=>{const t=o(e);t&&s.appendChild(t)}),a=s.innerHTML}if(r.trim&&r.trim.length>0){const e=parseInt(r.trim[0])||100,t=document.createElement("div");t.innerHTML=a;let o=0,s=!1;const n=t=>{if(s)return null;if(t.nodeType===Node.TEXT_NODE){const r=t.textContent,a=e-o;if(r.length<=a)return o+=r.length,t.cloneNode();{s=!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),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}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)=>{"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)=>{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("."),o=r[0],s=r.slice(1);a[o]=s}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],o=this.observe(a,t);this.executePScopeStatements(o,a._p_scope);const s=Object.getPrototypeOf(o._p_target);e._parentSnapshot={};for(let t in s)t.startsWith("_p_")||(e._parentSnapshot[t]=s[t]);const n=o._p_target;e._localSnapshot={};for(let t of Object.keys(n))t.startsWith("_p_")||(e._localSnapshot[t]=n[t]);return o},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 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;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"),o=a.match(/^(?:const|let)?\s*(.+?)\s+(of|in)\s+(.+)$/);if(!o)return void console.error(`Invalid p-for expression: ${a}`);const[,s,n,i]=o;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 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 o=document.createDocumentFragment();a.forEach(e=>o.appendChild(e)),lastInsertedElement.parentNode.insertBefore(o,lastInsertedElement.nextSibling),forData.renderedElements.push(...a),a.forEach(e=>{this.walkDom(e,r,!0)});const s=a[a.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 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,o]=e.split(":").map(e=>e.trim());a[o||t]=r[t]})}else a[t]=r;if(!e)return console.error("parentScope is undefined in createLoopScope"),new Proxy({},{get:()=>{},set:()=>!1});const o=e._p_target||e;if(!o||"object"!=typeof o)return console.error("Invalid parentTarget:",o),new Proxy(a,{get:(e,t)=>e[t],set:(e,t,r)=>(e[t]=r,!0)});const s=Object.create(o);Object.assign(s,a);const n=new Proxy(s,{get:(e,t)=>e[t],set:(e,t,r)=>(t in a?e[t]=r:o[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();
|