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