@schukai/monster 3.73.9 → 3.75.0

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