resonantjs 1.1.3 → 1.1.4
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-basic.html +4 -2
- package/examples/tests.html +452 -0
- package/package.json +1 -1
- package/resonant.js +84 -17
- package/resonant.min.js +1 -1
|
@@ -102,8 +102,8 @@
|
|
|
102
102
|
<section class="grid">
|
|
103
103
|
<div>
|
|
104
104
|
<h2>Project Members</h2>
|
|
105
|
-
<ul
|
|
106
|
-
<li>
|
|
105
|
+
<ul>
|
|
106
|
+
<li res="projectTeam">
|
|
107
107
|
<span res-prop="name"></span> - <span res-prop="role"></span>
|
|
108
108
|
</li>
|
|
109
109
|
</ul>
|
|
@@ -207,6 +207,8 @@
|
|
|
207
207
|
]
|
|
208
208
|
});
|
|
209
209
|
|
|
210
|
+
|
|
211
|
+
|
|
210
212
|
// Chain together callbacks
|
|
211
213
|
resonantJs.addCallback("user", (user) => {
|
|
212
214
|
console.log(`User updated: ${user.firstname} ${user.lastname}`);
|
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<title>Resonant.js Practical Demo</title>
|
|
5
|
+
<link
|
|
6
|
+
rel="stylesheet"
|
|
7
|
+
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2.0.6/css/pico.min.css"
|
|
8
|
+
/>
|
|
9
|
+
<script src="../resonant.js"></script>
|
|
10
|
+
<style>
|
|
11
|
+
.success {
|
|
12
|
+
background-color: #0172ad;
|
|
13
|
+
}
|
|
14
|
+
.fail {
|
|
15
|
+
background-color: #883935;
|
|
16
|
+
}
|
|
17
|
+
section.grid {
|
|
18
|
+
padding: 1rem;
|
|
19
|
+
margin-bottom: 2rem;
|
|
20
|
+
border: 1px solid #e0e0e0;
|
|
21
|
+
border-radius: 0.5rem;
|
|
22
|
+
}
|
|
23
|
+
</style>
|
|
24
|
+
</head>
|
|
25
|
+
<body>
|
|
26
|
+
|
|
27
|
+
<main class="container">
|
|
28
|
+
<h1>Variable/Object/Array update tests</h1>
|
|
29
|
+
<section class="grid">
|
|
30
|
+
<div>
|
|
31
|
+
<h2>Basic Variable: <span res="basicVariable"></span></h2>
|
|
32
|
+
<div res-display="basicVariable == 'Initial'">
|
|
33
|
+
Has not been updated yet
|
|
34
|
+
</div>
|
|
35
|
+
<div res-display="basicVariable != 'Initial'">
|
|
36
|
+
Has been updated
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
<div>
|
|
40
|
+
<button class="runTest" data-type="basicVariable">Run Test</button>
|
|
41
|
+
</div>
|
|
42
|
+
</section>
|
|
43
|
+
<section class="grid">
|
|
44
|
+
<div res="simpleObject">
|
|
45
|
+
<h2>Simple Object: </h2>
|
|
46
|
+
First Name: <span res-prop="firstName"></span>
|
|
47
|
+
Last Name: <span res-prop="lastName"></span>
|
|
48
|
+
|
|
49
|
+
<div res-display="firstName == 'John'">
|
|
50
|
+
Has not been updated yet
|
|
51
|
+
</div>
|
|
52
|
+
<div res-display="firstName != 'John'">
|
|
53
|
+
Has been updated
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
<div>
|
|
57
|
+
<button class="runTest" data-type="simpleObject">Run Test</button>
|
|
58
|
+
</div>
|
|
59
|
+
</section>
|
|
60
|
+
<section class="grid">
|
|
61
|
+
<div>
|
|
62
|
+
<h2>Simple Array Update: </h2>
|
|
63
|
+
<ul>
|
|
64
|
+
<li res="simpleArrayUpdate">
|
|
65
|
+
<span res-prop="firstName"></span> <span res-prop="lastName"></span>
|
|
66
|
+
</li>
|
|
67
|
+
</ul>
|
|
68
|
+
|
|
69
|
+
<div res-display="simpleArrayUpdate[0].firstName == 'John'">
|
|
70
|
+
Has not been updated yet
|
|
71
|
+
</div>
|
|
72
|
+
<div res-display="simpleArrayUpdate[0].firstName != 'John'">
|
|
73
|
+
Has been updated
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
<div>
|
|
77
|
+
<button class="runTest" data-type="simpleArrayUpdate">Run Test</button>
|
|
78
|
+
</div>
|
|
79
|
+
</section>
|
|
80
|
+
<section class="grid">
|
|
81
|
+
<div>
|
|
82
|
+
<h2>Simple Array Add: </h2>
|
|
83
|
+
<ul>
|
|
84
|
+
<li res="simpleArrayAdd">
|
|
85
|
+
<span res-prop="firstName"></span> <span res-prop="lastName"></span>
|
|
86
|
+
</li>
|
|
87
|
+
</ul>
|
|
88
|
+
|
|
89
|
+
<div res-display="simpleArrayAdd[2] === undefined">
|
|
90
|
+
Has not been updated yet
|
|
91
|
+
</div>
|
|
92
|
+
<div res-display="simpleArrayAdd[2] !== undefined">
|
|
93
|
+
Has been updated
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
<div>
|
|
97
|
+
<button class="runTest" data-type="simpleArrayAdd">Run Test</button>
|
|
98
|
+
</div>
|
|
99
|
+
</section>
|
|
100
|
+
<section class="grid">
|
|
101
|
+
<div>
|
|
102
|
+
<h2>Simple Array Remove: </h2>
|
|
103
|
+
<ul>
|
|
104
|
+
<li res="simpleArrayRemove">
|
|
105
|
+
<span res-prop="firstName"></span> <span res-prop="lastName"></span>
|
|
106
|
+
</li>
|
|
107
|
+
</ul>
|
|
108
|
+
|
|
109
|
+
<div res-display="simpleArrayRemove[0].firstName == 'John'">
|
|
110
|
+
Has not been updated yet
|
|
111
|
+
</div>
|
|
112
|
+
<div res-display="simpleArrayRemove[0].firstName != 'John'">
|
|
113
|
+
Has been updated
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
<div>
|
|
117
|
+
<button class="runTest" data-type="simpleArrayRemove">Run Test</button>
|
|
118
|
+
</div>
|
|
119
|
+
</section>
|
|
120
|
+
<section class="grid">
|
|
121
|
+
<div>
|
|
122
|
+
<h2>Complex Array With Children Update: </h2>
|
|
123
|
+
<hr>
|
|
124
|
+
<div>
|
|
125
|
+
<div res="complexArrayWithChildrenUpdate">
|
|
126
|
+
<h3><span res-prop="firstName"></span> <span res-prop="lastName"></span></h3>
|
|
127
|
+
|
|
128
|
+
Phone Numbers:
|
|
129
|
+
<ul>
|
|
130
|
+
<li res-prop="phoneNumbers">
|
|
131
|
+
</li>
|
|
132
|
+
</ul>
|
|
133
|
+
Addresses
|
|
134
|
+
<ul>
|
|
135
|
+
<li res-prop="addresses">
|
|
136
|
+
<span res-prop="city"></span>,
|
|
137
|
+
<span res-prop="state"></span>
|
|
138
|
+
</li>
|
|
139
|
+
</ul>
|
|
140
|
+
<hr>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
<div res-display="complexArrayWithChildrenUpdate[0].firstName == 'John'">
|
|
146
|
+
Has not been updated yet
|
|
147
|
+
</div>
|
|
148
|
+
<div res-display="complexArrayWithChildrenUpdate[0].firstName != 'John'">
|
|
149
|
+
Has been updated
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
<div>
|
|
153
|
+
<button class="runTest" data-type="complexArrayWithChildrenUpdate">Run Test</button>
|
|
154
|
+
</div>
|
|
155
|
+
</section>
|
|
156
|
+
<section class="grid">
|
|
157
|
+
<div>
|
|
158
|
+
<h2>Complex Array With Children Display Update: </h2>
|
|
159
|
+
<hr>
|
|
160
|
+
<div>
|
|
161
|
+
<div res="complexArrayWithChildrenDisplayUpdate">
|
|
162
|
+
<h3><span res-prop="firstName"></span> <span res-prop="lastName"></span></h3>
|
|
163
|
+
|
|
164
|
+
<div res-display="attributes.showOnPage">
|
|
165
|
+
Show on page
|
|
166
|
+
</div>
|
|
167
|
+
<hr>
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
<div>
|
|
172
|
+
<button class="runTest" data-type="complexArrayWithChildrenDisplayUpdate">Run Test</button>
|
|
173
|
+
</div>
|
|
174
|
+
</section>
|
|
175
|
+
<section class="grid">
|
|
176
|
+
<div>
|
|
177
|
+
<h2>Complex Array With Arrays Display Test: </h2>
|
|
178
|
+
<hr>
|
|
179
|
+
<div>
|
|
180
|
+
<div res="complexArrayWithArrayDisplayUpdate">
|
|
181
|
+
<h3>
|
|
182
|
+
<span res-prop="firstName"></span>
|
|
183
|
+
<span res-prop="lastName"></span>
|
|
184
|
+
</h3>
|
|
185
|
+
|
|
186
|
+
<div res-prop="attributes">
|
|
187
|
+
<div res-prop="name">
|
|
188
|
+
</div>
|
|
189
|
+
<div res-display="showOnPage">
|
|
190
|
+
Show on page
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
<hr>
|
|
194
|
+
</div>
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
<div>
|
|
198
|
+
<button class="runTest" data-type="complexArrayWithArrayDisplayUpdate">Run Test</button>
|
|
199
|
+
</div>
|
|
200
|
+
</section>
|
|
201
|
+
<button onclick="runAllTests()">Run All Tests</button>
|
|
202
|
+
</main>
|
|
203
|
+
|
|
204
|
+
<script>
|
|
205
|
+
const resonantJs = new Resonant();
|
|
206
|
+
resonantJs.add("basicVariable", "Initial");
|
|
207
|
+
resonantJs.add("simpleObject", {
|
|
208
|
+
firstName: "John",
|
|
209
|
+
lastName: "Doe"
|
|
210
|
+
});
|
|
211
|
+
resonantJs.add("simpleArrayUpdate", [
|
|
212
|
+
{
|
|
213
|
+
firstName: "John",
|
|
214
|
+
lastName: "Doe"
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
firstName: "Jane",
|
|
218
|
+
lastName: "Doe"
|
|
219
|
+
}
|
|
220
|
+
]);
|
|
221
|
+
resonantJs.add("simpleArrayAdd", [
|
|
222
|
+
{
|
|
223
|
+
firstName: "John",
|
|
224
|
+
lastName: "Doe"
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
firstName: "Jane",
|
|
228
|
+
lastName: "Doe"
|
|
229
|
+
}
|
|
230
|
+
]);
|
|
231
|
+
resonantJs.add("simpleArrayRemove", [
|
|
232
|
+
{
|
|
233
|
+
firstName: "John",
|
|
234
|
+
lastName: "Doe"
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
firstName: "Jane",
|
|
238
|
+
lastName: "Doe"
|
|
239
|
+
}
|
|
240
|
+
]);
|
|
241
|
+
resonantJs.add("complexArrayWithChildrenUpdate", [
|
|
242
|
+
{
|
|
243
|
+
firstName: "Michael",
|
|
244
|
+
lastName: "Smith",
|
|
245
|
+
phoneNumbers: [
|
|
246
|
+
"555-123-4567",
|
|
247
|
+
"555-987-6543"
|
|
248
|
+
],
|
|
249
|
+
addresses: [
|
|
250
|
+
{
|
|
251
|
+
city: "Chicago",
|
|
252
|
+
state: "IL"
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
city: "Seattle",
|
|
256
|
+
state: "WA"
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
city: "New York",
|
|
260
|
+
state: "NY"
|
|
261
|
+
}
|
|
262
|
+
]
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
firstName: "Sarah",
|
|
266
|
+
lastName: "Johnson",
|
|
267
|
+
phoneNumbers: [
|
|
268
|
+
"555-234-5678",
|
|
269
|
+
"555-876-5432",
|
|
270
|
+
"555-345-6789"
|
|
271
|
+
],
|
|
272
|
+
addresses: [
|
|
273
|
+
{
|
|
274
|
+
city: "Boston",
|
|
275
|
+
state: "MA"
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
city: "Austin",
|
|
279
|
+
state: "TX"
|
|
280
|
+
}
|
|
281
|
+
]
|
|
282
|
+
}
|
|
283
|
+
]);
|
|
284
|
+
resonantJs.add("complexArrayWithChildrenDisplayUpdate", [
|
|
285
|
+
{
|
|
286
|
+
firstName: "Michael",
|
|
287
|
+
lastName: "Smith",
|
|
288
|
+
attributes:
|
|
289
|
+
{
|
|
290
|
+
showOnPage: true
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
firstName: "Sarah",
|
|
295
|
+
lastName: "Johnson",
|
|
296
|
+
attributes:
|
|
297
|
+
{
|
|
298
|
+
showOnPage: true
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
]);
|
|
302
|
+
resonantJs.add("complexArrayWithArrayDisplayUpdate", [
|
|
303
|
+
{
|
|
304
|
+
firstName: "Michael",
|
|
305
|
+
lastName: "Smith",
|
|
306
|
+
attributes: [
|
|
307
|
+
{
|
|
308
|
+
name: "Personal",
|
|
309
|
+
showOnPage: true
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
name: "Not Personal",
|
|
313
|
+
showOnPage: true
|
|
314
|
+
}]
|
|
315
|
+
}
|
|
316
|
+
]);
|
|
317
|
+
|
|
318
|
+
function runAllTests() {
|
|
319
|
+
let tests = document.querySelectorAll(".runTest");
|
|
320
|
+
tests.forEach(test => {
|
|
321
|
+
test.click();
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function runTest() {
|
|
326
|
+
|
|
327
|
+
//hide button
|
|
328
|
+
this.style.display = "none";
|
|
329
|
+
|
|
330
|
+
let parentElement = this.parentElement.parentElement;
|
|
331
|
+
let dataAttribute = this.getAttribute("data-type");
|
|
332
|
+
let expectedText = "";
|
|
333
|
+
|
|
334
|
+
console.log("Parent element inner text: ", parentElement.innerText);
|
|
335
|
+
|
|
336
|
+
if(dataAttribute === "basicVariable") {
|
|
337
|
+
basicVariable = "Updated";
|
|
338
|
+
|
|
339
|
+
expectedText = `Basic Variable: Updated
|
|
340
|
+
Has been updated`;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if(dataAttribute === "simpleObject") {
|
|
344
|
+
simpleObject.firstName = "Jane";
|
|
345
|
+
simpleObject.lastName = "Doe";
|
|
346
|
+
|
|
347
|
+
expectedText = `Simple Object:
|
|
348
|
+
First Name: Jane
|
|
349
|
+
Last Name: Doe
|
|
350
|
+
Has been updated`;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if(dataAttribute === "simpleArrayUpdate") {
|
|
354
|
+
simpleArrayUpdate[0].firstName = "Josh";
|
|
355
|
+
simpleArrayUpdate[0].lastName = "Forger";
|
|
356
|
+
simpleArrayUpdate[1].firstName = "Matilda";
|
|
357
|
+
simpleArrayUpdate[1].lastName = "Swinson";
|
|
358
|
+
|
|
359
|
+
expectedText = `Simple Array Update:
|
|
360
|
+
Josh Forger
|
|
361
|
+
Matilda Swinson
|
|
362
|
+
Has been updated`;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if(dataAttribute === "simpleArrayAdd") {
|
|
366
|
+
simpleArrayAdd.push({
|
|
367
|
+
firstName: "Josh",
|
|
368
|
+
lastName: "Forger"
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
expectedText = `Simple Array Add:
|
|
372
|
+
John Doe
|
|
373
|
+
Jane Doe
|
|
374
|
+
Josh Forger
|
|
375
|
+
Has been updated`;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if(dataAttribute === "simpleArrayRemove") {
|
|
379
|
+
simpleArrayRemove.splice(0, 1);
|
|
380
|
+
|
|
381
|
+
expectedText = `Simple Array Remove:
|
|
382
|
+
Jane Doe
|
|
383
|
+
Has been updated`;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if(dataAttribute === "complexArrayWithChildrenUpdate") {
|
|
387
|
+
complexArrayWithChildrenUpdate[0].firstName = "Josh";
|
|
388
|
+
complexArrayWithChildrenUpdate[0].lastName = "Forger";
|
|
389
|
+
complexArrayWithChildrenUpdate[0].phoneNumbers[0] = "123-123-1234";
|
|
390
|
+
complexArrayWithChildrenUpdate[0].addresses[1].city = "Hudson";
|
|
391
|
+
complexArrayWithChildrenUpdate[0].addresses[1].state = "OH";
|
|
392
|
+
|
|
393
|
+
expectedText = `Complex Array With Children Update:
|
|
394
|
+
Josh Forger
|
|
395
|
+
Phone Numbers:
|
|
396
|
+
123-123-1234
|
|
397
|
+
555-987-6543
|
|
398
|
+
Addresses
|
|
399
|
+
Chicago, IL
|
|
400
|
+
Hudson, OH
|
|
401
|
+
New York, NY
|
|
402
|
+
Sarah Johnson
|
|
403
|
+
Phone Numbers:
|
|
404
|
+
555-234-5678
|
|
405
|
+
555-876-5432
|
|
406
|
+
555-345-6789
|
|
407
|
+
Addresses
|
|
408
|
+
Boston, MA
|
|
409
|
+
Austin, TX
|
|
410
|
+
Has been updated
|
|
411
|
+
`;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
if(dataAttribute === "complexArrayWithChildrenDisplayUpdate") {
|
|
415
|
+
complexArrayWithChildrenDisplayUpdate[0].attributes.showOnPage = false;
|
|
416
|
+
// requires a force update unfortunately
|
|
417
|
+
complexArrayWithChildrenDisplayUpdate.forceUpdate();
|
|
418
|
+
|
|
419
|
+
expectedText = `Complex Array With Children Display Update:
|
|
420
|
+
Michael Smith
|
|
421
|
+
Sarah Johnson
|
|
422
|
+
Show on page
|
|
423
|
+
`;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
setTimeout(() => {
|
|
427
|
+
visibleTextCompare(parentElement,expectedText);
|
|
428
|
+
}, 1000)
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
document.querySelectorAll(".runTest").forEach(test => {
|
|
432
|
+
test.addEventListener("click", runTest);
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
function visibleTextCompare(element, expectedText) {
|
|
436
|
+
let visibleText = element.innerText;
|
|
437
|
+
|
|
438
|
+
visibleText = visibleText.replace(/\s/g, "").replace("RunTest", "");
|
|
439
|
+
expectedText = expectedText.replace(/\s/g, "").replace("RunTest", "");
|
|
440
|
+
|
|
441
|
+
let result = visibleText === expectedText;
|
|
442
|
+
|
|
443
|
+
if(!result) {
|
|
444
|
+
console.log("Visible text: ", visibleText);
|
|
445
|
+
console.log("Expected text: ", expectedText);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
element.classList.add(result ? "success" : "fail");
|
|
449
|
+
}
|
|
450
|
+
</script>
|
|
451
|
+
</body>
|
|
452
|
+
</html>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "resonantjs",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.4",
|
|
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
|
@@ -79,7 +79,6 @@ class ObservableArray extends Array {
|
|
|
79
79
|
|
|
80
80
|
forceUpdate() {
|
|
81
81
|
this.forEach((item, index) => {
|
|
82
|
-
console.log('forceUpdate', item, index);
|
|
83
82
|
this._setKeyForObjectAndChildObjects(item, index);
|
|
84
83
|
});
|
|
85
84
|
this.resonantInstance.arrayDataChangeDetection[this.variableName] = this.slice();
|
|
@@ -259,10 +258,60 @@ class Resonant {
|
|
|
259
258
|
});
|
|
260
259
|
}
|
|
261
260
|
|
|
262
|
-
_evaluateDisplayCondition(element,
|
|
261
|
+
_evaluateDisplayCondition(element, instance, condition) {
|
|
263
262
|
try {
|
|
264
|
-
|
|
265
|
-
|
|
263
|
+
let parent = element.parentElement;
|
|
264
|
+
let parentResName = null;
|
|
265
|
+
let arrayIndex = null;
|
|
266
|
+
|
|
267
|
+
while (parent && !parentResName) {
|
|
268
|
+
parentResName = parent.getAttribute('res');
|
|
269
|
+
if (!arrayIndex) {
|
|
270
|
+
arrayIndex = parent.getAttribute('res-index');
|
|
271
|
+
}
|
|
272
|
+
parent = parent.parentElement;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// For array items, use the specific array element as instance
|
|
276
|
+
if (parentResName && arrayIndex !== null && Array.isArray(this.data[parentResName])) {
|
|
277
|
+
instance = this.data[parentResName][arrayIndex];
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// For object properties without parent reference
|
|
281
|
+
if (parentResName && !condition.includes(parentResName + '.')) {
|
|
282
|
+
const propNames = condition.match(/\b[a-zA-Z_][a-zA-Z0-9_]*\b/g) || [];
|
|
283
|
+
propNames.forEach(prop => {
|
|
284
|
+
if (prop === '==' || prop === '!=' || !(prop in instance)) return;
|
|
285
|
+
|
|
286
|
+
const regex = new RegExp(`\\b${prop}\\b`, 'g');
|
|
287
|
+
const propValue = instance[prop];
|
|
288
|
+
|
|
289
|
+
if (propValue === undefined) {
|
|
290
|
+
// Keep as is - property doesn't exist
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Determine correct property access path
|
|
295
|
+
if (instance['item.' + prop] !== undefined) {
|
|
296
|
+
condition = condition.replace(regex, `item.${prop}`);
|
|
297
|
+
} else if (typeof propValue === 'object') {
|
|
298
|
+
condition = condition.replace(regex, `${parentResName}.${prop}`);
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
let show = false;
|
|
304
|
+
try{
|
|
305
|
+
show = new Function('item', `return ${condition}`)(instance);
|
|
306
|
+
} catch(e) {
|
|
307
|
+
|
|
308
|
+
const firstPeriodIndex = condition.indexOf('.');
|
|
309
|
+
const conditionAfterFirstPeriod = condition.substring(firstPeriodIndex + 1);
|
|
310
|
+
show = new Function('item', `return item.${conditionAfterFirstPeriod}`)(instance);
|
|
311
|
+
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
element.style.display = show ? 'inherit' : 'none';
|
|
266
315
|
} catch (e) {
|
|
267
316
|
console.error(`Error evaluating display condition: ${condition}`, e);
|
|
268
317
|
}
|
|
@@ -361,9 +410,9 @@ class Resonant {
|
|
|
361
410
|
}
|
|
362
411
|
|
|
363
412
|
updateElement(variableName) {
|
|
413
|
+
|
|
364
414
|
const elements = document.querySelectorAll(`[res="${variableName}"]`);
|
|
365
415
|
const value = this.data[variableName];
|
|
366
|
-
|
|
367
416
|
elements.forEach(element => {
|
|
368
417
|
if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') {
|
|
369
418
|
this._handleInputElement(element, value, (newValue) => {
|
|
@@ -402,7 +451,8 @@ class Resonant {
|
|
|
402
451
|
});
|
|
403
452
|
}
|
|
404
453
|
else if (Array.isArray(propValue)) {
|
|
405
|
-
this.
|
|
454
|
+
let parentKey = this.data[parentVarName].key;
|
|
455
|
+
this._renderNestedArray(subEl, propValue, parentKey);
|
|
406
456
|
}
|
|
407
457
|
else if (typeof propValue === 'object' && propValue !== null) {
|
|
408
458
|
const nestedElements = subEl.querySelectorAll('[res-prop]');
|
|
@@ -480,7 +530,8 @@ class Resonant {
|
|
|
480
530
|
);
|
|
481
531
|
}
|
|
482
532
|
else if (Array.isArray(value)) {
|
|
483
|
-
this.
|
|
533
|
+
let parentKey = this.data[variableName][index].key;
|
|
534
|
+
this._renderNestedArray(subEl, value, parentKey);
|
|
484
535
|
}
|
|
485
536
|
else if (typeof value === 'object' && value !== null) {
|
|
486
537
|
const nestedElements = subEl.querySelectorAll('[res-prop]');
|
|
@@ -504,7 +555,7 @@ class Resonant {
|
|
|
504
555
|
container.appendChild(elementToUse);
|
|
505
556
|
});
|
|
506
557
|
}
|
|
507
|
-
_renderNestedArray(subEl, arrayValue) {
|
|
558
|
+
_renderNestedArray(subEl, arrayValue, parentKey) {
|
|
508
559
|
const template = subEl.cloneNode(true);
|
|
509
560
|
subEl.innerHTML = '';
|
|
510
561
|
|
|
@@ -514,26 +565,42 @@ class Resonant {
|
|
|
514
565
|
const cloned = template.cloneNode(true);
|
|
515
566
|
cloned.setAttribute('res-rendered', 'true');
|
|
516
567
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
568
|
+
if (item !== null && typeof item !== 'object') {
|
|
569
|
+
cloned.innerHTML = item;
|
|
570
|
+
} else {
|
|
571
|
+
const nestedEls = cloned.querySelectorAll('[res-prop]');
|
|
572
|
+
nestedEls.forEach(nestedEl => {
|
|
573
|
+
const nestedKey = nestedEl.getAttribute('res-prop');
|
|
574
|
+
if (nestedKey && nestedKey in item) {
|
|
575
|
+
nestedEl.setAttribute('res-child', item.key + '-' + nestedKey + '--' + parentKey);
|
|
576
|
+
this._renderObjectProperty(nestedEl, item[nestedKey], null, nestedKey);
|
|
577
|
+
}
|
|
578
|
+
});
|
|
524
579
|
|
|
525
|
-
|
|
526
|
-
|
|
580
|
+
this._handleDisplayElements(cloned, item);
|
|
581
|
+
this._bindClickEvents(cloned, item, arrayValue);
|
|
527
582
|
|
|
583
|
+
const displayCondition = cloned.getAttribute('res-display');
|
|
584
|
+
if (displayCondition) {
|
|
585
|
+
this._evaluateDisplayCondition(cloned, item, displayCondition);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
528
588
|
subEl.appendChild(cloned);
|
|
529
589
|
});
|
|
530
590
|
}
|
|
591
|
+
|
|
531
592
|
updateDisplayConditionalsFor(variableName) {
|
|
532
593
|
const conditionalElements = document.querySelectorAll(`[res-display*="${variableName}"]`);
|
|
533
594
|
conditionalElements.forEach(conditionalElement => {
|
|
534
595
|
const condition = conditionalElement.getAttribute('res-display');
|
|
535
596
|
this._evaluateDisplayCondition(conditionalElement, this.data[variableName], condition);
|
|
536
597
|
});
|
|
598
|
+
|
|
599
|
+
const contextElements = document.querySelectorAll(`[res="${variableName}"] [res-display]`);
|
|
600
|
+
contextElements.forEach(conditionalElement => {
|
|
601
|
+
const condition = conditionalElement.getAttribute('res-display');
|
|
602
|
+
this._evaluateDisplayCondition(conditionalElement, this.data[variableName], condition);
|
|
603
|
+
});
|
|
537
604
|
}
|
|
538
605
|
|
|
539
606
|
updateStylesFor(variableName) {
|
package/resonant.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
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)}}
|
|
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 l=s.charCodeAt(i);a=(a<<5)-a+l,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)=>{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=e.parentElement,a=null,i=null;for(;s&&!a;)a=s.getAttribute("res"),i||(i=s.getAttribute("res-index")),s=s.parentElement;if(a&&null!==i&&Array.isArray(this.data[a])&&(t=this.data[a][i]),a&&!r.includes(a+".")){let l=r.match(/\b[a-zA-Z_][a-zA-Z0-9_]*\b/g)||[];l.forEach(e=>{if("=="===e||"!="===e||!(e in t))return;let s=RegExp(`\\b${e}\\b`,"g"),i=t[e];void 0!==i&&(void 0!==t["item."+e]?r=r.replace(s,`item.${e}`):"object"==typeof i&&(r=r.replace(s,`${a}.${e}`)))})}let n=!1;try{n=Function("item",`return ${r}`)(t)}catch(o){let h=r.indexOf("."),d=r.substring(h+1);n=Function("item",`return item.${d}`)(t)}e.style.display=n?"inherit":"none"}catch(c){console.error(`Error evaluating display condition: ${r}`,c)}}_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)){let a=this.data[r].key;this._renderNestedArray(e,t,a)}else if("object"==typeof t&&null!==t){let i=e.querySelectorAll("[res-prop]");i.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 l=t.key,n;if(a.has(l))n=a.get(l),a.delete(l),n.setAttribute("res-index",i);else for(let o in(n=s.cloneNode(!0)).removeAttribute("res-template"),n.style.display="",n.setAttribute("res-rendered","true"),n.setAttribute("res-key",l),n.setAttribute("res-index",i),t){let h=null,d=n.querySelector(`[res-prop="${o}"]`);if(d||(d=n.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)){let u=this.data[e][i].key;this._renderNestedArray(d,c,u)}else if("object"==typeof c&&null!==c){let p=d.querySelectorAll("[res-prop]");p.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(n,t),this._bindClickEvents(n,t,this.data[e]),r.appendChild(n)})}_renderNestedArray(e,t,r){let s=e.cloneNode(!0);e.innerHTML="",e.removeAttribute("res-prop"),t.forEach((a,i)=>{let l=s.cloneNode(!0);if(l.setAttribute("res-rendered","true"),null!==a&&"object"!=typeof a)l.innerHTML=a;else{let n=l.querySelectorAll("[res-prop]");n.forEach(e=>{let t=e.getAttribute("res-prop");t&&t in a&&(e.setAttribute("res-child",a.key+"-"+t+"--"+r),this._renderObjectProperty(e,a[t],null,t))}),this._handleDisplayElements(l,a),this._bindClickEvents(l,a,t);let o=l.getAttribute("res-display");o&&this._evaluateDisplayCondition(l,a,o)}e.appendChild(l)})}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)});let r=document.querySelectorAll(`[res="${e}"] [res-display]`);r.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)}}
|