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