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