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