@schukai/monster 3.74.0 → 3.76.0

Sign up to get free protection for your applications and to get access to all the features.
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
  }