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