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