@schukai/monster 3.73.7 → 3.73.9

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