resonantjs 1.0.3 → 1.0.5
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/examples/example-taskmanager.html +4 -6
- package/package.json +1 -1
- package/resonant.js +148 -62
- package/resonant.min.js +1 -1
|
@@ -16,9 +16,6 @@
|
|
|
16
16
|
<div>
|
|
17
17
|
<h2>Add New Task</h2>
|
|
18
18
|
<input type="text" placeholder="Task Name" res="taskName" />
|
|
19
|
-
<p>
|
|
20
|
-
Name: <span res="taskName"></span>
|
|
21
|
-
</p>
|
|
22
19
|
<button onclick="addTask()">Add Task</button>
|
|
23
20
|
</div>
|
|
24
21
|
|
|
@@ -26,10 +23,10 @@
|
|
|
26
23
|
<div>
|
|
27
24
|
<h2>Task List</h2>
|
|
28
25
|
<ul res="tasks">
|
|
29
|
-
<li>
|
|
26
|
+
<li res-style="tasks.done ? 'done' : ''">
|
|
30
27
|
<input type="checkbox" res-prop="done" />
|
|
31
28
|
<span res-prop="name"></span>
|
|
32
|
-
<button res-onclick
|
|
29
|
+
<button res-onclick="remove">Remove</button>
|
|
33
30
|
</li>
|
|
34
31
|
</ul>
|
|
35
32
|
</div>
|
|
@@ -52,7 +49,8 @@
|
|
|
52
49
|
});
|
|
53
50
|
|
|
54
51
|
function remove(task) {
|
|
55
|
-
|
|
52
|
+
//get by name
|
|
53
|
+
const index = tasks.findIndex(t => t.name === task.name);
|
|
56
54
|
tasks.splice(index, 1);
|
|
57
55
|
|
|
58
56
|
//You could use as well, still trying to figure out if I want to leave this or not
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "resonantjs",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
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
|
@@ -1,12 +1,94 @@
|
|
|
1
|
+
class ObservableArray extends Array {
|
|
2
|
+
constructor(variableName, resonantInstance, ...args) {
|
|
3
|
+
super(...args);
|
|
4
|
+
this.variableName = variableName;
|
|
5
|
+
this.resonantInstance = resonantInstance;
|
|
6
|
+
this.isDeleting = false;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
push(...args) {
|
|
10
|
+
const result = super.push(...args);
|
|
11
|
+
this.resonantInstance.arrayDataChangeDetection[this.variableName] = this.slice();
|
|
12
|
+
args.forEach((item, index) => {
|
|
13
|
+
this.resonantInstance._queueUpdate(this.variableName, 'added', item, this.length - 1 - index);
|
|
14
|
+
});
|
|
15
|
+
return result;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
splice(start, deleteCount, ...items) {
|
|
19
|
+
const removedItems = super.splice(start, deleteCount, ...items);
|
|
20
|
+
this.resonantInstance.arrayDataChangeDetection[this.variableName] = this.slice();
|
|
21
|
+
|
|
22
|
+
if (deleteCount > 0) {
|
|
23
|
+
removedItems.forEach((item, index) => {
|
|
24
|
+
this.resonantInstance._queueUpdate(this.variableName, 'removed', item, start + index);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (items.length > 0) {
|
|
29
|
+
items.forEach((item, index) => {
|
|
30
|
+
this.resonantInstance._queueUpdate(this.variableName, 'added', item, start + index);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return removedItems;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
set(index, value) {
|
|
38
|
+
if (this[index] !== value) {
|
|
39
|
+
if (this.isDeleting) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const originalBeforeChange = this.resonantInstance.arrayDataChangeDetection[this.variableName];
|
|
44
|
+
let action = 'modified';
|
|
45
|
+
|
|
46
|
+
if (index >= originalBeforeChange.length) {
|
|
47
|
+
action = 'added';
|
|
48
|
+
} else if (originalBeforeChange[index] === undefined) {
|
|
49
|
+
action = 'added';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
this.resonantInstance.arrayDataChangeDetection[this.variableName] = this.slice();
|
|
53
|
+
|
|
54
|
+
const oldValue = this[index];
|
|
55
|
+
this[index] = value;
|
|
56
|
+
this.resonantInstance._queueUpdate(this.variableName, action, this[index], index, oldValue);
|
|
57
|
+
}
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
delete(index) {
|
|
62
|
+
const oldValue = this[index];
|
|
63
|
+
this.isDeleting = true;
|
|
64
|
+
this.splice(index, 1);
|
|
65
|
+
|
|
66
|
+
this.resonantInstance.arrayDataChangeDetection[this.variableName] = this.slice();
|
|
67
|
+
|
|
68
|
+
this.resonantInstance._queueUpdate(this.variableName, 'removed', null, index, oldValue);
|
|
69
|
+
this.isDeleting = false;
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
1
74
|
class Resonant {
|
|
2
75
|
constructor() {
|
|
3
76
|
this.data = {};
|
|
4
77
|
this.callbacks = {};
|
|
5
|
-
this.pendingUpdates = new
|
|
78
|
+
this.pendingUpdates = new Map();
|
|
79
|
+
this.arrayDataChangeDetection = {}; // Added to keep track of array state
|
|
6
80
|
}
|
|
7
81
|
|
|
8
82
|
add(variableName, value) {
|
|
9
|
-
|
|
83
|
+
if (Array.isArray(value)) {
|
|
84
|
+
this.data[variableName] = new ObservableArray(variableName, this, ...value);
|
|
85
|
+
this.arrayDataChangeDetection[variableName] = this.data[variableName].slice();
|
|
86
|
+
} else if (typeof value === 'object') {
|
|
87
|
+
this.data[variableName] = this._createObject(variableName, value);
|
|
88
|
+
} else {
|
|
89
|
+
this.data[variableName] = value;
|
|
90
|
+
}
|
|
91
|
+
|
|
10
92
|
this._defineProperty(variableName);
|
|
11
93
|
this.updateElement(variableName);
|
|
12
94
|
}
|
|
@@ -17,16 +99,6 @@ class Resonant {
|
|
|
17
99
|
});
|
|
18
100
|
}
|
|
19
101
|
|
|
20
|
-
_assignValueToData(variableName, value) {
|
|
21
|
-
if (Array.isArray(value)) {
|
|
22
|
-
this.data[variableName] = this._createArray(variableName, value);
|
|
23
|
-
} else if (typeof value === 'object') {
|
|
24
|
-
this.data[variableName] = this._createObject(variableName, value);
|
|
25
|
-
} else {
|
|
26
|
-
this.data[variableName] = value;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
102
|
_createObject(variableName, obj) {
|
|
31
103
|
obj[Symbol('isProxy')] = true;
|
|
32
104
|
return new Proxy(obj, {
|
|
@@ -41,39 +113,43 @@ class Resonant {
|
|
|
41
113
|
});
|
|
42
114
|
}
|
|
43
115
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (
|
|
49
|
-
|
|
116
|
+
_defineProperty(variableName) {
|
|
117
|
+
Object.defineProperty(window, variableName, {
|
|
118
|
+
get: () => this.data[variableName],
|
|
119
|
+
set: (newValue) => {
|
|
120
|
+
if (Array.isArray(newValue)) {
|
|
121
|
+
this.data[variableName] = new ObservableArray(variableName, this, ...newValue);
|
|
122
|
+
this.arrayDataChangeDetection[variableName] = this.data[variableName].slice(); // Create a copy for change detection
|
|
123
|
+
|
|
124
|
+
} else if (typeof newValue === 'object') {
|
|
125
|
+
this.data[variableName] = this._createObject(variableName, newValue);
|
|
126
|
+
} else {
|
|
127
|
+
this.data[variableName] = newValue;
|
|
50
128
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if (
|
|
55
|
-
|
|
56
|
-
const oldValue = target[index];
|
|
57
|
-
target[index] = value;
|
|
58
|
-
self._queueUpdate(variableName, action, target[index], index, oldValue);
|
|
129
|
+
this.updateElement(variableName);
|
|
130
|
+
this.updateConditionalsFor(variableName);
|
|
131
|
+
this.updateStylesFor(variableName);
|
|
132
|
+
if (!Array.isArray(newValue) && typeof newValue !== 'object') {
|
|
133
|
+
this._queueUpdate(variableName, 'modified', this.data[variableName]);
|
|
59
134
|
}
|
|
60
|
-
return true;
|
|
61
|
-
},
|
|
62
|
-
deleteProperty(target, index) {
|
|
63
|
-
const oldValue = target[index];
|
|
64
|
-
target.splice(index, 1);
|
|
65
|
-
self._queueUpdate(variableName, 'removed', oldValue, index);
|
|
66
|
-
return true;
|
|
67
135
|
}
|
|
68
136
|
});
|
|
69
137
|
}
|
|
70
138
|
|
|
71
139
|
_queueUpdate(variableName, action, item, property, oldValue) {
|
|
72
140
|
if (!this.pendingUpdates.has(variableName)) {
|
|
73
|
-
this.pendingUpdates.
|
|
141
|
+
this.pendingUpdates.set(variableName, []);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
this.pendingUpdates.get(variableName).push({ action, item, property, oldValue });
|
|
145
|
+
|
|
146
|
+
if (this.pendingUpdates.get(variableName).length === 1) {
|
|
74
147
|
setTimeout(() => {
|
|
148
|
+
const updates = this.pendingUpdates.get(variableName);
|
|
75
149
|
this.pendingUpdates.delete(variableName);
|
|
76
|
-
|
|
150
|
+
updates.forEach(update => {
|
|
151
|
+
this._triggerCallbacks(variableName, update);
|
|
152
|
+
});
|
|
77
153
|
this.updateElement(variableName);
|
|
78
154
|
this.updateConditionalsFor(variableName);
|
|
79
155
|
this.updateStylesFor(variableName);
|
|
@@ -81,35 +157,23 @@ class Resonant {
|
|
|
81
157
|
}
|
|
82
158
|
}
|
|
83
159
|
|
|
84
|
-
_triggerCallbacks(variableName,
|
|
160
|
+
_triggerCallbacks(variableName, callbackData) {
|
|
85
161
|
if (this.callbacks[variableName]) {
|
|
86
|
-
this.callbacks[variableName].forEach(callback =>
|
|
162
|
+
this.callbacks[variableName].forEach(callback => {
|
|
163
|
+
const item = callbackData.item || callbackData.oldValue;
|
|
164
|
+
callback(this.data[variableName], item, callbackData.action);
|
|
165
|
+
});
|
|
87
166
|
}
|
|
88
167
|
}
|
|
89
168
|
|
|
90
|
-
_defineProperty(variableName) {
|
|
91
|
-
Object.defineProperty(window, variableName, {
|
|
92
|
-
get: () => this.data[variableName],
|
|
93
|
-
set: (newValue) => {
|
|
94
|
-
this._assignValueToData(variableName, newValue);
|
|
95
|
-
this.updateElement(variableName);
|
|
96
|
-
this.updateConditionalsFor(variableName);
|
|
97
|
-
this.updateStylesFor(variableName);
|
|
98
|
-
if (!Array.isArray(newValue) && typeof newValue !== 'object') {
|
|
99
|
-
this._queueUpdate(variableName, 'modified', this.data[variableName]);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
|
|
105
169
|
updateElement(variableName) {
|
|
106
170
|
const elements = document.querySelectorAll(`[res="${variableName}"]`);
|
|
107
171
|
const value = this.data[variableName];
|
|
108
172
|
|
|
109
173
|
elements.forEach(element => {
|
|
174
|
+
element.value = value;
|
|
110
175
|
if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') {
|
|
111
176
|
if (!element.hasAttribute('data-resonant-bound')) {
|
|
112
|
-
element.value = value;
|
|
113
177
|
element.oninput = () => {
|
|
114
178
|
this.data[variableName] = element.value;
|
|
115
179
|
this._queueUpdate(variableName, 'modified', this.data[variableName]);
|
|
@@ -181,14 +245,40 @@ class Resonant {
|
|
|
181
245
|
|
|
182
246
|
updateStylesFor(variableName) {
|
|
183
247
|
const styleElements = document.querySelectorAll(`[res-style*="${variableName}"]`);
|
|
248
|
+
|
|
184
249
|
styleElements.forEach(styleElement => {
|
|
185
|
-
|
|
250
|
+
let styleCondition = styleElement.getAttribute('res-style');
|
|
186
251
|
try {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
252
|
+
let parent = styleElement;
|
|
253
|
+
let index = null;
|
|
254
|
+
while (parent && !index) {
|
|
255
|
+
index = parent.getAttribute('res-index');
|
|
256
|
+
parent = parent.parentElement;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (index !== null) {
|
|
260
|
+
const item = this.data[variableName][index];
|
|
261
|
+
styleCondition = styleCondition.replace(new RegExp(`\\b${variableName}\\b`, 'g'), 'item');
|
|
262
|
+
const styleClass = new Function('item', `return ${styleCondition}`)(item);
|
|
263
|
+
|
|
264
|
+
if (styleClass) {
|
|
265
|
+
styleElement.classList.add(styleClass);
|
|
266
|
+
} else {
|
|
267
|
+
var elementHasStyle = styleElement.classList.contains(styleClass);
|
|
268
|
+
if (elementHasStyle) {
|
|
269
|
+
styleElement.classList.remove(styleClass);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
190
272
|
} else {
|
|
191
|
-
|
|
273
|
+
const styleClass = eval(styleCondition);
|
|
274
|
+
if (styleClass) {
|
|
275
|
+
styleElement.classList.add(styleClass);
|
|
276
|
+
} else {
|
|
277
|
+
var elementHasStyle = styleElement.classList.contains(styleClass);
|
|
278
|
+
if (elementHasStyle) {
|
|
279
|
+
styleElement.classList.remove(styleClass);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
192
282
|
}
|
|
193
283
|
} catch (e) {
|
|
194
284
|
console.error(`Error evaluating style for ${variableName}: ${styleCondition}`, e);
|
|
@@ -251,8 +341,6 @@ class Resonant {
|
|
|
251
341
|
const removeKey = onclickEl.getAttribute('res-onclick-remove');
|
|
252
342
|
|
|
253
343
|
if (functionName) {
|
|
254
|
-
onclickEl.onclick = null;
|
|
255
|
-
|
|
256
344
|
onclickEl.onclick = () => {
|
|
257
345
|
const func = new Function('item', `return ${functionName}(item)`);
|
|
258
346
|
func(instance);
|
|
@@ -260,8 +348,6 @@ class Resonant {
|
|
|
260
348
|
}
|
|
261
349
|
|
|
262
350
|
if (removeKey) {
|
|
263
|
-
onclickEl.onclick = null;
|
|
264
|
-
|
|
265
351
|
onclickEl.onclick = () => {
|
|
266
352
|
const index = this.data[variableName].findIndex(t => t[removeKey] === instance[removeKey]);
|
|
267
353
|
if (index !== -1) {
|
package/resonant.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
class
|
|
1
|
+
class ObservableArray extends Array{constructor(e,t,...a){super(...a),this.variableName=e,this.resonantInstance=t,this.isDeleting=!1}push(...e){var 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(a,e,...t){var s=super.splice(a,e,...t);return this.resonantInstance.arrayDataChangeDetection[this.variableName]=this.slice(),0<e&&s.forEach((e,t)=>{this.resonantInstance._queueUpdate(this.variableName,"removed",e,a+t)}),0<t.length&&t.forEach((e,t)=>{this.resonantInstance._queueUpdate(this.variableName,"added",e,a+t)}),s}set(t,a){if(this[t]!==a){if(this.isDeleting)return!0;var s=this.resonantInstance.arrayDataChangeDetection[this.variableName];let e="modified";(t>=s.length||void 0===s[t])&&(e="added"),this.resonantInstance.arrayDataChangeDetection[this.variableName]=this.slice();s=this[t];this[t]=a,this.resonantInstance._queueUpdate(this.variableName,e,this[t],t,s)}return!0}delete(e){var 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)}}class Resonant{constructor(){this.data={},this.callbacks={},this.pendingUpdates=new Map,this.arrayDataChangeDetection={}}add(e,t){Array.isArray(t)?(this.data[e]=new ObservableArray(e,this,...t),this.arrayDataChangeDetection[e]=this.data[e].slice()):this.data[e]="object"==typeof t?this._createObject(e,t):t,this._defineProperty(e),this.updateElement(e)}addAll(e){Object.entries(e).forEach(([e,t])=>{this.add(e,t)})}_createObject(i,e){return e[Symbol("isProxy")]=!0,new Proxy(e,{set:(e,t,a)=>{var s;return e[t]!==a&&(s=e[t],e[t]=a,this._queueUpdate(i,"modified",e,t,s)),!0}})}_defineProperty(t){Object.defineProperty(window,t,{get:()=>this.data[t],set:e=>{Array.isArray(e)?(this.data[t]=new ObservableArray(t,this,...e),this.arrayDataChangeDetection[t]=this.data[t].slice()):this.data[t]="object"==typeof e?this._createObject(t,e):e,this.updateElement(t),this.updateConditionalsFor(t),this.updateStylesFor(t),Array.isArray(e)||"object"==typeof e||this._queueUpdate(t,"modified",this.data[t])}})}_queueUpdate(t,e,a,s,i){this.pendingUpdates.has(t)||this.pendingUpdates.set(t,[]),this.pendingUpdates.get(t).push({action:e,item:a,property:s,oldValue:i}),1===this.pendingUpdates.get(t).length&&setTimeout(()=>{var e=this.pendingUpdates.get(t);this.pendingUpdates.delete(t),e.forEach(e=>{this._triggerCallbacks(t,e)}),this.updateElement(t),this.updateConditionalsFor(t),this.updateStylesFor(t)},0)}_triggerCallbacks(a,s){this.callbacks[a]&&this.callbacks[a].forEach(e=>{var t=s.item||s.oldValue;e(this.data[a],t,s.action)})}updateElement(a){var e=document.querySelectorAll(`[res="${a}"]`);const s=this.data[a];e.forEach(e=>{e.value=s,"INPUT"===e.tagName||"TEXTAREA"===e.tagName?e.hasAttribute("data-resonant-bound")||(e.oninput=()=>{this.data[a]=e.value,this._queueUpdate(a,"modified",this.data[a])},e.setAttribute("data-resonant-bound","true")):Array.isArray(s)?(e.querySelectorAll(`[res="${a}"][res-rendered=true]`).forEach(e=>e.remove()),this._renderArray(a,e)):"object"==typeof s?e.querySelectorAll("[res-prop]").forEach(e=>{const t=e.getAttribute("res-prop");t&&t in s&&(e.hasAttribute("data-resonant-bound")?"INPUT"===e.tagName||"TEXTAREA"===e.tagName?"checkbox"===e.type?e.checked=s[t]:e.value=s[t]:e.innerHTML=s[t]:("INPUT"===e.tagName||"TEXTAREA"===e.tagName?"checkbox"===e.type?(e.checked=s[t],e.onchange=()=>{this.data[a][t]=e.checked}):(e.value=s[t],e.oninput=()=>{this.data[a][t]=e.value}):e.innerHTML=s[t],e.setAttribute("data-resonant-bound","true")))}):e.innerHTML=s}),this.updateConditionalsFor(a),this.updateStylesFor(a)}updateConditionalsFor(variableName){const conditionalElements=document.querySelectorAll(`[res-conditional*="${variableName}"]`);conditionalElements.forEach(conditionalElement=>{const condition=conditionalElement.getAttribute("res-conditional");try{eval(condition)?conditionalElement.style.display="":conditionalElement.style.display="none"}catch(e){}})}updateStylesFor(variableName){const 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;if(null!==index){const item=this.data[variableName][index],styleClass=(styleCondition=styleCondition.replace(new RegExp(`\\b${variableName}\\b`,"g"),"item"),new Function("item","return "+styleCondition)(item));var elementHasStyle;styleClass?styleElement.classList.add(styleClass):(elementHasStyle=styleElement.classList.contains(styleClass),elementHasStyle&&styleElement.classList.remove(styleClass))}else{const styleClass=eval(styleCondition);var elementHasStyle;styleClass?styleElement.classList.add(styleClass):(elementHasStyle=styleElement.classList.contains(styleClass),elementHasStyle&&styleElement.classList.remove(styleClass))}}catch(e){}})}_renderArray(i,n){let r=n.cloneNode(!0);n.innerHTML="",window[i+"_template"]?r=window[i+"_template"]:window[i+"_template"]=r,this.data[i].forEach((s,e)=>{var t=r.cloneNode(!0);t.setAttribute("res-index",e);for(let e in s){const a=t.querySelector(`[res-prop="${e}"]`);a&&(a.hasAttribute("data-resonant-bound")?"INPUT"===a.tagName||"TEXTAREA"===a.tagName?"checkbox"===a.type?a.checked=s[e]:a.value=s[e]:a.innerHTML=s[e]:("INPUT"===a.tagName||"TEXTAREA"===a.tagName?"checkbox"===a.type?(a.checked=s[e],a.onchange=()=>{s[e]=a.checked,this._queueUpdate(i,"modified",s,e,s[e])}):(a.value=s[e],a.oninput=()=>{s[e]=a.value,this._queueUpdate(i,"modified",s,e,s[e])}):a.innerHTML=s[e],a.setAttribute("data-resonant-bound","true")))}t.querySelectorAll("[res-onclick], [res-onclick-remove]").forEach(e=>{const t=e.getAttribute("res-onclick"),a=e.getAttribute("res-onclick-remove");t&&(e.onclick=()=>{new Function("item",`return ${t}(item)`)(s)}),a&&(e.onclick=()=>{var e=this.data[i].findIndex(e=>e[a]===s[a]);-1!==e&&this.data[i].splice(e,1)})}),t.setAttribute("res-rendered",!0),n.appendChild(t)})}addCallback(e,t){this.callbacks[e]||(this.callbacks[e]=[]),this.callbacks[e].push(t)}}
|