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