resonantjs 1.1.2 → 1.1.3
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/package.json +1 -1
- package/resonant.js +116 -46
- package/resonant.min.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "resonantjs",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.3",
|
|
4
4
|
"description": "A lightweight JavaScript framework that enables reactive data-binding for building dynamic and responsive web applications. It simplifies creating interactive UIs by automatically updating the DOM when your data changes.",
|
|
5
5
|
"main": "resonant.js",
|
|
6
6
|
"repository": {
|
package/resonant.js
CHANGED
|
@@ -13,6 +13,7 @@ class ObservableArray extends Array {
|
|
|
13
13
|
|
|
14
14
|
this.forEach((item, index) => {
|
|
15
15
|
if (typeof item === 'object') {
|
|
16
|
+
this._setKeyForObjectAndChildObjects(item, index);
|
|
16
17
|
this[index] = this._createProxy(item, index);
|
|
17
18
|
}
|
|
18
19
|
});
|
|
@@ -22,12 +23,53 @@ class ObservableArray extends Array {
|
|
|
22
23
|
}
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
_setKeyForObjectAndChildObjects(obj, index) {
|
|
27
|
+
if (typeof obj === 'object') {
|
|
28
|
+
obj.key = index + '-' + this._generateKeyByContentCheckSum(obj);
|
|
29
|
+
Object.keys(obj).forEach(key => {
|
|
30
|
+
this._setKeyForObjectAndChildObjects(obj[key], index);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
_generateKeyByContentCheckSum(obj) {
|
|
36
|
+
const removeKeysRecursively = (obj) => {
|
|
37
|
+
if (!obj || typeof obj !== 'object') return obj;
|
|
38
|
+
|
|
39
|
+
if (Array.isArray(obj)) {
|
|
40
|
+
return obj.map(item => removeKeysRecursively(item));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const newObj = {...obj};
|
|
44
|
+
delete newObj.key;
|
|
45
|
+
|
|
46
|
+
Object.keys(newObj).forEach(key => {
|
|
47
|
+
if (typeof newObj[key] === 'object' && newObj[key] !== null) {
|
|
48
|
+
newObj[key] = removeKeysRecursively(newObj[key]);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
return newObj;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const objForHash = removeKeysRecursively(obj);
|
|
56
|
+
const str = JSON.stringify(objForHash);
|
|
57
|
+
let hash = 0;
|
|
58
|
+
for (let i = 0; i < str.length; i++) {
|
|
59
|
+
const char = str.charCodeAt(i);
|
|
60
|
+
hash = ((hash << 5) - hash) + char;
|
|
61
|
+
hash = hash & hash;
|
|
62
|
+
}
|
|
63
|
+
return Math.abs(hash).toString(36);
|
|
64
|
+
}
|
|
65
|
+
|
|
25
66
|
_createProxy(item, index) {
|
|
26
67
|
return new Proxy(item, {
|
|
27
68
|
set: (target, property, value) => {
|
|
28
69
|
if (target[property] !== value) {
|
|
29
70
|
const oldValue = target[property];
|
|
30
71
|
target[property] = value;
|
|
72
|
+
this._setKeyForObjectAndChildObjects(item, index);
|
|
31
73
|
this.resonantInstance._queueUpdate(this.variableName, 'modified', target, property, oldValue, index);
|
|
32
74
|
}
|
|
33
75
|
return true;
|
|
@@ -36,6 +78,10 @@ class ObservableArray extends Array {
|
|
|
36
78
|
}
|
|
37
79
|
|
|
38
80
|
forceUpdate() {
|
|
81
|
+
this.forEach((item, index) => {
|
|
82
|
+
console.log('forceUpdate', item, index);
|
|
83
|
+
this._setKeyForObjectAndChildObjects(item, index);
|
|
84
|
+
});
|
|
39
85
|
this.resonantInstance.arrayDataChangeDetection[this.variableName] = this.slice();
|
|
40
86
|
this.resonantInstance._queueUpdate(this.variableName, 'modified', this.slice());
|
|
41
87
|
}
|
|
@@ -48,6 +94,7 @@ class ObservableArray extends Array {
|
|
|
48
94
|
push(...args) {
|
|
49
95
|
args = args.map((item, index) => {
|
|
50
96
|
if (typeof item === 'object') {
|
|
97
|
+
this._setKeyForObjectAndChildObjects(item, this.length + index);
|
|
51
98
|
return this._createProxy(item, this.length + index);
|
|
52
99
|
}
|
|
53
100
|
return item;
|
|
@@ -372,67 +419,91 @@ class Resonant {
|
|
|
372
419
|
}
|
|
373
420
|
|
|
374
421
|
_renderArray(variableName, el) {
|
|
375
|
-
|
|
376
|
-
el.innerHTML = '';
|
|
422
|
+
const container = el.hasAttribute('res') && el.parentElement ? el.parentElement : el;
|
|
377
423
|
|
|
424
|
+
let template;
|
|
378
425
|
if (!window[variableName + "_template"]) {
|
|
426
|
+
template = el.cloneNode(true);
|
|
379
427
|
window[variableName + "_template"] = template;
|
|
428
|
+
el.style.display = 'none';
|
|
429
|
+
el.setAttribute('res-template', 'true');
|
|
380
430
|
} else {
|
|
381
431
|
template = window[variableName + "_template"];
|
|
382
432
|
}
|
|
383
433
|
|
|
434
|
+
const existingElements = new Map();
|
|
435
|
+
container.querySelectorAll(`[res="${variableName}"][res-rendered="true"]`).forEach(element => {
|
|
436
|
+
const key = element.getAttribute('res-key');
|
|
437
|
+
if (key) {
|
|
438
|
+
existingElements.set(key, element);
|
|
439
|
+
}
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
container.querySelectorAll(`[res="${variableName}"][res-rendered="true"]`).forEach(el => el.remove());
|
|
443
|
+
|
|
384
444
|
this.data[variableName].forEach((instance, index) => {
|
|
385
|
-
const
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
this._queueUpdate(variableName, 'modified', instance, key, value);
|
|
407
|
-
}
|
|
408
|
-
);
|
|
409
|
-
}
|
|
410
|
-
else if (Array.isArray(value)) {
|
|
411
|
-
this._renderNestedArray(subEl, value);
|
|
445
|
+
const elementKey = instance.key;
|
|
446
|
+
let elementToUse;
|
|
447
|
+
|
|
448
|
+
if (existingElements.has(elementKey)) {
|
|
449
|
+
elementToUse = existingElements.get(elementKey);
|
|
450
|
+
existingElements.delete(elementKey);
|
|
451
|
+
elementToUse.setAttribute("res-index", index);
|
|
452
|
+
} else {
|
|
453
|
+
elementToUse = template.cloneNode(true);
|
|
454
|
+
elementToUse.removeAttribute('res-template');
|
|
455
|
+
elementToUse.style.display = '';
|
|
456
|
+
elementToUse.setAttribute("res-rendered", "true");
|
|
457
|
+
elementToUse.setAttribute("res-key", elementKey);
|
|
458
|
+
elementToUse.setAttribute("res-index", index);
|
|
459
|
+
|
|
460
|
+
for (let key in instance) {
|
|
461
|
+
let overrideInstanceValue = null;
|
|
462
|
+
let subEl = elementToUse.querySelector(`[res-prop="${key}"]`);
|
|
463
|
+
if (!subEl) {
|
|
464
|
+
subEl = elementToUse.querySelector('[res-prop=""]');
|
|
465
|
+
overrideInstanceValue = instance;
|
|
412
466
|
}
|
|
413
|
-
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
467
|
+
if (subEl) {
|
|
468
|
+
const value = this._resolveValue(instance, key, overrideInstanceValue);
|
|
469
|
+
|
|
470
|
+
if ((subEl.tagName === 'INPUT' || subEl.tagName === 'TEXTAREA') &&
|
|
471
|
+
!Array.isArray(value) &&
|
|
472
|
+
typeof value !== 'object') {
|
|
473
|
+
this._handleInputElement(
|
|
474
|
+
subEl,
|
|
475
|
+
value,
|
|
476
|
+
(newValue) => {
|
|
477
|
+
instance[key] = newValue;
|
|
478
|
+
this._queueUpdate(variableName, 'modified', instance, key, value);
|
|
479
|
+
}
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
else if (Array.isArray(value)) {
|
|
483
|
+
this._renderNestedArray(subEl, value);
|
|
484
|
+
}
|
|
485
|
+
else if (typeof value === 'object' && value !== null) {
|
|
486
|
+
const nestedElements = subEl.querySelectorAll('[res-prop]');
|
|
487
|
+
nestedElements.forEach(nestedEl => {
|
|
488
|
+
const nestedKey = nestedEl.getAttribute('res-prop');
|
|
489
|
+
if (nestedKey && nestedKey in value) {
|
|
490
|
+
this._renderObjectProperty(nestedEl, value[nestedKey], variableName, nestedKey);
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
else {
|
|
495
|
+
subEl.innerHTML = value ?? '';
|
|
496
|
+
}
|
|
424
497
|
}
|
|
425
498
|
}
|
|
426
499
|
}
|
|
427
500
|
|
|
428
|
-
this._handleDisplayElements(
|
|
429
|
-
this._bindClickEvents(
|
|
501
|
+
this._handleDisplayElements(elementToUse, instance);
|
|
502
|
+
this._bindClickEvents(elementToUse, instance, this.data[variableName]);
|
|
430
503
|
|
|
431
|
-
|
|
432
|
-
el.appendChild(clonedEl);
|
|
504
|
+
container.appendChild(elementToUse);
|
|
433
505
|
});
|
|
434
506
|
}
|
|
435
|
-
|
|
436
507
|
_renderNestedArray(subEl, arrayValue) {
|
|
437
508
|
const template = subEl.cloneNode(true);
|
|
438
509
|
subEl.innerHTML = '';
|
|
@@ -457,7 +528,6 @@ class Resonant {
|
|
|
457
528
|
subEl.appendChild(cloned);
|
|
458
529
|
});
|
|
459
530
|
}
|
|
460
|
-
|
|
461
531
|
updateDisplayConditionalsFor(variableName) {
|
|
462
532
|
const conditionalElements = document.querySelectorAll(`[res-display*="${variableName}"]`);
|
|
463
533
|
conditionalElements.forEach(conditionalElement => {
|
package/resonant.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
class ObservableArray extends Array{constructor(e,t,...a){if(void 0===t)return super(...a);super(...a);var s=void 0===t.data[e];this.variableName=e,this.resonantInstance=t,this.isDeleting=!1,this.forEach((e,t)=>{"object"==typeof e&&(this[t]=this._createProxy(e,t))}),s||this.forceUpdate()}_createProxy(e,t){return new Proxy(e,{set:(e,a,s)=>{if(e[a]!==s){let r=e[a];e[a]=s,this.resonantInstance._queueUpdate(this.variableName,"modified",e,a,r,t)}return!0}})}forceUpdate(){this.resonantInstance.arrayDataChangeDetection[this.variableName]=this.slice(),this.resonantInstance._queueUpdate(this.variableName,"modified",this.slice())}update(e){window[this.variableName]=e,this.resonantInstance._queueUpdate(this.variableName,"updated",e)}push(...e){e=e.map((e,t)=>"object"==typeof e?this._createProxy(e,this.length+t):e);let t=super.push(...e);return this.resonantInstance.arrayDataChangeDetection[this.variableName]=this.slice(),e.forEach((e,t)=>{this.resonantInstance._queueUpdate(this.variableName,"added",e,this.length-1-t)}),t}splice(e,t,...a){a=a.map((t,a)=>"object"==typeof t?this._createProxy(t,e+a):t);let s=super.splice(e,t,...a);return this.resonantInstance.arrayDataChangeDetection[this.variableName]=this.slice(),t>0&&s.forEach((t,a)=>{this.resonantInstance._queueUpdate(this.variableName,"removed",t,e+a)}),a.length>0&&a.forEach((t,a)=>{this.resonantInstance._queueUpdate(this.variableName,"added",t,e+a)}),s}set(e,t){if(this[e]!==t){if(this.isDeleting)return!0;let a=this.resonantInstance.arrayDataChangeDetection[this.variableName],s="modified";e>=a.length?s="added":void 0===a[e]&&(s="added"),this.resonantInstance.arrayDataChangeDetection[this.variableName]=this.slice();let r=this[e];this[e]=t,this.resonantInstance._queueUpdate(this.variableName,s,this[e],e,r)}return!0}delete(e){let t=this[e];return this.isDeleting=!0,this.splice(e,1),this.resonantInstance.arrayDataChangeDetection[this.variableName]=this.slice(),this.resonantInstance._queueUpdate(this.variableName,"removed",null,e,t),this.isDeleting=!1,!0}filter(e,t=!0){if(void 0===this.resonantInstance||!1===t)return super.filter(e);let a=super.filter(e);return this.resonantInstance.arrayDataChangeDetection[this.variableName]=this.slice(),this.resonantInstance._queueUpdate(this.variableName,"filtered"),a}}class Resonant{constructor(){this.data={},this.callbacks={},this.pendingUpdates=new Map,this.arrayDataChangeDetection={}}_handleInputElement(e,t,a){"checkbox"===e.type?(e.checked=t,e.hasAttribute("data-resonant-bound")||(e.onchange=()=>a(e.checked),e.setAttribute("data-resonant-bound","true"))):(e.value=t,e.hasAttribute("data-resonant-bound")||(e.oninput=()=>a(e.value),e.setAttribute("data-resonant-bound","true")))}add(e,t,a){Array.isArray(t=this.persist(e,t,a))?(this.data[e]=new ObservableArray(e,this,...t),this.arrayDataChangeDetection[e]=this.data[e].slice()):"object"==typeof t&&null!==t?this.data[e]=this._createObject(e,t):this.data[e]=t,this._defineProperty(e),this.updateElement(e)}persist(e,t,a){if(void 0===a||!a)return t;var s=localStorage.getItem("res_"+e);return null!=s?JSON.parse(s):(localStorage.setItem("res_"+e,JSON.stringify(t)),t)}updatePersistantData(e){localStorage.getItem("res_"+e)&&localStorage.setItem("res_"+e,JSON.stringify(this.data[e]))}addAll(e){Object.entries(e).forEach(([e,t])=>{this.add(e,t)})}_resolveValue(e,t,a=null){return a??e[t]}_createObject(e,t){return t[Symbol("isProxy")]=!0,new Proxy(t,{set:(t,a,s)=>{if(t[a]!==s){let r=t[a];t[a]=s,this._queueUpdate(e,"modified",t,a,r)}return!0}})}_evaluateDisplayCondition(e,t,a){try{let s=Function("item",`return ${a}`)(t);e.style.display=s?"":"none"}catch(r){console.error(`Error evaluating display condition: ${a}`,r)}}_handleDisplayElements(e,t){let a=e.querySelectorAll("[res-display]");a.forEach(e=>{let a=e.getAttribute("res-display")||"";this._evaluateDisplayCondition(e,t,a)})}_bindClickEvents(e,t,a){let s=e.querySelectorAll("[res-onclick], [res-onclick-remove]");s.forEach(e=>{let s=e.getAttribute("res-onclick"),r=e.getAttribute("res-onclick-remove");s&&(e.onclick=()=>{let e=Function("item",`return ${s}(item)`);e(t)}),r&&(e.onclick=()=>{if(a){let e=a.findIndex(e=>e[r]===t[r]);-1!==e&&a.splice(e,1)}})})}_defineProperty(e){Object.defineProperty(window,e,{get:()=>this.data[e],set:t=>{Array.isArray(t)?(this.data[e]=new ObservableArray(e,this,...t),this.arrayDataChangeDetection[e]=this.data[e].slice()):"object"==typeof t&&null!==t?this.data[e]=this._createObject(e,t):this.data[e]=t,this.updateElement(e),this.updateDisplayConditionalsFor(e),this.updateStylesFor(e),Array.isArray(t)||"object"==typeof t&&null!==t||this._queueUpdate(e,"modified",this.data[e])}})}_queueUpdate(e,t,a,s,r){this.pendingUpdates.has(e)||this.pendingUpdates.set(e,[]),this.pendingUpdates.get(e).push({action:t,item:a,property:s,oldValue:r}),1===this.pendingUpdates.get(e).length&&setTimeout(()=>{let t=this.pendingUpdates.get(e);this.updatePersistantData(e),this.pendingUpdates.delete(e),(t=t.filter((e,t,a)=>a.findIndex(t=>t.property===e.property&&t.action===e.action)===t)).forEach(t=>{this._triggerCallbacks(e,t)}),this.updateElement(e),this.updateDisplayConditionalsFor(e),this.updateStylesFor(e)},0)}_triggerCallbacks(e,t){this.callbacks[e]&&this.callbacks[e].forEach(a=>{let s=t.item||t.oldValue;a(this.data[e],s,t.action)})}updateElement(e){let t=document.querySelectorAll(`[res="${e}"]`),a=this.data[e];t.forEach(t=>{if("INPUT"===t.tagName||"TEXTAREA"===t.tagName)this._handleInputElement(t,a,t=>{this.data[e]=t,this._queueUpdate(e,"modified",this.data[e])});else if(Array.isArray(a))t.querySelectorAll(`[res="${e}"][res-rendered="true"]`).forEach(e=>e.remove()),this._renderArray(e,t);else if("object"==typeof a&&null!==a){let s=t.querySelectorAll("[res-prop]");s.forEach(t=>{let s=t.getAttribute("res-prop");s&&s in a&&this._renderObjectProperty(t,a[s],e,s)})}else t.innerHTML=a??""}),this.updateDisplayConditionalsFor(e),this.updateStylesFor(e)}_renderObjectProperty(e,t,a,s){if("INPUT"!==e.tagName&&"TEXTAREA"!==e.tagName||Array.isArray(t)||"object"==typeof t){if(Array.isArray(t))this._renderNestedArray(e,t);else if("object"==typeof t&&null!==t){let r=e.querySelectorAll("[res-prop]");r.forEach(e=>{let s=e.getAttribute("res-prop");s&&s in t&&this._renderObjectProperty(e,t[s],a,s)})}else e.innerHTML=t??""}else this._handleInputElement(e,t,e=>{window[a][s]=e})}_renderArray(e,t){let a=t.cloneNode(!0);t.innerHTML="",window[e+"_template"]?a=window[e+"_template"]:window[e+"_template"]=a,this.data[e].forEach((s,r)=>{let i=a.cloneNode(!0);for(let n in i.setAttribute("res-index",r),s){let l=null,o=i.querySelector(`[res-prop="${n}"]`);if(o||(o=i.querySelector('[res-prop=""]'),l=s),o){let h=this._resolveValue(s,n,l);if("INPUT"!==o.tagName&&"TEXTAREA"!==o.tagName||Array.isArray(h)||"object"==typeof h){if(Array.isArray(h))this._renderNestedArray(o,h);else if("object"==typeof h&&null!==h){let d=o.querySelectorAll("[res-prop]");d.forEach(t=>{let a=t.getAttribute("res-prop");a&&a in h&&this._renderObjectProperty(t,h[a],e,a)})}else o.innerHTML=h??""}else this._handleInputElement(o,h,t=>{s[n]=t,this._queueUpdate(e,"modified",s,n,h)})}}this._handleDisplayElements(i,s),this._bindClickEvents(i,s,this.data[e]),i.setAttribute("res-rendered","true"),t.appendChild(i)})}_renderNestedArray(e,t){let a=e.cloneNode(!0);e.innerHTML="",e.removeAttribute("res-prop"),t.forEach((s,r)=>{let i=a.cloneNode(!0);i.setAttribute("res-rendered","true");let n=i.querySelectorAll("[res-prop]");n.forEach(e=>{let t=e.getAttribute("res-prop");t&&t in s&&this._renderObjectProperty(e,s[t],null,t)}),this._handleDisplayElements(i,s),this._bindClickEvents(i,s,t),e.appendChild(i)})}updateDisplayConditionalsFor(e){let t=document.querySelectorAll(`[res-display*="${e}"]`);t.forEach(t=>{let a=t.getAttribute("res-display");this._evaluateDisplayCondition(t,this.data[e],a)})}updateStylesFor(variableName){let styleElements=document.querySelectorAll(`[res-style*="${variableName}"]`);styleElements.forEach(styleElement=>{let styleCondition=styleElement.getAttribute("res-style");try{let parent=styleElement,index=null;for(;parent&&!index;)index=parent.getAttribute("res-index"),parent=parent.parentElement;let resStyles=styleElement.getAttribute("res-styles");if(resStyles&&resStyles.split(" ").forEach(e=>{styleElement.classList.remove(e)}),null!==index){let item=this.data[variableName][index];styleCondition=styleCondition.replace(RegExp(`\\b${variableName}\\b`,"g"),"item");let styleClass=Function("item",`return ${styleCondition}`)(item);if(styleClass)styleElement.classList.add(styleClass);else{let elementHasStyle=styleElement.classList.contains(styleClass);elementHasStyle&&styleElement.classList.remove(styleClass)}}else{let styleClass=eval(styleCondition);if(styleClass)styleElement.classList.add(styleClass),styleElement.setAttribute("res-styles",styleClass);else{let elementHasStyle=styleElement.classList.contains(styleClass);elementHasStyle&&styleElement.classList.remove(styleClass)}}}catch(e){console.error(`Error evaluating style for ${variableName}: ${styleCondition}`,e)}})}addCallback(e,t){this.callbacks[e]||(this.callbacks[e]=[]),this.callbacks[e].push(t)}}
|
|
1
|
+
class ObservableArray extends Array{constructor(e,t,...r){if(void 0===t)return super(...r);super(...r);var s=void 0===t.data[e];this.variableName=e,this.resonantInstance=t,this.isDeleting=!1,this.forEach((e,t)=>{"object"==typeof e&&(this._setKeyForObjectAndChildObjects(e,t),this[t]=this._createProxy(e,t))}),s||this.forceUpdate()}_setKeyForObjectAndChildObjects(e,t){"object"==typeof e&&(e.key=t+"-"+this._generateKeyByContentCheckSum(e),Object.keys(e).forEach(r=>{this._setKeyForObjectAndChildObjects(e[r],t)}))}_generateKeyByContentCheckSum(e){let t=e=>{if(!e||"object"!=typeof e)return e;if(Array.isArray(e))return e.map(e=>t(e));let r={...e};return delete r.key,Object.keys(r).forEach(e=>{"object"==typeof r[e]&&null!==r[e]&&(r[e]=t(r[e]))}),r},r=t(e),s=JSON.stringify(r),a=0;for(let i=0;i<s.length;i++){let n=s.charCodeAt(i);a=(a<<5)-a+n,a&=a}return Math.abs(a).toString(36)}_createProxy(e,t){return new Proxy(e,{set:(r,s,a)=>{if(r[s]!==a){let i=r[s];r[s]=a,this._setKeyForObjectAndChildObjects(e,t),this.resonantInstance._queueUpdate(this.variableName,"modified",r,s,i,t)}return!0}})}forceUpdate(){this.forEach((e,t)=>{console.log("forceUpdate",e,t),this._setKeyForObjectAndChildObjects(e,t)}),this.resonantInstance.arrayDataChangeDetection[this.variableName]=this.slice(),this.resonantInstance._queueUpdate(this.variableName,"modified",this.slice())}update(e){window[this.variableName]=e,this.resonantInstance._queueUpdate(this.variableName,"updated",e)}push(...e){e=e.map((e,t)=>"object"==typeof e?(this._setKeyForObjectAndChildObjects(e,this.length+t),this._createProxy(e,this.length+t)):e);let t=super.push(...e);return this.resonantInstance.arrayDataChangeDetection[this.variableName]=this.slice(),e.forEach((e,t)=>{this.resonantInstance._queueUpdate(this.variableName,"added",e,this.length-1-t)}),t}splice(e,t,...r){r=r.map((t,r)=>"object"==typeof t?this._createProxy(t,e+r):t);let s=super.splice(e,t,...r);return this.resonantInstance.arrayDataChangeDetection[this.variableName]=this.slice(),t>0&&s.forEach((t,r)=>{this.resonantInstance._queueUpdate(this.variableName,"removed",t,e+r)}),r.length>0&&r.forEach((t,r)=>{this.resonantInstance._queueUpdate(this.variableName,"added",t,e+r)}),s}set(e,t){if(this[e]!==t){if(this.isDeleting)return!0;let r=this.resonantInstance.arrayDataChangeDetection[this.variableName],s="modified";e>=r.length?s="added":void 0===r[e]&&(s="added"),this.resonantInstance.arrayDataChangeDetection[this.variableName]=this.slice();let a=this[e];this[e]=t,this.resonantInstance._queueUpdate(this.variableName,s,this[e],e,a)}return!0}delete(e){let t=this[e];return this.isDeleting=!0,this.splice(e,1),this.resonantInstance.arrayDataChangeDetection[this.variableName]=this.slice(),this.resonantInstance._queueUpdate(this.variableName,"removed",null,e,t),this.isDeleting=!1,!0}filter(e,t=!0){if(void 0===this.resonantInstance||!1===t)return super.filter(e);let r=super.filter(e);return this.resonantInstance.arrayDataChangeDetection[this.variableName]=this.slice(),this.resonantInstance._queueUpdate(this.variableName,"filtered"),r}}class Resonant{constructor(){this.data={},this.callbacks={},this.pendingUpdates=new Map,this.arrayDataChangeDetection={}}_handleInputElement(e,t,r){"checkbox"===e.type?(e.checked=t,e.hasAttribute("data-resonant-bound")||(e.onchange=()=>r(e.checked),e.setAttribute("data-resonant-bound","true"))):(e.value=t,e.hasAttribute("data-resonant-bound")||(e.oninput=()=>r(e.value),e.setAttribute("data-resonant-bound","true")))}add(e,t,r){Array.isArray(t=this.persist(e,t,r))?(this.data[e]=new ObservableArray(e,this,...t),this.arrayDataChangeDetection[e]=this.data[e].slice()):"object"==typeof t&&null!==t?this.data[e]=this._createObject(e,t):this.data[e]=t,this._defineProperty(e),this.updateElement(e)}persist(e,t,r){if(void 0===r||!r)return t;var s=localStorage.getItem("res_"+e);return null!=s?JSON.parse(s):(localStorage.setItem("res_"+e,JSON.stringify(t)),t)}updatePersistantData(e){localStorage.getItem("res_"+e)&&localStorage.setItem("res_"+e,JSON.stringify(this.data[e]))}addAll(e){Object.entries(e).forEach(([e,t])=>{this.add(e,t)})}_resolveValue(e,t,r=null){return r??e[t]}_createObject(e,t){return t[Symbol("isProxy")]=!0,new Proxy(t,{set:(t,r,s)=>{if(t[r]!==s){let a=t[r];t[r]=s,this._queueUpdate(e,"modified",t,r,a)}return!0}})}_evaluateDisplayCondition(e,t,r){try{let s=Function("item",`return ${r}`)(t);e.style.display=s?"":"none"}catch(a){console.error(`Error evaluating display condition: ${r}`,a)}}_handleDisplayElements(e,t){let r=e.querySelectorAll("[res-display]");r.forEach(e=>{let r=e.getAttribute("res-display")||"";this._evaluateDisplayCondition(e,t,r)})}_bindClickEvents(e,t,r){let s=e.querySelectorAll("[res-onclick], [res-onclick-remove]");s.forEach(e=>{let s=e.getAttribute("res-onclick"),a=e.getAttribute("res-onclick-remove");s&&(e.onclick=()=>{let e=Function("item",`return ${s}(item)`);e(t)}),a&&(e.onclick=()=>{if(r){let e=r.findIndex(e=>e[a]===t[a]);-1!==e&&r.splice(e,1)}})})}_defineProperty(e){Object.defineProperty(window,e,{get:()=>this.data[e],set:t=>{Array.isArray(t)?(this.data[e]=new ObservableArray(e,this,...t),this.arrayDataChangeDetection[e]=this.data[e].slice()):"object"==typeof t&&null!==t?this.data[e]=this._createObject(e,t):this.data[e]=t,this.updateElement(e),this.updateDisplayConditionalsFor(e),this.updateStylesFor(e),Array.isArray(t)||"object"==typeof t&&null!==t||this._queueUpdate(e,"modified",this.data[e])}})}_queueUpdate(e,t,r,s,a){this.pendingUpdates.has(e)||this.pendingUpdates.set(e,[]),this.pendingUpdates.get(e).push({action:t,item:r,property:s,oldValue:a}),1===this.pendingUpdates.get(e).length&&setTimeout(()=>{let t=this.pendingUpdates.get(e);this.updatePersistantData(e),this.pendingUpdates.delete(e),(t=t.filter((e,t,r)=>r.findIndex(t=>t.property===e.property&&t.action===e.action)===t)).forEach(t=>{this._triggerCallbacks(e,t)}),this.updateElement(e),this.updateDisplayConditionalsFor(e),this.updateStylesFor(e)},0)}_triggerCallbacks(e,t){this.callbacks[e]&&this.callbacks[e].forEach(r=>{let s=t.item||t.oldValue;r(this.data[e],s,t.action)})}updateElement(e){let t=document.querySelectorAll(`[res="${e}"]`),r=this.data[e];t.forEach(t=>{if("INPUT"===t.tagName||"TEXTAREA"===t.tagName)this._handleInputElement(t,r,t=>{this.data[e]=t,this._queueUpdate(e,"modified",this.data[e])});else if(Array.isArray(r))t.querySelectorAll(`[res="${e}"][res-rendered="true"]`).forEach(e=>e.remove()),this._renderArray(e,t);else if("object"==typeof r&&null!==r){let s=t.querySelectorAll("[res-prop]");s.forEach(t=>{let s=t.getAttribute("res-prop");s&&s in r&&this._renderObjectProperty(t,r[s],e,s)})}else t.innerHTML=r??""}),this.updateDisplayConditionalsFor(e),this.updateStylesFor(e)}_renderObjectProperty(e,t,r,s){if("INPUT"!==e.tagName&&"TEXTAREA"!==e.tagName||Array.isArray(t)||"object"==typeof t){if(Array.isArray(t))this._renderNestedArray(e,t);else if("object"==typeof t&&null!==t){let a=e.querySelectorAll("[res-prop]");a.forEach(e=>{let s=e.getAttribute("res-prop");s&&s in t&&this._renderObjectProperty(e,t[s],r,s)})}else e.innerHTML=t??""}else this._handleInputElement(e,t,e=>{window[r][s]=e})}_renderArray(e,t){let r=t.hasAttribute("res")&&t.parentElement?t.parentElement:t,s;window[e+"_template"]?s=window[e+"_template"]:(s=t.cloneNode(!0),window[e+"_template"]=s,t.style.display="none",t.setAttribute("res-template","true"));let a=new Map;r.querySelectorAll(`[res="${e}"][res-rendered="true"]`).forEach(e=>{let t=e.getAttribute("res-key");t&&a.set(t,e)}),r.querySelectorAll(`[res="${e}"][res-rendered="true"]`).forEach(e=>e.remove()),this.data[e].forEach((t,i)=>{let n=t.key,l;if(a.has(n))l=a.get(n),a.delete(n),l.setAttribute("res-index",i);else for(let o in(l=s.cloneNode(!0)).removeAttribute("res-template"),l.style.display="",l.setAttribute("res-rendered","true"),l.setAttribute("res-key",n),l.setAttribute("res-index",i),t){let h=null,d=l.querySelector(`[res-prop="${o}"]`);if(d||(d=l.querySelector('[res-prop=""]'),h=t),d){let c=this._resolveValue(t,o,h);if("INPUT"!==d.tagName&&"TEXTAREA"!==d.tagName||Array.isArray(c)||"object"==typeof c){if(Array.isArray(c))this._renderNestedArray(d,c);else if("object"==typeof c&&null!==c){let u=d.querySelectorAll("[res-prop]");u.forEach(t=>{let r=t.getAttribute("res-prop");r&&r in c&&this._renderObjectProperty(t,c[r],e,r)})}else d.innerHTML=c??""}else this._handleInputElement(d,c,r=>{t[o]=r,this._queueUpdate(e,"modified",t,o,c)})}}this._handleDisplayElements(l,t),this._bindClickEvents(l,t,this.data[e]),r.appendChild(l)})}_renderNestedArray(e,t){let r=e.cloneNode(!0);e.innerHTML="",e.removeAttribute("res-prop"),t.forEach((s,a)=>{let i=r.cloneNode(!0);i.setAttribute("res-rendered","true");let n=i.querySelectorAll("[res-prop]");n.forEach(e=>{let t=e.getAttribute("res-prop");t&&t in s&&this._renderObjectProperty(e,s[t],null,t)}),this._handleDisplayElements(i,s),this._bindClickEvents(i,s,t),e.appendChild(i)})}updateDisplayConditionalsFor(e){let t=document.querySelectorAll(`[res-display*="${e}"]`);t.forEach(t=>{let r=t.getAttribute("res-display");this._evaluateDisplayCondition(t,this.data[e],r)})}updateStylesFor(variableName){let styleElements=document.querySelectorAll(`[res-style*="${variableName}"]`);styleElements.forEach(styleElement=>{let styleCondition=styleElement.getAttribute("res-style");try{let parent=styleElement,index=null;for(;parent&&!index;)index=parent.getAttribute("res-index"),parent=parent.parentElement;let resStyles=styleElement.getAttribute("res-styles");if(resStyles&&resStyles.split(" ").forEach(e=>{styleElement.classList.remove(e)}),null!==index){let item=this.data[variableName][index];styleCondition=styleCondition.replace(RegExp(`\\b${variableName}\\b`,"g"),"item");let styleClass=Function("item",`return ${styleCondition}`)(item);if(styleClass)styleElement.classList.add(styleClass);else{let elementHasStyle=styleElement.classList.contains(styleClass);elementHasStyle&&styleElement.classList.remove(styleClass)}}else{let styleClass=eval(styleCondition);if(styleClass)styleElement.classList.add(styleClass),styleElement.setAttribute("res-styles",styleClass);else{let elementHasStyle=styleElement.classList.contains(styleClass);elementHasStyle&&styleElement.classList.remove(styleClass)}}}catch(e){console.error(`Error evaluating style for ${variableName}: ${styleCondition}`,e)}})}addCallback(e,t){this.callbacks[e]||(this.callbacks[e]=[]),this.callbacks[e].push(t)}}
|