@schukai/monster 4.30.0 → 4.31.0

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