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