@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.
@@ -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
- 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,
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
- isArray,
34
- isInteger,
35
- isString,
36
- isInstance,
37
- isIterable,
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
- CustomElement,
47
- updaterTransformerMethodsSymbol,
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
- * @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
- }
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
- 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
- };
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
- 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];
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
- 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
- if (isString(value)) {
438
- if (value.trim() === "") {
439
- value = [];
440
- } else {
441
- value = value.split(",").map((v) => `${v}`);
442
- }
443
- } else if (isInteger(value)) {
444
- value = [`${value}`];
445
- } else if (value === undefined || value === null) {
446
- value = [];
447
- } else if (isArray(value)) {
448
- value = value.map((v) => `${v}`);
449
- } else {
450
- throw new Error("unsupported value");
451
- }
452
-
453
- break;
454
-
455
- case "int[]":
456
- case "integer[]":
457
- if (isString(value)) {
458
- if (value.trim() === "") {
459
- value = [];
460
- } else {
461
- value = value
462
- .split(",")
463
- .map((v) => {
464
- try {
465
- return parseInt(v, 10);
466
- } catch (e) {}
467
- return -1;
468
- })
469
- .filter((v) => v !== -1);
470
- }
471
- } else if (isInteger(value)) {
472
- value = [value];
473
- } else if (value === undefined || value === null) {
474
- value = [];
475
- } else if (isArray(value)) {
476
- value = value
477
- .split(",")
478
- .map((v) => {
479
- try {
480
- return parseInt(v, 10);
481
- } catch (e) {}
482
- return -1;
483
- })
484
- .filter((v) => v !== -1);
485
- } else {
486
- throw new Error("unsupported value");
487
- }
488
-
489
- break;
490
- case "[]":
491
- case "array":
492
- case "list":
493
- if (isString(value)) {
494
- if (value.trim() === "") {
495
- value = [];
496
- } else {
497
- value = value.split(",");
498
- }
499
- } else if (isInteger(value)) {
500
- value = [value];
501
- } else if (value === undefined || value === null) {
502
- value = [];
503
- } else if (isArray(value)) {
504
- // nothing to do
505
- } else {
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
- if (this[internalSymbol].element.matches(`[${ATTRIBUTE_UPDATER_BIND}]`)) {
542
- retrieveAndSetValue.call(this, this[internalSymbol].element);
543
- }
544
-
545
- for (const [, element] of this[internalSymbol].element
546
- .querySelectorAll(`[${ATTRIBUTE_UPDATER_BIND}]`)
547
- .entries()) {
548
- retrieveAndSetValue.call(this, element);
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
- for (const [, element] of this[internalSymbol].element
561
- .querySelectorAll(`:scope [${ATTRIBUTE_UPDATER_REMOVE}]`)
562
- .entries()) {
563
- element.parentNode.removeChild(element);
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
- const subject = this[internalSymbol].subject.getRealSubject();
578
+ const subject = this[internalSymbol].subject.getRealSubject();
581
579
 
582
- const mem = new WeakSet();
583
- let wd = 0;
580
+ const mem = new WeakSet();
581
+ let wd = 0;
584
582
 
585
- const container = this[internalSymbol].element;
583
+ const container = this[internalSymbol].element;
586
584
 
587
- while (true) {
588
- let found = false;
589
- wd++;
590
-
591
- const p = clone(change?.["path"]);
592
- if (!isArray(p)) return;
593
-
594
- while (p.length > 0) {
595
- const current = p.join(".");
596
-
597
- let iterator = new Set();
598
- const query = `[${ATTRIBUTE_UPDATER_INSERT}*="path:${current}"]`;
599
-
600
- const e = container.querySelectorAll(query);
601
-
602
- if (e.length > 0) {
603
- iterator = new Set([...e]);
604
- }
605
-
606
- if (container.matches(query)) {
607
- iterator.add(container);
608
- }
609
-
610
- for (const [, containerElement] of iterator.entries()) {
611
- if (mem.has(containerElement)) continue;
612
- mem.add(containerElement);
613
-
614
- found = true;
615
-
616
- const attributes = containerElement.getAttribute(
617
- ATTRIBUTE_UPDATER_INSERT,
618
- );
619
- if (attributes === null) continue;
620
-
621
- const def = trimSpaces(attributes);
622
- const i = def.indexOf(" ");
623
- const key = trimSpaces(def.substr(0, i));
624
- const refPrefix = `${key}-`;
625
- const cmd = trimSpaces(def.substr(i));
626
-
627
- // this case is actually excluded by the query but is nevertheless checked again here
628
- if (cmd.indexOf("|") > 0) {
629
- throw new Error("pipes are not allowed when cloning a node.");
630
- }
631
-
632
- const pipe = new Pipe(cmd);
633
- this[internalSymbol].callbacks.forEach((f, n) => {
634
- pipe.setCallback(n, f);
635
- });
636
-
637
- let value;
638
- try {
639
- containerElement.removeAttribute(ATTRIBUTE_ERRORMESSAGE);
640
- value = pipe.run(subject);
641
- } catch (e) {
642
- addErrorAttribute(containerElement, e);
643
- }
644
-
645
- const dataPath = cmd.split(":").pop();
646
-
647
- let insertPoint;
648
- if (containerElement.hasChildNodes()) {
649
- insertPoint = containerElement.lastChild;
650
- }
651
-
652
- if (!isIterable(value)) {
653
- throw new Error("the value is not iterable");
654
- }
655
-
656
- const available = new Set();
657
-
658
- for (const [i] of Object.entries(value)) {
659
- const ref = refPrefix + i;
660
- const currentPath = `${dataPath}.${i}`;
661
-
662
- available.add(ref);
663
- const refElement = containerElement.querySelector(
664
- `[${ATTRIBUTE_UPDATER_INSERT_REFERENCE}="${ref}"]`,
665
- );
666
-
667
- if (refElement instanceof HTMLElement) {
668
- insertPoint = refElement;
669
- continue;
670
- }
671
-
672
- appendNewDocumentFragment(containerElement, key, ref, currentPath);
673
- }
674
-
675
- const nodes = containerElement.querySelectorAll(
676
- `[${ATTRIBUTE_UPDATER_INSERT_REFERENCE}*="${refPrefix}"]`,
677
- );
678
-
679
- for (const [, node] of Object.entries(nodes)) {
680
- if (
681
- !available.has(
682
- node.getAttribute(ATTRIBUTE_UPDATER_INSERT_REFERENCE),
683
- )
684
- ) {
685
- try {
686
- containerElement.removeChild(node);
687
- } catch (e) {
688
- addErrorAttribute(containerElement, e);
689
- }
690
- }
691
- }
692
- }
693
-
694
- p.pop();
695
- }
696
-
697
- if (found === false) break;
698
- if (wd++ > 200) {
699
- throw new Error("the maximum depth for the recursion is reached.");
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
- const template = findDocumentTemplate(key, container);
714
+ const template = findDocumentTemplate(key, container);
717
715
 
718
- const nodes = template.createDocumentFragment();
719
- for (const [, node] of Object.entries(nodes.childNodes)) {
720
- if (node instanceof HTMLElement) {
721
- applyRecursive(node, key, path);
722
- node.setAttribute(ATTRIBUTE_UPDATER_INSERT_REFERENCE, ref);
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
- container.appendChild(node);
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
- if (!(node instanceof HTMLElement)) {
740
- return;
741
- }
742
-
743
- if (node.hasAttribute(ATTRIBUTE_UPDATER_REPLACE)) {
744
- const value = node.getAttribute(ATTRIBUTE_UPDATER_REPLACE);
745
- node.setAttribute(
746
- ATTRIBUTE_UPDATER_REPLACE,
747
- value.replaceAll(`path:${key}`, `path:${path}`),
748
- );
749
- }
750
-
751
- if (node.hasAttribute(ATTRIBUTE_UPDATER_ATTRIBUTES)) {
752
- const value = node.getAttribute(ATTRIBUTE_UPDATER_ATTRIBUTES);
753
- node.setAttribute(
754
- ATTRIBUTE_UPDATER_ATTRIBUTES,
755
- value.replaceAll(`path:${key}`, `path:${path}`),
756
- );
757
- }
758
-
759
- for (const [, child] of Object.entries(node.childNodes)) {
760
- if (child instanceof HTMLElement) {
761
- applyRecursive(child, key, path);
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
- const subject = this[internalSymbol].subject.getRealSubject();
776
-
777
- const p = clone(change?.["path"]);
778
- runUpdateContent.call(this, this[internalSymbol].element, p, subject);
779
-
780
- const slots = this[internalSymbol].element.querySelectorAll("slot");
781
- if (slots.length > 0) {
782
- for (const [, slot] of Object.entries(slots)) {
783
- for (const [, element] of Object.entries(slot.assignedNodes())) {
784
- runUpdateContent.call(this, element, p, subject);
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
- if (!isArray(parts)) return;
801
- if (!(container instanceof HTMLElement)) return;
802
- parts = clone(parts);
803
-
804
- const mem = new WeakSet();
805
-
806
- while (parts.length > 0) {
807
- const current = parts.join(".");
808
- parts.pop();
809
-
810
- // Unfortunately, static data is always changed as well, since it is not possible to react to changes here.
811
- const query = `[${ATTRIBUTE_UPDATER_REPLACE}^="path:${current}"], [${ATTRIBUTE_UPDATER_REPLACE}^="static:"], [${ATTRIBUTE_UPDATER_REPLACE}^="i18n:"]`;
812
- const e = container.querySelectorAll(`${query}`);
813
-
814
- const iterator = new Set([...e]);
815
-
816
- if (container.matches(query)) {
817
- iterator.add(container);
818
- }
819
-
820
- /**
821
- * @type {HTMLElement}
822
- */
823
- for (const [element] of iterator.entries()) {
824
- if (mem.has(element)) return;
825
- mem.add(element);
826
-
827
- const attributes = element.getAttribute(ATTRIBUTE_UPDATER_REPLACE);
828
- const cmd = trimSpaces(attributes);
829
-
830
- const pipe = new Pipe(cmd);
831
- this[internalSymbol].callbacks.forEach((f, n) => {
832
- pipe.setCallback(n, f);
833
- });
834
-
835
- let value;
836
- try {
837
- element.removeAttribute(ATTRIBUTE_ERRORMESSAGE);
838
- value = pipe.run(subject);
839
- } catch (e) {
840
- element.setAttribute(ATTRIBUTE_ERRORMESSAGE, e.message);
841
- }
842
-
843
- if (value instanceof HTMLElement) {
844
- while (element.firstChild) {
845
- element.removeChild(element.firstChild);
846
- }
847
-
848
- try {
849
- element.appendChild(value);
850
- } catch (e) {
851
- addErrorAttribute(element, e);
852
- }
853
- } else {
854
- element.innerHTML = value;
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
- const subject = this[internalSymbol].subject.getRealSubject();
868
- const p = clone(change?.["path"]);
869
- runUpdateAttributes.call(this, this[internalSymbol].element, p, subject);
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
- if (!isArray(parts)) return;
882
- parts = clone(parts);
879
+ if (!isArray(parts)) return;
880
+ parts = clone(parts);
883
881
 
884
- const mem = new WeakSet();
882
+ const mem = new WeakSet();
885
883
 
886
- while (parts.length > 0) {
887
- const current = parts.join(".");
888
- parts.pop();
884
+ while (parts.length > 0) {
885
+ const current = parts.join(".");
886
+ parts.pop();
889
887
 
890
- let iterator = new Set();
888
+ let iterator = new Set();
891
889
 
892
- const query = `[${ATTRIBUTE_UPDATER_SELECT_THIS}][${ATTRIBUTE_UPDATER_ATTRIBUTES}], [${ATTRIBUTE_UPDATER_ATTRIBUTES}*="path:${current}"], [${ATTRIBUTE_UPDATER_ATTRIBUTES}^="static:"], [${ATTRIBUTE_UPDATER_ATTRIBUTES}^="i18n:"]`;
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
- const e = container.querySelectorAll(query);
892
+ const e = container.querySelectorAll(query);
895
893
 
896
- if (e.length > 0) {
897
- iterator = new Set([...e]);
898
- }
894
+ if (e.length > 0) {
895
+ iterator = new Set([...e]);
896
+ }
899
897
 
900
- if (container.matches(query)) {
901
- iterator.add(container);
902
- }
898
+ if (container.matches(query)) {
899
+ iterator.add(container);
900
+ }
903
901
 
904
- for (const [element] of iterator.entries()) {
905
- if (mem.has(element)) return;
906
- mem.add(element);
902
+ for (const [element] of iterator.entries()) {
903
+ if (mem.has(element)) return;
904
+ mem.add(element);
907
905
 
908
- // this case occurs when the ATTRIBUTE_UPDATER_SELECT_THIS attribute is set
909
- if (!element.hasAttribute(ATTRIBUTE_UPDATER_ATTRIBUTES)) {
910
- continue;
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
- const attributes = element.getAttribute(ATTRIBUTE_UPDATER_ATTRIBUTES);
911
+ const attributes = element.getAttribute(ATTRIBUTE_UPDATER_ATTRIBUTES);
914
912
 
915
- for (let [, def] of Object.entries(attributes.split(","))) {
916
- def = trimSpaces(def);
917
- const i = def.indexOf(" ");
918
- const name = trimSpaces(def.substr(0, i));
919
- const cmd = trimSpaces(def.substr(i));
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
- const pipe = new Pipe(cmd);
919
+ const pipe = new Pipe(cmd);
922
920
 
923
- this[internalSymbol].callbacks.forEach((f, n) => {
924
- pipe.setCallback(n, f, element);
925
- });
921
+ this[internalSymbol].callbacks.forEach((f, n) => {
922
+ pipe.setCallback(n, f, element);
923
+ });
926
924
 
927
- let value;
928
- try {
929
- element.removeAttribute(ATTRIBUTE_ERRORMESSAGE);
930
- value = pipe.run(subject);
931
- } catch (e) {
932
- addErrorAttribute(element, e);
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
- if (value === undefined) {
936
- element.removeAttribute(name);
937
- } else if (element.getAttribute(name) !== value) {
938
- element.setAttribute(name, value);
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
- handleInputControlAttributeUpdate.call(this, element, name, value);
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
- if (element instanceof HTMLSelectElement) {
958
- switch (element.type) {
959
- case "select-multiple":
960
- for (const [index, opt] of Object.entries(element.options)) {
961
- opt.selected = value.indexOf(opt.value) !== -1;
962
- }
963
-
964
- break;
965
- case "select-one":
966
- // Only one value may be selected
967
-
968
- for (const [index, opt] of Object.entries(element.options)) {
969
- if (opt.value === value) {
970
- element.selectedIndex = index;
971
- break;
972
- }
973
- }
974
-
975
- break;
976
- }
977
- } else if (element instanceof HTMLInputElement) {
978
- switch (element.type) {
979
- case "radio":
980
- if (name === "checked") {
981
- element.checked = value !== undefined;
982
- }
983
- break;
984
-
985
- case "checkbox":
986
- if (name === "checked") {
987
- element.checked = value !== undefined;
988
- }
989
- break;
990
-
991
- case "text":
992
- default:
993
- if (name === "value") {
994
- element.value = value === undefined ? "" : value;
995
- }
996
- break;
997
- }
998
- } else if (element instanceof HTMLTextAreaElement) {
999
- if (name === "value") {
1000
- element.value = value === undefined ? "" : value;
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
- if (!(this instanceof HTMLElement)) {
1022
- throw new TypeError(
1023
- "the context of this function must be an instance of HTMLElement",
1024
- );
1025
- }
1026
-
1027
- if (!(typeof symbol === "symbol")) {
1028
- throw new TypeError("symbol must be an instance of Symbol");
1029
- }
1030
-
1031
- const updaters = new Set();
1032
-
1033
- if (elements instanceof NodeList) {
1034
- elements = new Set([...elements]);
1035
- } else if (elements instanceof HTMLElement) {
1036
- elements = new Set([elements]);
1037
- } else if (elements instanceof Set) {
1038
- } else {
1039
- throw new TypeError(
1040
- `elements is not a valid type. (actual: ${typeof elements})`,
1041
- );
1042
- }
1043
-
1044
- const result = [];
1045
-
1046
- const updaterCallbacks = [];
1047
- const cb = this?.[updaterTransformerMethodsSymbol];
1048
- if (this instanceof HTMLElement && typeof cb === "function") {
1049
- const callbacks = cb.call(this);
1050
- if (typeof callbacks === "object") {
1051
- for (const [name, callback] of Object.entries(callbacks)) {
1052
- if (typeof callback === "function") {
1053
- updaterCallbacks.push([name, callback]);
1054
- } else {
1055
- addErrorAttribute(
1056
- this,
1057
- `onUpdaterPipeCallbacks: ${name} is not a function`,
1058
- );
1059
- }
1060
- }
1061
- } else {
1062
- addErrorAttribute(
1063
- this,
1064
- `onUpdaterPipeCallbacks do not return an object with functions`,
1065
- );
1066
- }
1067
- }
1068
-
1069
- elements.forEach((element) => {
1070
- if (!(element instanceof HTMLElement)) return;
1071
- if (element instanceof HTMLTemplateElement) return;
1072
-
1073
- const u = new Updater(element, object);
1074
- updaters.add(u);
1075
-
1076
- if (updaterCallbacks.length > 0) {
1077
- for (const [name, callback] of updaterCallbacks) {
1078
- u.setCallback(name, callback);
1079
- }
1080
- }
1081
-
1082
- result.push(
1083
- u.run().then(() => {
1084
- if (config.eventProcessing === true) {
1085
- u.enableEventProcessing();
1086
- }
1087
-
1088
- return u;
1089
- }),
1090
- );
1091
- });
1092
-
1093
- if (updaters.size > 0) {
1094
- addToObjectLink(this, symbol, updaters);
1095
- }
1096
-
1097
- return result;
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
  }