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