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