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