@schukai/monster 4.28.0 → 4.29.0
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/CHANGELOG.md +12 -0
- package/package.json +1 -1
- package/source/dom/updater.mjs +837 -839
- package/source/types/version.mjs +1 -1
- package/test/cases/monster.mjs +1 -1
- package/test/web/test.html +2 -2
- package/test/web/tests.js +76 -58
package/source/dom/updater.mjs
CHANGED
@@ -17,24 +17,24 @@ import { diff } from "../data/diff.mjs";
|
|
17
17
|
import { Pathfinder } from "../data/pathfinder.mjs";
|
18
18
|
import { Pipe } from "../data/pipe.mjs";
|
19
19
|
import {
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
20
|
+
ATTRIBUTE_ERRORMESSAGE,
|
21
|
+
ATTRIBUTE_UPDATER_ATTRIBUTES,
|
22
|
+
ATTRIBUTE_UPDATER_BIND,
|
23
|
+
ATTRIBUTE_UPDATER_BIND_TYPE,
|
24
|
+
ATTRIBUTE_UPDATER_INSERT,
|
25
|
+
ATTRIBUTE_UPDATER_INSERT_REFERENCE,
|
26
|
+
ATTRIBUTE_UPDATER_REMOVE,
|
27
|
+
ATTRIBUTE_UPDATER_REPLACE,
|
28
|
+
ATTRIBUTE_UPDATER_SELECT_THIS,
|
29
29
|
} from "./constants.mjs";
|
30
30
|
|
31
31
|
import { Base } from "../types/base.mjs";
|
32
32
|
import {
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
33
|
+
isArray,
|
34
|
+
isInteger,
|
35
|
+
isString,
|
36
|
+
isInstance,
|
37
|
+
isIterable,
|
38
38
|
} from "../types/is.mjs";
|
39
39
|
import { Observer } from "../types/observer.mjs";
|
40
40
|
import { ProxyObserver } from "../types/proxyobserver.mjs";
|
@@ -43,8 +43,8 @@ import { clone } from "../util/clone.mjs";
|
|
43
43
|
import { trimSpaces } from "../util/trimspaces.mjs";
|
44
44
|
import { addAttributeToken, addToObjectLink } from "./attributes.mjs";
|
45
45
|
import {
|
46
|
-
|
47
|
-
|
46
|
+
CustomElement,
|
47
|
+
updaterTransformerMethodsSymbol,
|
48
48
|
} from "./customelement.mjs";
|
49
49
|
import { findTargetElementFromEvent } from "./events.mjs";
|
50
50
|
import { findDocumentTemplate } from "./template.mjs";
|
@@ -85,190 +85,190 @@ const timerElementEventHandlerSymbol = Symbol("timerElementEventHandler");
|
|
85
85
|
* @summary The updater class connects an object with the dom
|
86
86
|
*/
|
87
87
|
class Updater extends Base {
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
88
|
+
/**
|
89
|
+
* @since 1.8.0
|
90
|
+
* @param {HTMLElement} element
|
91
|
+
* @param {object|ProxyObserver|undefined} subject
|
92
|
+
* @throws {TypeError} value is not a object
|
93
|
+
* @throws {TypeError} value is not an instance of HTMLElement
|
94
|
+
* @see {@link findDocumentTemplate}
|
95
|
+
*/
|
96
|
+
constructor(element, subject) {
|
97
|
+
super();
|
98
|
+
|
99
|
+
/**
|
100
|
+
* @type {HTMLElement}
|
101
|
+
*/
|
102
|
+
if (subject === undefined) subject = {};
|
103
|
+
if (!isInstance(subject, ProxyObserver)) {
|
104
|
+
subject = new ProxyObserver(subject);
|
105
|
+
}
|
106
|
+
|
107
|
+
this[internalSymbol] = {
|
108
|
+
element: validateInstance(element, HTMLElement),
|
109
|
+
last: {},
|
110
|
+
callbacks: new Map(),
|
111
|
+
eventTypes: ["keyup", "click", "change", "drop", "touchend", "input"],
|
112
|
+
subject: subject,
|
113
|
+
};
|
114
|
+
|
115
|
+
this[internalSymbol].callbacks.set(
|
116
|
+
"checkstate",
|
117
|
+
getCheckStateCallback.call(this),
|
118
|
+
);
|
119
|
+
|
120
|
+
this[internalSymbol].subject.attachObserver(
|
121
|
+
new Observer(() => {
|
122
|
+
const s = this[internalSymbol].subject.getRealSubject();
|
123
|
+
|
124
|
+
const diffResult = diff(this[internalSymbol].last, s);
|
125
|
+
this[internalSymbol].last = clone(s);
|
126
|
+
|
127
|
+
const promises = [];
|
128
|
+
|
129
|
+
for (const [, change] of Object.entries(diffResult)) {
|
130
|
+
promises.push(
|
131
|
+
new Promise((resolve, reject) => {
|
132
|
+
getWindow().requestAnimationFrame(() => {
|
133
|
+
try {
|
134
|
+
removeElement.call(this, change);
|
135
|
+
insertElement.call(this, change);
|
136
|
+
updateContent.call(this, change);
|
137
|
+
updateAttributes.call(this, change);
|
138
|
+
|
139
|
+
resolve();
|
140
|
+
} catch (error) {
|
141
|
+
reject(error);
|
142
|
+
}
|
143
|
+
});
|
144
|
+
}),
|
145
|
+
);
|
146
|
+
}
|
147
|
+
|
148
|
+
return Promise.all(promises);
|
149
|
+
}),
|
150
|
+
);
|
151
|
+
}
|
152
|
+
|
153
|
+
/**
|
154
|
+
* Defaults: 'keyup', 'click', 'change', 'drop', 'touchend'
|
155
|
+
*
|
156
|
+
* @see {@link https://developer.mozilla.org/de/docs/Web/Events}
|
157
|
+
* @since 1.9.0
|
158
|
+
* @param {Array} types
|
159
|
+
* @return {Updater}
|
160
|
+
*/
|
161
|
+
setEventTypes(types) {
|
162
|
+
this[internalSymbol].eventTypes = validateArray(types);
|
163
|
+
return this;
|
164
|
+
}
|
165
|
+
|
166
|
+
/**
|
167
|
+
* With this method, the eventlisteners are hooked in and the magic begins.
|
168
|
+
*
|
169
|
+
* ```js
|
170
|
+
* updater.run().then(() => {
|
171
|
+
* updater.enableEventProcessing();
|
172
|
+
* });
|
173
|
+
* ```
|
174
|
+
*
|
175
|
+
* @since 1.9.0
|
176
|
+
* @return {Updater}
|
177
|
+
* @throws {Error} the bind argument must start as a value with a path
|
178
|
+
*/
|
179
|
+
enableEventProcessing() {
|
180
|
+
this.disableEventProcessing();
|
181
|
+
|
182
|
+
for (const type of this[internalSymbol].eventTypes) {
|
183
|
+
// @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
|
184
|
+
this[internalSymbol].element.addEventListener(
|
185
|
+
type,
|
186
|
+
getControlEventHandler.call(this),
|
187
|
+
{
|
188
|
+
capture: true,
|
189
|
+
passive: true,
|
190
|
+
},
|
191
|
+
);
|
192
|
+
}
|
193
|
+
|
194
|
+
return this;
|
195
|
+
}
|
196
|
+
|
197
|
+
/**
|
198
|
+
* This method turns off the magic or who loves it more profane it removes the eventListener.
|
199
|
+
*
|
200
|
+
* @since 1.9.0
|
201
|
+
* @return {Updater}
|
202
|
+
*/
|
203
|
+
disableEventProcessing() {
|
204
|
+
for (const type of this[internalSymbol].eventTypes) {
|
205
|
+
this[internalSymbol].element.removeEventListener(
|
206
|
+
type,
|
207
|
+
getControlEventHandler.call(this),
|
208
|
+
);
|
209
|
+
}
|
210
|
+
|
211
|
+
return this;
|
212
|
+
}
|
213
|
+
|
214
|
+
/**
|
215
|
+
* The run method must be called for the update to start working.
|
216
|
+
* The method ensures that changes are detected.
|
217
|
+
*
|
218
|
+
* ```js
|
219
|
+
* updater.run().then(() => {
|
220
|
+
* updater.enableEventProcessing();
|
221
|
+
* });
|
222
|
+
* ```
|
223
|
+
*
|
224
|
+
* @summary Let the magic begin
|
225
|
+
* @return {Promise}
|
226
|
+
*/
|
227
|
+
run() {
|
228
|
+
// the key __init__has no further meaning and is only
|
229
|
+
// used to create the diff for empty objects.
|
230
|
+
this[internalSymbol].last = { __init__: true };
|
231
|
+
return this[internalSymbol].subject.notifyObservers();
|
232
|
+
}
|
233
|
+
|
234
|
+
/**
|
235
|
+
* Gets the values of bound elements and changes them in subject
|
236
|
+
*
|
237
|
+
* @since 1.27.0
|
238
|
+
* @return {Monster.DOM.Updater}
|
239
|
+
*/
|
240
|
+
retrieve() {
|
241
|
+
retrieveFromBindings.call(this);
|
242
|
+
return this;
|
243
|
+
}
|
244
|
+
|
245
|
+
/**
|
246
|
+
* If you have passed a ProxyObserver in the constructor, you will get the object that the ProxyObserver manages here.
|
247
|
+
* However, if you passed a simple object, here you will get a proxy for that object.
|
248
|
+
*
|
249
|
+
* For changes, the ProxyObserver must be used.
|
250
|
+
*
|
251
|
+
* @since 1.8.0
|
252
|
+
* @return {Proxy}
|
253
|
+
*/
|
254
|
+
getSubject() {
|
255
|
+
return this[internalSymbol].subject.getSubject();
|
256
|
+
}
|
257
|
+
|
258
|
+
/**
|
259
|
+
* This method can be used to register commands that can be called via call: instruction.
|
260
|
+
* This can be used to provide a pipe with its own functionality.
|
261
|
+
*
|
262
|
+
* @param {string} name
|
263
|
+
* @param {function} callback
|
264
|
+
* @return {Transformer}
|
265
|
+
* @throws {TypeError} value is not a string
|
266
|
+
* @throws {TypeError} value is not a function
|
267
|
+
*/
|
268
|
+
setCallback(name, callback) {
|
269
|
+
this[internalSymbol].callbacks.set(name, callback);
|
270
|
+
return this;
|
271
|
+
}
|
272
272
|
}
|
273
273
|
|
274
274
|
/**
|
@@ -279,20 +279,20 @@ class Updater extends Base {
|
|
279
279
|
* @this Updater
|
280
280
|
*/
|
281
281
|
function getCheckStateCallback() {
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
282
|
+
return function (current) {
|
283
|
+
// this is a reference to the current object (therefore no array function here)
|
284
|
+
if (this instanceof HTMLInputElement) {
|
285
|
+
if (["radio", "checkbox"].indexOf(this.type) !== -1) {
|
286
|
+
return `${this.value}` === `${current}` ? "true" : undefined;
|
287
|
+
}
|
288
|
+
} else if (this instanceof HTMLOptionElement) {
|
289
|
+
if (isArray(current) && current.indexOf(this.value) !== -1) {
|
290
|
+
return "true";
|
291
|
+
}
|
292
|
+
|
293
|
+
return undefined;
|
294
|
+
}
|
295
|
+
};
|
296
296
|
}
|
297
297
|
|
298
298
|
/**
|
@@ -307,41 +307,41 @@ const symbol = Symbol("@schukai/monster/updater@@EventHandler");
|
|
307
307
|
* @throws {Error} the bind argument must start as a value with a path
|
308
308
|
*/
|
309
309
|
function getControlEventHandler() {
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
310
|
+
if (this[symbol]) {
|
311
|
+
return this[symbol];
|
312
|
+
}
|
313
|
+
|
314
|
+
/**
|
315
|
+
* @throws {Error} the bind argument must start as a value with a path.
|
316
|
+
* @throws {Error} unsupported object
|
317
|
+
* @param {Event} event
|
318
|
+
*/
|
319
|
+
this[symbol] = (event) => {
|
320
|
+
const element = findTargetElementFromEvent(event, ATTRIBUTE_UPDATER_BIND);
|
321
|
+
|
322
|
+
if (element === undefined) {
|
323
|
+
return;
|
324
|
+
}
|
325
|
+
|
326
|
+
if (this[timerElementEventHandlerSymbol] instanceof DeadMansSwitch) {
|
327
|
+
try {
|
328
|
+
this[timerElementEventHandlerSymbol].touch();
|
329
|
+
return;
|
330
|
+
} catch (e) {
|
331
|
+
delete this[timerElementEventHandlerSymbol];
|
332
|
+
}
|
333
|
+
}
|
334
|
+
|
335
|
+
this[timerElementEventHandlerSymbol] = new DeadMansSwitch(50, () => {
|
336
|
+
try {
|
337
|
+
retrieveAndSetValue.call(this, element);
|
338
|
+
} catch (e) {
|
339
|
+
addErrorAttribute(element, e);
|
340
|
+
}
|
341
|
+
});
|
342
|
+
};
|
343
|
+
|
344
|
+
return this[symbol];
|
345
345
|
}
|
346
346
|
|
347
347
|
/**
|
@@ -351,186 +351,184 @@ function getControlEventHandler() {
|
|
351
351
|
* @private
|
352
352
|
*/
|
353
353
|
function retrieveAndSetValue(element) {
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
throw new Error("unsupported value for array");
|
507
|
-
}
|
508
|
-
break;
|
509
|
-
case "object":
|
510
|
-
case "json":
|
511
|
-
if (isString(value)) {
|
512
|
-
value = JSON.parse(value);
|
513
|
-
} else {
|
514
|
-
throw new Error("unsupported value for object");
|
515
|
-
}
|
516
|
-
|
517
|
-
break;
|
518
|
-
default:
|
519
|
-
break;
|
520
|
-
}
|
521
|
-
|
522
|
-
const copy = clone(this[internalSymbol].subject.getRealSubject());
|
523
|
-
|
524
|
-
const pf = new Pathfinder(copy);
|
525
|
-
pf.setVia(path, value);
|
526
|
-
|
527
|
-
const diffResult = diff(copy, this[internalSymbol].subject.getRealSubject());
|
528
|
-
|
529
|
-
if (diffResult.length > 0) {
|
530
|
-
pathfinder.setVia(path, value);
|
531
|
-
}
|
354
|
+
const pathfinder = new Pathfinder(this[internalSymbol].subject.getSubject());
|
355
|
+
|
356
|
+
let path = element.getAttribute(ATTRIBUTE_UPDATER_BIND);
|
357
|
+
if (path === null)
|
358
|
+
throw new Error("the bind argument must start as a value with a path");
|
359
|
+
|
360
|
+
if (path.indexOf("path:") !== 0) {
|
361
|
+
throw new Error("the bind argument must start as a value with a path");
|
362
|
+
}
|
363
|
+
|
364
|
+
path = path.substring(5); // remove path: from the string
|
365
|
+
|
366
|
+
let value;
|
367
|
+
|
368
|
+
if (element instanceof HTMLInputElement) {
|
369
|
+
switch (element.type) {
|
370
|
+
case "checkbox":
|
371
|
+
value = element.checked ? element.value : undefined;
|
372
|
+
break;
|
373
|
+
default:
|
374
|
+
value = element.value;
|
375
|
+
break;
|
376
|
+
}
|
377
|
+
} else if (element instanceof HTMLTextAreaElement) {
|
378
|
+
value = element.value;
|
379
|
+
} else if (element instanceof HTMLSelectElement) {
|
380
|
+
switch (element.type) {
|
381
|
+
case "select-one":
|
382
|
+
value = element.value;
|
383
|
+
break;
|
384
|
+
case "select-multiple":
|
385
|
+
value = element.value;
|
386
|
+
|
387
|
+
let options = element?.selectedOptions;
|
388
|
+
if (options === undefined)
|
389
|
+
options = element.querySelectorAll(":scope option:checked");
|
390
|
+
value = Array.from(options).map(({ value }) => value);
|
391
|
+
|
392
|
+
break;
|
393
|
+
}
|
394
|
+
|
395
|
+
// values from custom elements
|
396
|
+
} else if (
|
397
|
+
(element?.constructor?.prototype &&
|
398
|
+
!!Object.getOwnPropertyDescriptor(
|
399
|
+
element.constructor.prototype,
|
400
|
+
"value",
|
401
|
+
)?.["get"]) ||
|
402
|
+
"value" in element
|
403
|
+
) {
|
404
|
+
value = element?.["value"];
|
405
|
+
} else {
|
406
|
+
throw new Error("unsupported object");
|
407
|
+
}
|
408
|
+
|
409
|
+
const type = element.getAttribute(ATTRIBUTE_UPDATER_BIND_TYPE);
|
410
|
+
switch (type) {
|
411
|
+
case "integer?":
|
412
|
+
case "int?":
|
413
|
+
case "number?":
|
414
|
+
value = Number(value);
|
415
|
+
if (isNaN(value) || 0 === value) {
|
416
|
+
value = undefined;
|
417
|
+
}
|
418
|
+
break;
|
419
|
+
|
420
|
+
case "number":
|
421
|
+
case "int":
|
422
|
+
case "float":
|
423
|
+
case "integer":
|
424
|
+
value = Number(value);
|
425
|
+
if (isNaN(value)) {
|
426
|
+
value = 0;
|
427
|
+
}
|
428
|
+
break;
|
429
|
+
case "boolean":
|
430
|
+
case "bool":
|
431
|
+
case "checkbox":
|
432
|
+
value =
|
433
|
+
value === "true" || value === "1" || value === "on" || value === true;
|
434
|
+
break;
|
435
|
+
|
436
|
+
case "string[]":
|
437
|
+
case "[]string":
|
438
|
+
if (isString(value)) {
|
439
|
+
if (value.trim() === "") {
|
440
|
+
value = [];
|
441
|
+
} else {
|
442
|
+
value = value.split(",").map((v) => `${v}`);
|
443
|
+
}
|
444
|
+
} else if (isInteger(value)) {
|
445
|
+
value = [`${value}`];
|
446
|
+
} else if (value === undefined || value === null) {
|
447
|
+
value = [];
|
448
|
+
} else if (isArray(value)) {
|
449
|
+
value = value.map((v) => `${v}`);
|
450
|
+
} else {
|
451
|
+
throw new Error("unsupported value");
|
452
|
+
}
|
453
|
+
|
454
|
+
break;
|
455
|
+
|
456
|
+
case "int[]":
|
457
|
+
case "integer[]":
|
458
|
+
case "number[]":
|
459
|
+
case "[]int":
|
460
|
+
case "[]integer":
|
461
|
+
case "[]number":
|
462
|
+
value=parseIntArray(value)
|
463
|
+
break;
|
464
|
+
case "[]":
|
465
|
+
case "array":
|
466
|
+
case "list":
|
467
|
+
if (isString(value)) {
|
468
|
+
if (value.trim() === "") {
|
469
|
+
value = [];
|
470
|
+
} else {
|
471
|
+
value = value.split(",");
|
472
|
+
}
|
473
|
+
} else if (isInteger(value)) {
|
474
|
+
value = [value];
|
475
|
+
} else if (value === undefined || value === null) {
|
476
|
+
value = [];
|
477
|
+
} else if (isArray(value)) {
|
478
|
+
// nothing to do
|
479
|
+
} else {
|
480
|
+
throw new Error("unsupported value for array");
|
481
|
+
}
|
482
|
+
break;
|
483
|
+
case "object":
|
484
|
+
case "json":
|
485
|
+
if (isString(value)) {
|
486
|
+
value = JSON.parse(value);
|
487
|
+
} else {
|
488
|
+
throw new Error("unsupported value for object");
|
489
|
+
}
|
490
|
+
|
491
|
+
break;
|
492
|
+
default:
|
493
|
+
break;
|
494
|
+
}
|
495
|
+
|
496
|
+
const copy = clone(this[internalSymbol].subject.getRealSubject());
|
497
|
+
|
498
|
+
const pf = new Pathfinder(copy);
|
499
|
+
pf.setVia(path, value);
|
500
|
+
|
501
|
+
const diffResult = diff(copy, this[internalSymbol].subject.getRealSubject());
|
502
|
+
|
503
|
+
if (diffResult.length > 0) {
|
504
|
+
pathfinder.setVia(path, value);
|
505
|
+
}
|
532
506
|
}
|
533
507
|
|
508
|
+
/**
|
509
|
+
* @private
|
510
|
+
*/
|
511
|
+
function parseIntArray(val) {
|
512
|
+
if (isString(val)) {
|
513
|
+
return val.trim() === ""
|
514
|
+
? []
|
515
|
+
: val
|
516
|
+
.split(",")
|
517
|
+
.map((v) => parseInt(v, 10))
|
518
|
+
.filter((v) => !isNaN(v));
|
519
|
+
} else if (isInteger(val)) {
|
520
|
+
return [val];
|
521
|
+
} else if (val === undefined || val === null) {
|
522
|
+
return [];
|
523
|
+
} else if (isArray(val)) {
|
524
|
+
return val
|
525
|
+
.map((v) => parseInt(v, 10))
|
526
|
+
.filter((v) => !isNaN(v));
|
527
|
+
}
|
528
|
+
throw new Error("unsupported value for int array");
|
529
|
+
}
|
530
|
+
|
531
|
+
|
534
532
|
/**
|
535
533
|
* @license AGPLv3
|
536
534
|
* @since 1.27.0
|
@@ -538,15 +536,15 @@ function retrieveAndSetValue(element) {
|
|
538
536
|
* @private
|
539
537
|
*/
|
540
538
|
function retrieveFromBindings() {
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
539
|
+
if (this[internalSymbol].element.matches(`[${ATTRIBUTE_UPDATER_BIND}]`)) {
|
540
|
+
retrieveAndSetValue.call(this, this[internalSymbol].element);
|
541
|
+
}
|
542
|
+
|
543
|
+
for (const [, element] of this[internalSymbol].element
|
544
|
+
.querySelectorAll(`[${ATTRIBUTE_UPDATER_BIND}]`)
|
545
|
+
.entries()) {
|
546
|
+
retrieveAndSetValue.call(this, element);
|
547
|
+
}
|
550
548
|
}
|
551
549
|
|
552
550
|
/**
|
@@ -557,11 +555,11 @@ function retrieveFromBindings() {
|
|
557
555
|
* @return {void}
|
558
556
|
*/
|
559
557
|
function removeElement(change) {
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
558
|
+
for (const [, element] of this[internalSymbol].element
|
559
|
+
.querySelectorAll(`:scope [${ATTRIBUTE_UPDATER_REMOVE}]`)
|
560
|
+
.entries()) {
|
561
|
+
element.parentNode.removeChild(element);
|
562
|
+
}
|
565
563
|
}
|
566
564
|
|
567
565
|
/**
|
@@ -577,128 +575,128 @@ function removeElement(change) {
|
|
577
575
|
* @this Updater
|
578
576
|
*/
|
579
577
|
function insertElement(change) {
|
580
|
-
|
578
|
+
const subject = this[internalSymbol].subject.getRealSubject();
|
581
579
|
|
582
|
-
|
583
|
-
|
580
|
+
const mem = new WeakSet();
|
581
|
+
let wd = 0;
|
584
582
|
|
585
|
-
|
583
|
+
const container = this[internalSymbol].element;
|
586
584
|
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
585
|
+
while (true) {
|
586
|
+
let found = false;
|
587
|
+
wd++;
|
588
|
+
|
589
|
+
const p = clone(change?.["path"]);
|
590
|
+
if (!isArray(p)) return;
|
591
|
+
|
592
|
+
while (p.length > 0) {
|
593
|
+
const current = p.join(".");
|
594
|
+
|
595
|
+
let iterator = new Set();
|
596
|
+
const query = `[${ATTRIBUTE_UPDATER_INSERT}*="path:${current}"]`;
|
597
|
+
|
598
|
+
const e = container.querySelectorAll(query);
|
599
|
+
|
600
|
+
if (e.length > 0) {
|
601
|
+
iterator = new Set([...e]);
|
602
|
+
}
|
603
|
+
|
604
|
+
if (container.matches(query)) {
|
605
|
+
iterator.add(container);
|
606
|
+
}
|
607
|
+
|
608
|
+
for (const [, containerElement] of iterator.entries()) {
|
609
|
+
if (mem.has(containerElement)) continue;
|
610
|
+
mem.add(containerElement);
|
611
|
+
|
612
|
+
found = true;
|
613
|
+
|
614
|
+
const attributes = containerElement.getAttribute(
|
615
|
+
ATTRIBUTE_UPDATER_INSERT,
|
616
|
+
);
|
617
|
+
if (attributes === null) continue;
|
618
|
+
|
619
|
+
const def = trimSpaces(attributes);
|
620
|
+
const i = def.indexOf(" ");
|
621
|
+
const key = trimSpaces(def.substr(0, i));
|
622
|
+
const refPrefix = `${key}-`;
|
623
|
+
const cmd = trimSpaces(def.substr(i));
|
624
|
+
|
625
|
+
// this case is actually excluded by the query but is nevertheless checked again here
|
626
|
+
if (cmd.indexOf("|") > 0) {
|
627
|
+
throw new Error("pipes are not allowed when cloning a node.");
|
628
|
+
}
|
629
|
+
|
630
|
+
const pipe = new Pipe(cmd);
|
631
|
+
this[internalSymbol].callbacks.forEach((f, n) => {
|
632
|
+
pipe.setCallback(n, f);
|
633
|
+
});
|
634
|
+
|
635
|
+
let value;
|
636
|
+
try {
|
637
|
+
containerElement.removeAttribute(ATTRIBUTE_ERRORMESSAGE);
|
638
|
+
value = pipe.run(subject);
|
639
|
+
} catch (e) {
|
640
|
+
addErrorAttribute(containerElement, e);
|
641
|
+
}
|
642
|
+
|
643
|
+
const dataPath = cmd.split(":").pop();
|
644
|
+
|
645
|
+
let insertPoint;
|
646
|
+
if (containerElement.hasChildNodes()) {
|
647
|
+
insertPoint = containerElement.lastChild;
|
648
|
+
}
|
649
|
+
|
650
|
+
if (!isIterable(value)) {
|
651
|
+
throw new Error("the value is not iterable");
|
652
|
+
}
|
653
|
+
|
654
|
+
const available = new Set();
|
655
|
+
|
656
|
+
for (const [i] of Object.entries(value)) {
|
657
|
+
const ref = refPrefix + i;
|
658
|
+
const currentPath = `${dataPath}.${i}`;
|
659
|
+
|
660
|
+
available.add(ref);
|
661
|
+
const refElement = containerElement.querySelector(
|
662
|
+
`[${ATTRIBUTE_UPDATER_INSERT_REFERENCE}="${ref}"]`,
|
663
|
+
);
|
664
|
+
|
665
|
+
if (refElement instanceof HTMLElement) {
|
666
|
+
insertPoint = refElement;
|
667
|
+
continue;
|
668
|
+
}
|
669
|
+
|
670
|
+
appendNewDocumentFragment(containerElement, key, ref, currentPath);
|
671
|
+
}
|
672
|
+
|
673
|
+
const nodes = containerElement.querySelectorAll(
|
674
|
+
`[${ATTRIBUTE_UPDATER_INSERT_REFERENCE}*="${refPrefix}"]`,
|
675
|
+
);
|
676
|
+
|
677
|
+
for (const [, node] of Object.entries(nodes)) {
|
678
|
+
if (
|
679
|
+
!available.has(
|
680
|
+
node.getAttribute(ATTRIBUTE_UPDATER_INSERT_REFERENCE),
|
681
|
+
)
|
682
|
+
) {
|
683
|
+
try {
|
684
|
+
containerElement.removeChild(node);
|
685
|
+
} catch (e) {
|
686
|
+
addErrorAttribute(containerElement, e);
|
687
|
+
}
|
688
|
+
}
|
689
|
+
}
|
690
|
+
}
|
691
|
+
|
692
|
+
p.pop();
|
693
|
+
}
|
694
|
+
|
695
|
+
if (found === false) break;
|
696
|
+
if (wd++ > 200) {
|
697
|
+
throw new Error("the maximum depth for the recursion is reached.");
|
698
|
+
}
|
699
|
+
}
|
702
700
|
}
|
703
701
|
|
704
702
|
/**
|
@@ -713,17 +711,17 @@ function insertElement(change) {
|
|
713
711
|
* @throws {Error} no template was found with the specified key.
|
714
712
|
*/
|
715
713
|
function appendNewDocumentFragment(container, key, ref, path) {
|
716
|
-
|
714
|
+
const template = findDocumentTemplate(key, container);
|
717
715
|
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
716
|
+
const nodes = template.createDocumentFragment();
|
717
|
+
for (const [, node] of Object.entries(nodes.childNodes)) {
|
718
|
+
if (node instanceof HTMLElement) {
|
719
|
+
applyRecursive(node, key, path);
|
720
|
+
node.setAttribute(ATTRIBUTE_UPDATER_INSERT_REFERENCE, ref);
|
721
|
+
}
|
724
722
|
|
725
|
-
|
726
|
-
|
723
|
+
container.appendChild(node);
|
724
|
+
}
|
727
725
|
}
|
728
726
|
|
729
727
|
/**
|
@@ -736,31 +734,31 @@ function appendNewDocumentFragment(container, key, ref, path) {
|
|
736
734
|
* @return {void}
|
737
735
|
*/
|
738
736
|
function applyRecursive(node, key, path) {
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
737
|
+
if (!(node instanceof HTMLElement)) {
|
738
|
+
return;
|
739
|
+
}
|
740
|
+
|
741
|
+
if (node.hasAttribute(ATTRIBUTE_UPDATER_REPLACE)) {
|
742
|
+
const value = node.getAttribute(ATTRIBUTE_UPDATER_REPLACE);
|
743
|
+
node.setAttribute(
|
744
|
+
ATTRIBUTE_UPDATER_REPLACE,
|
745
|
+
value.replaceAll(`path:${key}`, `path:${path}`),
|
746
|
+
);
|
747
|
+
}
|
748
|
+
|
749
|
+
if (node.hasAttribute(ATTRIBUTE_UPDATER_ATTRIBUTES)) {
|
750
|
+
const value = node.getAttribute(ATTRIBUTE_UPDATER_ATTRIBUTES);
|
751
|
+
node.setAttribute(
|
752
|
+
ATTRIBUTE_UPDATER_ATTRIBUTES,
|
753
|
+
value.replaceAll(`path:${key}`, `path:${path}`),
|
754
|
+
);
|
755
|
+
}
|
756
|
+
|
757
|
+
for (const [, child] of Object.entries(node.childNodes)) {
|
758
|
+
if (child instanceof HTMLElement) {
|
759
|
+
applyRecursive(child, key, path);
|
760
|
+
}
|
761
|
+
}
|
764
762
|
}
|
765
763
|
|
766
764
|
/**
|
@@ -772,19 +770,19 @@ function applyRecursive(node, key, path) {
|
|
772
770
|
* @this Updater
|
773
771
|
*/
|
774
772
|
function updateContent(change) {
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
773
|
+
const subject = this[internalSymbol].subject.getRealSubject();
|
774
|
+
|
775
|
+
const p = clone(change?.["path"]);
|
776
|
+
runUpdateContent.call(this, this[internalSymbol].element, p, subject);
|
777
|
+
|
778
|
+
const slots = this[internalSymbol].element.querySelectorAll("slot");
|
779
|
+
if (slots.length > 0) {
|
780
|
+
for (const [, slot] of Object.entries(slots)) {
|
781
|
+
for (const [, element] of Object.entries(slot.assignedNodes())) {
|
782
|
+
runUpdateContent.call(this, element, p, subject);
|
783
|
+
}
|
784
|
+
}
|
785
|
+
}
|
788
786
|
}
|
789
787
|
|
790
788
|
/**
|
@@ -797,64 +795,64 @@ function updateContent(change) {
|
|
797
795
|
* @return {void}
|
798
796
|
*/
|
799
797
|
function runUpdateContent(container, parts, subject) {
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
798
|
+
if (!isArray(parts)) return;
|
799
|
+
if (!(container instanceof HTMLElement)) return;
|
800
|
+
parts = clone(parts);
|
801
|
+
|
802
|
+
const mem = new WeakSet();
|
803
|
+
|
804
|
+
while (parts.length > 0) {
|
805
|
+
const current = parts.join(".");
|
806
|
+
parts.pop();
|
807
|
+
|
808
|
+
// Unfortunately, static data is always changed as well, since it is not possible to react to changes here.
|
809
|
+
const query = `[${ATTRIBUTE_UPDATER_REPLACE}^="path:${current}"], [${ATTRIBUTE_UPDATER_REPLACE}^="static:"], [${ATTRIBUTE_UPDATER_REPLACE}^="i18n:"]`;
|
810
|
+
const e = container.querySelectorAll(`${query}`);
|
811
|
+
|
812
|
+
const iterator = new Set([...e]);
|
813
|
+
|
814
|
+
if (container.matches(query)) {
|
815
|
+
iterator.add(container);
|
816
|
+
}
|
817
|
+
|
818
|
+
/**
|
819
|
+
* @type {HTMLElement}
|
820
|
+
*/
|
821
|
+
for (const [element] of iterator.entries()) {
|
822
|
+
if (mem.has(element)) return;
|
823
|
+
mem.add(element);
|
824
|
+
|
825
|
+
const attributes = element.getAttribute(ATTRIBUTE_UPDATER_REPLACE);
|
826
|
+
const cmd = trimSpaces(attributes);
|
827
|
+
|
828
|
+
const pipe = new Pipe(cmd);
|
829
|
+
this[internalSymbol].callbacks.forEach((f, n) => {
|
830
|
+
pipe.setCallback(n, f);
|
831
|
+
});
|
832
|
+
|
833
|
+
let value;
|
834
|
+
try {
|
835
|
+
element.removeAttribute(ATTRIBUTE_ERRORMESSAGE);
|
836
|
+
value = pipe.run(subject);
|
837
|
+
} catch (e) {
|
838
|
+
element.setAttribute(ATTRIBUTE_ERRORMESSAGE, e.message);
|
839
|
+
}
|
840
|
+
|
841
|
+
if (value instanceof HTMLElement) {
|
842
|
+
while (element.firstChild) {
|
843
|
+
element.removeChild(element.firstChild);
|
844
|
+
}
|
845
|
+
|
846
|
+
try {
|
847
|
+
element.appendChild(value);
|
848
|
+
} catch (e) {
|
849
|
+
addErrorAttribute(element, e);
|
850
|
+
}
|
851
|
+
} else {
|
852
|
+
element.innerHTML = value;
|
853
|
+
}
|
854
|
+
}
|
855
|
+
}
|
858
856
|
}
|
859
857
|
|
860
858
|
/**
|
@@ -864,9 +862,9 @@ function runUpdateContent(container, parts, subject) {
|
|
864
862
|
* @return {void}
|
865
863
|
*/
|
866
864
|
function updateAttributes(change) {
|
867
|
-
|
868
|
-
|
869
|
-
|
865
|
+
const subject = this[internalSymbol].subject.getRealSubject();
|
866
|
+
const p = clone(change?.["path"]);
|
867
|
+
runUpdateAttributes.call(this, this[internalSymbol].element, p, subject);
|
870
868
|
}
|
871
869
|
|
872
870
|
/**
|
@@ -878,70 +876,70 @@ function updateAttributes(change) {
|
|
878
876
|
* @this Updater
|
879
877
|
*/
|
880
878
|
function runUpdateAttributes(container, parts, subject) {
|
881
|
-
|
882
|
-
|
879
|
+
if (!isArray(parts)) return;
|
880
|
+
parts = clone(parts);
|
883
881
|
|
884
|
-
|
882
|
+
const mem = new WeakSet();
|
885
883
|
|
886
|
-
|
887
|
-
|
888
|
-
|
884
|
+
while (parts.length > 0) {
|
885
|
+
const current = parts.join(".");
|
886
|
+
parts.pop();
|
889
887
|
|
890
|
-
|
888
|
+
let iterator = new Set();
|
891
889
|
|
892
|
-
|
890
|
+
const query = `[${ATTRIBUTE_UPDATER_SELECT_THIS}][${ATTRIBUTE_UPDATER_ATTRIBUTES}], [${ATTRIBUTE_UPDATER_ATTRIBUTES}*="path:${current}"], [${ATTRIBUTE_UPDATER_ATTRIBUTES}^="static:"], [${ATTRIBUTE_UPDATER_ATTRIBUTES}^="i18n:"]`;
|
893
891
|
|
894
|
-
|
892
|
+
const e = container.querySelectorAll(query);
|
895
893
|
|
896
|
-
|
897
|
-
|
898
|
-
|
894
|
+
if (e.length > 0) {
|
895
|
+
iterator = new Set([...e]);
|
896
|
+
}
|
899
897
|
|
900
|
-
|
901
|
-
|
902
|
-
|
898
|
+
if (container.matches(query)) {
|
899
|
+
iterator.add(container);
|
900
|
+
}
|
903
901
|
|
904
|
-
|
905
|
-
|
906
|
-
|
902
|
+
for (const [element] of iterator.entries()) {
|
903
|
+
if (mem.has(element)) return;
|
904
|
+
mem.add(element);
|
907
905
|
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
906
|
+
// this case occurs when the ATTRIBUTE_UPDATER_SELECT_THIS attribute is set
|
907
|
+
if (!element.hasAttribute(ATTRIBUTE_UPDATER_ATTRIBUTES)) {
|
908
|
+
continue;
|
909
|
+
}
|
912
910
|
|
913
|
-
|
911
|
+
const attributes = element.getAttribute(ATTRIBUTE_UPDATER_ATTRIBUTES);
|
914
912
|
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
913
|
+
for (let [, def] of Object.entries(attributes.split(","))) {
|
914
|
+
def = trimSpaces(def);
|
915
|
+
const i = def.indexOf(" ");
|
916
|
+
const name = trimSpaces(def.substr(0, i));
|
917
|
+
const cmd = trimSpaces(def.substr(i));
|
920
918
|
|
921
|
-
|
919
|
+
const pipe = new Pipe(cmd);
|
922
920
|
|
923
|
-
|
924
|
-
|
925
|
-
|
921
|
+
this[internalSymbol].callbacks.forEach((f, n) => {
|
922
|
+
pipe.setCallback(n, f, element);
|
923
|
+
});
|
926
924
|
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
925
|
+
let value;
|
926
|
+
try {
|
927
|
+
element.removeAttribute(ATTRIBUTE_ERRORMESSAGE);
|
928
|
+
value = pipe.run(subject);
|
929
|
+
} catch (e) {
|
930
|
+
addErrorAttribute(element, e);
|
931
|
+
}
|
934
932
|
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
933
|
+
if (value === undefined) {
|
934
|
+
element.removeAttribute(name);
|
935
|
+
} else if (element.getAttribute(name) !== value) {
|
936
|
+
element.setAttribute(name, value);
|
937
|
+
}
|
940
938
|
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
|
939
|
+
handleInputControlAttributeUpdate.call(this, element, name, value);
|
940
|
+
}
|
941
|
+
}
|
942
|
+
}
|
945
943
|
}
|
946
944
|
|
947
945
|
/**
|
@@ -954,52 +952,52 @@ function runUpdateAttributes(container, parts, subject) {
|
|
954
952
|
*/
|
955
953
|
|
956
954
|
function handleInputControlAttributeUpdate(element, name, value) {
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
|
978
|
-
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
|
985
|
-
|
986
|
-
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
955
|
+
if (element instanceof HTMLSelectElement) {
|
956
|
+
switch (element.type) {
|
957
|
+
case "select-multiple":
|
958
|
+
for (const [index, opt] of Object.entries(element.options)) {
|
959
|
+
opt.selected = value.indexOf(opt.value) !== -1;
|
960
|
+
}
|
961
|
+
|
962
|
+
break;
|
963
|
+
case "select-one":
|
964
|
+
// Only one value may be selected
|
965
|
+
|
966
|
+
for (const [index, opt] of Object.entries(element.options)) {
|
967
|
+
if (opt.value === value) {
|
968
|
+
element.selectedIndex = index;
|
969
|
+
break;
|
970
|
+
}
|
971
|
+
}
|
972
|
+
|
973
|
+
break;
|
974
|
+
}
|
975
|
+
} else if (element instanceof HTMLInputElement) {
|
976
|
+
switch (element.type) {
|
977
|
+
case "radio":
|
978
|
+
if (name === "checked") {
|
979
|
+
element.checked = value !== undefined;
|
980
|
+
}
|
981
|
+
break;
|
982
|
+
|
983
|
+
case "checkbox":
|
984
|
+
if (name === "checked") {
|
985
|
+
element.checked = value !== undefined;
|
986
|
+
}
|
987
|
+
break;
|
988
|
+
|
989
|
+
case "text":
|
990
|
+
default:
|
991
|
+
if (name === "value") {
|
992
|
+
element.value = value === undefined ? "" : value;
|
993
|
+
}
|
994
|
+
break;
|
995
|
+
}
|
996
|
+
} else if (element instanceof HTMLTextAreaElement) {
|
997
|
+
if (name === "value") {
|
998
|
+
element.value = value === undefined ? "" : value;
|
999
|
+
}
|
1000
|
+
}
|
1003
1001
|
}
|
1004
1002
|
|
1005
1003
|
/**
|
@@ -1018,81 +1016,81 @@ function handleInputControlAttributeUpdate(element, name, value) {
|
|
1018
1016
|
* @throws {TypeError} symbol must be an instance of Symbol
|
1019
1017
|
*/
|
1020
1018
|
function addObjectWithUpdaterToElement(elements, symbol, object, config = {}) {
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1053
|
-
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1019
|
+
if (!(this instanceof HTMLElement)) {
|
1020
|
+
throw new TypeError(
|
1021
|
+
"the context of this function must be an instance of HTMLElement",
|
1022
|
+
);
|
1023
|
+
}
|
1024
|
+
|
1025
|
+
if (!(typeof symbol === "symbol")) {
|
1026
|
+
throw new TypeError("symbol must be an instance of Symbol");
|
1027
|
+
}
|
1028
|
+
|
1029
|
+
const updaters = new Set();
|
1030
|
+
|
1031
|
+
if (elements instanceof NodeList) {
|
1032
|
+
elements = new Set([...elements]);
|
1033
|
+
} else if (elements instanceof HTMLElement) {
|
1034
|
+
elements = new Set([elements]);
|
1035
|
+
} else if (elements instanceof Set) {
|
1036
|
+
} else {
|
1037
|
+
throw new TypeError(
|
1038
|
+
`elements is not a valid type. (actual: ${typeof elements})`,
|
1039
|
+
);
|
1040
|
+
}
|
1041
|
+
|
1042
|
+
const result = [];
|
1043
|
+
|
1044
|
+
const updaterCallbacks = [];
|
1045
|
+
const cb = this?.[updaterTransformerMethodsSymbol];
|
1046
|
+
if (this instanceof HTMLElement && typeof cb === "function") {
|
1047
|
+
const callbacks = cb.call(this);
|
1048
|
+
if (typeof callbacks === "object") {
|
1049
|
+
for (const [name, callback] of Object.entries(callbacks)) {
|
1050
|
+
if (typeof callback === "function") {
|
1051
|
+
updaterCallbacks.push([name, callback]);
|
1052
|
+
} else {
|
1053
|
+
addErrorAttribute(
|
1054
|
+
this,
|
1055
|
+
`onUpdaterPipeCallbacks: ${name} is not a function`,
|
1056
|
+
);
|
1057
|
+
}
|
1058
|
+
}
|
1059
|
+
} else {
|
1060
|
+
addErrorAttribute(
|
1061
|
+
this,
|
1062
|
+
`onUpdaterPipeCallbacks do not return an object with functions`,
|
1063
|
+
);
|
1064
|
+
}
|
1065
|
+
}
|
1066
|
+
|
1067
|
+
elements.forEach((element) => {
|
1068
|
+
if (!(element instanceof HTMLElement)) return;
|
1069
|
+
if (element instanceof HTMLTemplateElement) return;
|
1070
|
+
|
1071
|
+
const u = new Updater(element, object);
|
1072
|
+
updaters.add(u);
|
1073
|
+
|
1074
|
+
if (updaterCallbacks.length > 0) {
|
1075
|
+
for (const [name, callback] of updaterCallbacks) {
|
1076
|
+
u.setCallback(name, callback);
|
1077
|
+
}
|
1078
|
+
}
|
1079
|
+
|
1080
|
+
result.push(
|
1081
|
+
u.run().then(() => {
|
1082
|
+
if (config.eventProcessing === true) {
|
1083
|
+
u.enableEventProcessing();
|
1084
|
+
}
|
1085
|
+
|
1086
|
+
return u;
|
1087
|
+
}),
|
1088
|
+
);
|
1089
|
+
});
|
1090
|
+
|
1091
|
+
if (updaters.size > 0) {
|
1092
|
+
addToObjectLink(this, symbol, updaters);
|
1093
|
+
}
|
1094
|
+
|
1095
|
+
return result;
|
1098
1096
|
}
|