@node-projects/web-component-designer-visualization-addons 0.1.152 → 0.3.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/dist/components/BindingsEditor.js +2 -2
- package/dist/components/BindingsEditorHelp.d.ts +1 -1
- package/dist/components/BindingsEditorHelp.js +4 -0
- package/dist/components/EventAssignment.d.ts +1 -0
- package/dist/components/EventAssignment.js +5 -2
- package/dist/helpers/BindingsHelper.d.ts +25 -13
- package/dist/helpers/BindingsHelper.js +602 -605
- package/dist/index-min.js +52 -52
- package/dist/services/VisualizationBindingsRefactorService.js +20 -23
- package/package.json +1 -1
- package/tests/BindingsHelper.test.ts +527 -0
|
@@ -16,25 +16,51 @@ export function isLit(element) {
|
|
|
16
16
|
return element.constructor?.elementProperties != null;
|
|
17
17
|
}
|
|
18
18
|
export function parseBindingString(id) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
let
|
|
19
|
+
const parts = [];
|
|
20
|
+
const signals = [];
|
|
21
|
+
let start = 0;
|
|
22
22
|
for (let n = 0; n < id.length; n++) {
|
|
23
23
|
if (id[n] == '{') {
|
|
24
|
-
parts.push(
|
|
25
|
-
|
|
24
|
+
parts.push(id.substring(start, n));
|
|
25
|
+
start = n + 1;
|
|
26
26
|
}
|
|
27
27
|
else if (id[n] == '}') {
|
|
28
|
-
signals.push(
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
else {
|
|
32
|
-
tx += id[n];
|
|
28
|
+
signals.push(id.substring(start, n));
|
|
29
|
+
start = n + 1;
|
|
33
30
|
}
|
|
34
31
|
}
|
|
35
|
-
parts.push(
|
|
32
|
+
parts.push(id.substring(start));
|
|
36
33
|
return { parts, signals };
|
|
37
34
|
}
|
|
35
|
+
function bindSpecialValue(handler, name, context, valueChanged) {
|
|
36
|
+
let active = true;
|
|
37
|
+
let revision = 0;
|
|
38
|
+
const loadValue = () => {
|
|
39
|
+
const currentRevision = ++revision;
|
|
40
|
+
const value = handler.valueProvider(name, context);
|
|
41
|
+
if (value instanceof Promise)
|
|
42
|
+
value.then(v => active && currentRevision === revision && valueChanged(v));
|
|
43
|
+
else if (active)
|
|
44
|
+
valueChanged(value);
|
|
45
|
+
};
|
|
46
|
+
loadValue();
|
|
47
|
+
if (!handler.valueChangedCallbacks)
|
|
48
|
+
handler.valueChangedCallbacks = new Map();
|
|
49
|
+
let callbacks = handler.valueChangedCallbacks.get(name);
|
|
50
|
+
if (!callbacks) {
|
|
51
|
+
callbacks = [];
|
|
52
|
+
handler.valueChangedCallbacks.set(name, callbacks);
|
|
53
|
+
}
|
|
54
|
+
callbacks.push(loadValue);
|
|
55
|
+
return () => {
|
|
56
|
+
active = false;
|
|
57
|
+
const index = callbacks.indexOf(loadValue);
|
|
58
|
+
if (index >= 0)
|
|
59
|
+
callbacks.splice(index, 1);
|
|
60
|
+
if (callbacks.length === 0 && handler.valueChangedCallbacks.get(name) === callbacks)
|
|
61
|
+
handler.valueChangedCallbacks.delete(name);
|
|
62
|
+
};
|
|
63
|
+
}
|
|
38
64
|
export function getNestedProperty(obj, path) {
|
|
39
65
|
const parts = path.split('.');
|
|
40
66
|
let current = obj;
|
|
@@ -45,100 +71,97 @@ export function getNestedProperty(obj, path) {
|
|
|
45
71
|
}
|
|
46
72
|
return current;
|
|
47
73
|
}
|
|
74
|
+
function observeProperty(bindingsHelper, owner, propertyName, valueChanged, supportsNestedPath = false) {
|
|
75
|
+
const isNested = supportsNestedPath && propertyName.includes('.');
|
|
76
|
+
const readValue = isNested
|
|
77
|
+
? () => getNestedProperty(owner, propertyName)
|
|
78
|
+
: () => owner[propertyName];
|
|
79
|
+
valueChanged(readValue());
|
|
80
|
+
if (isNested)
|
|
81
|
+
return () => { };
|
|
82
|
+
const callback = () => valueChanged(readValue());
|
|
83
|
+
const eventName = bindingsHelper.getChangedEventName(owner, propertyName);
|
|
84
|
+
owner.addEventListener(eventName, callback);
|
|
85
|
+
return () => owner.removeEventListener(eventName, callback);
|
|
86
|
+
}
|
|
87
|
+
function observeAttribute(owner, attributeName, valueChanged) {
|
|
88
|
+
valueChanged(owner.getAttribute(attributeName));
|
|
89
|
+
const observer = new MutationObserver(() => valueChanged(owner.getAttribute(attributeName)));
|
|
90
|
+
observer.observe(owner, { attributes: true, attributeFilter: [attributeName] });
|
|
91
|
+
return () => observer.disconnect();
|
|
92
|
+
}
|
|
48
93
|
class IndirectSignal {
|
|
94
|
+
bindingsHelper;
|
|
49
95
|
parts;
|
|
50
96
|
signals;
|
|
51
97
|
values;
|
|
52
98
|
unsubscribeTargetValue;
|
|
53
99
|
cleanupCalls = [];
|
|
54
100
|
combinedName;
|
|
55
|
-
disposed;
|
|
101
|
+
disposed = false;
|
|
56
102
|
valueChangedCb;
|
|
57
103
|
visualizationHandler;
|
|
58
104
|
element;
|
|
59
105
|
relativeSignalPath;
|
|
60
106
|
constructor(bindingsHelper, visualizationHandler, id, valueChangedCb, element, relativeSignalPath, root, specialValueHandler) {
|
|
107
|
+
this.bindingsHelper = bindingsHelper;
|
|
61
108
|
this.visualizationHandler = visualizationHandler;
|
|
62
109
|
this.valueChangedCb = valueChangedCb;
|
|
63
110
|
this.element = element;
|
|
64
111
|
this.relativeSignalPath = relativeSignalPath;
|
|
65
112
|
this.parseIndirectBinding(id);
|
|
66
113
|
this.values = new Array(this.signals.length);
|
|
67
|
-
|
|
68
|
-
let
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
114
|
+
const bindSignalFromProperty = (owner, propertyName, index) => {
|
|
115
|
+
let currentSignalName;
|
|
116
|
+
let subscriptionCleanup = () => { };
|
|
117
|
+
const switchSignal = (propertyValue) => {
|
|
118
|
+
let signalName = propertyValue;
|
|
119
|
+
if (typeof signalName === 'string' && signalName[0] === '.')
|
|
120
|
+
signalName = visualizationHandler.getNormalizedSignalName(signalName, relativeSignalPath, element);
|
|
121
|
+
if (signalName === currentSignalName)
|
|
122
|
+
return;
|
|
123
|
+
subscriptionCleanup();
|
|
124
|
+
currentSignalName = signalName;
|
|
125
|
+
if (typeof signalName === 'string') {
|
|
126
|
+
const callback = (changedId, value) => this.handleValueChanged(value.val, index);
|
|
127
|
+
subscriptionCleanup = bindingsHelper.subscribeToState(signalName, callback);
|
|
77
128
|
}
|
|
78
129
|
else {
|
|
79
|
-
|
|
80
|
-
this.handleValueChanged(val, i);
|
|
130
|
+
subscriptionCleanup = () => { };
|
|
81
131
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
element
|
|
91
|
-
this.cleanupCalls.push(() =>
|
|
92
|
-
}
|
|
93
|
-
else {
|
|
94
|
-
const val = getNestedProperty(element, propNm);
|
|
95
|
-
this.handleValueChanged(val, i);
|
|
96
|
-
}
|
|
97
|
-
continue;
|
|
98
|
-
}
|
|
99
|
-
else if (nm[0] === '§') {
|
|
100
|
-
const mS = nm.substring(1);
|
|
101
|
-
const value = specialValueHandler.valueProvider(mS, { element, relativeSignalPath, root });
|
|
102
|
-
if (value instanceof Promise)
|
|
103
|
-
value.then(v => this.handleValueChanged(v, i));
|
|
104
|
-
else
|
|
105
|
-
this.handleValueChanged(value, i);
|
|
106
|
-
if (!specialValueHandler.valueChangedCallbacks)
|
|
107
|
-
specialValueHandler.valueChangedCallbacks = new Map();
|
|
108
|
-
let changeList = specialValueHandler.valueChangedCallbacks.get(mS);
|
|
109
|
-
if (changeList == null) {
|
|
110
|
-
changeList = [];
|
|
111
|
-
specialValueHandler.valueChangedCallbacks.set(mS, changeList);
|
|
132
|
+
};
|
|
133
|
+
this.cleanupCalls.push(observeProperty(bindingsHelper, owner, propertyName, switchSignal, true));
|
|
134
|
+
this.cleanupCalls.push(() => subscriptionCleanup());
|
|
135
|
+
};
|
|
136
|
+
try {
|
|
137
|
+
for (let i = 0; i < this.signals.length; i++) {
|
|
138
|
+
const signal = this.signals[i];
|
|
139
|
+
if (signal.startsWith('?@') || signal.startsWith('#@')) {
|
|
140
|
+
const owner = signal[0] === '?' ? root : element;
|
|
141
|
+
this.cleanupCalls.push(observeAttribute(owner, signal.substring(2), value => this.handleValueChanged(value, i)));
|
|
112
142
|
}
|
|
113
|
-
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
value.then(v => this.handleValueChanged(v, i));
|
|
117
|
-
else
|
|
118
|
-
this.handleValueChanged(value, i);
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
else if (nm[0] === '?') {
|
|
122
|
-
//TODO: react to changes of signal name in prop
|
|
123
|
-
if (!nm.includes('.')) {
|
|
124
|
-
nm = root[nm.substring(1)];
|
|
143
|
+
else if (signal.startsWith('??') || signal.startsWith('##') || signal.startsWith('?$') || signal.startsWith('#$')) {
|
|
144
|
+
const owner = signal[0] === '?' ? root : element;
|
|
145
|
+
this.cleanupCalls.push(observeProperty(bindingsHelper, owner, signal.substring(2), value => this.handleValueChanged(value, i), true));
|
|
125
146
|
}
|
|
126
|
-
else {
|
|
127
|
-
|
|
147
|
+
else if (signal[0] === '§') {
|
|
148
|
+
const name = signal.substring(1);
|
|
149
|
+
if (!specialValueHandler)
|
|
150
|
+
throw new Error(`No special value handler registered for "${signal}"`);
|
|
151
|
+
this.cleanupCalls.push(bindSpecialValue(specialValueHandler, name, { element, relativeSignalPath, root }, value => this.handleValueChanged(value, i)));
|
|
128
152
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
//TODO: react to changes of signal name in prop
|
|
132
|
-
if (!nm.includes('.')) {
|
|
133
|
-
nm = root[nm.substring(1)];
|
|
153
|
+
else if (signal[0] === '?' || signal[0] === '#') {
|
|
154
|
+
bindSignalFromProperty(signal[0] === '?' ? root : element, signal.substring(1), i);
|
|
134
155
|
}
|
|
135
156
|
else {
|
|
136
|
-
|
|
157
|
+
const callback = (changedId, value) => this.handleValueChanged(value.val, i);
|
|
158
|
+
this.cleanupCalls.push(bindingsHelper.subscribeToState(signal, callback));
|
|
137
159
|
}
|
|
138
160
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
this.
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
this.dispose();
|
|
164
|
+
throw error;
|
|
142
165
|
}
|
|
143
166
|
}
|
|
144
167
|
parseIndirectBinding(id) {
|
|
@@ -151,6 +174,8 @@ class IndirectSignal {
|
|
|
151
174
|
}
|
|
152
175
|
}
|
|
153
176
|
handleValueChanged(value, index) {
|
|
177
|
+
if (this.disposed)
|
|
178
|
+
return;
|
|
154
179
|
this.values[index] = value;
|
|
155
180
|
let nm = this.parts[0];
|
|
156
181
|
for (let i = 0; i < this.parts.length - 1; i++) {
|
|
@@ -163,27 +188,27 @@ class IndirectSignal {
|
|
|
163
188
|
nm = this.visualizationHandler.getNormalizedSignalName(nm, this.relativeSignalPath, this.element);
|
|
164
189
|
if (this.combinedName != nm) {
|
|
165
190
|
if (this.unsubscribeTargetValue) {
|
|
166
|
-
this.
|
|
191
|
+
this.unsubscribeTargetValue();
|
|
167
192
|
}
|
|
168
193
|
if (!this.disposed) {
|
|
169
194
|
this.combinedName = nm;
|
|
170
|
-
|
|
171
|
-
this.unsubscribeTargetValue =
|
|
195
|
+
const cb = (id, value) => this.valueChangedCb(value);
|
|
196
|
+
this.unsubscribeTargetValue = this.bindingsHelper.subscribeToState(nm, cb);
|
|
172
197
|
}
|
|
173
198
|
}
|
|
174
199
|
}
|
|
175
200
|
dispose() {
|
|
176
201
|
this.disposed = true;
|
|
177
202
|
if (this.unsubscribeTargetValue) {
|
|
178
|
-
this.
|
|
203
|
+
this.unsubscribeTargetValue();
|
|
179
204
|
this.unsubscribeTargetValue = null;
|
|
180
205
|
}
|
|
181
|
-
for (
|
|
182
|
-
|
|
183
|
-
|
|
206
|
+
for (const cleanup of this.cleanupCalls)
|
|
207
|
+
cleanup();
|
|
208
|
+
this.cleanupCalls.length = 0;
|
|
184
209
|
}
|
|
185
210
|
setState(value) {
|
|
186
|
-
if (!this.disposed) {
|
|
211
|
+
if (!this.disposed && this.combinedName) {
|
|
187
212
|
this.visualizationHandler.setState(this.combinedName, value);
|
|
188
213
|
}
|
|
189
214
|
}
|
|
@@ -191,20 +216,41 @@ class IndirectSignal {
|
|
|
191
216
|
export class BindingsHelper {
|
|
192
217
|
_visualizationHandler;
|
|
193
218
|
namedConverterCallback;
|
|
219
|
+
_stateSubscriptions = new Map();
|
|
220
|
+
_scheduledUpdates = new Set();
|
|
221
|
+
_updateFlushScheduled = false;
|
|
222
|
+
_compiledConverters = new WeakMap();
|
|
194
223
|
constructor(visualizationHandler) {
|
|
195
224
|
this._visualizationHandler = visualizationHandler;
|
|
196
225
|
}
|
|
226
|
+
scheduleUpdate(update) {
|
|
227
|
+
this._scheduledUpdates.add(update);
|
|
228
|
+
if (this._updateFlushScheduled)
|
|
229
|
+
return;
|
|
230
|
+
this._updateFlushScheduled = true;
|
|
231
|
+
queueMicrotask(() => {
|
|
232
|
+
this._updateFlushScheduled = false;
|
|
233
|
+
const updates = [...this._scheduledUpdates];
|
|
234
|
+
this._scheduledUpdates.clear();
|
|
235
|
+
for (const scheduledUpdate of updates)
|
|
236
|
+
scheduledUpdate();
|
|
237
|
+
});
|
|
238
|
+
}
|
|
197
239
|
getChangedEventName(element, propertyName) {
|
|
198
240
|
const posColon = propertyName.indexOf('::');
|
|
199
241
|
if (posColon >= 0)
|
|
200
242
|
return propertyName.substring(posColon + 2);
|
|
201
|
-
if (element instanceof HTMLInputElement)
|
|
202
|
-
return 'change';
|
|
203
|
-
if (element instanceof HTMLSelectElement)
|
|
243
|
+
if (element instanceof HTMLInputElement || element instanceof HTMLSelectElement)
|
|
204
244
|
return 'change';
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
245
|
+
const eventName = PropertiesHelper.camelToDashCase(propertyName);
|
|
246
|
+
return isLit(element) ? eventName : eventName + '-changed';
|
|
247
|
+
}
|
|
248
|
+
getParsedTargetName(propertyName, bindingTarget) {
|
|
249
|
+
if (bindingTarget === BindingTarget.cssvar || bindingTarget === BindingTarget.class)
|
|
250
|
+
return BindingsHelper.dotToCamelCase(propertyName);
|
|
251
|
+
if (bindingTarget === BindingTarget.attribute)
|
|
252
|
+
return propertyName;
|
|
253
|
+
return PropertiesHelper.dashToCamelCase(propertyName);
|
|
208
254
|
}
|
|
209
255
|
//Not allowed chars in Var Names: |{}(),;:[]
|
|
210
256
|
parseBinding(element, name, value, bindingTarget, prefix) {
|
|
@@ -232,20 +278,11 @@ export class BindingsHelper {
|
|
|
232
278
|
}
|
|
233
279
|
binding.twoWay = true;
|
|
234
280
|
if (!binding.events) {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
binding.
|
|
239
|
-
|
|
240
|
-
if (isLit(element)) {
|
|
241
|
-
binding.events = [this.getChangedEventName(element, propname)];
|
|
242
|
-
}
|
|
243
|
-
else {
|
|
244
|
-
binding.events = [this.getChangedEventName(element, propname)];
|
|
245
|
-
//Binding could be a lit elemnt but not yet loaded
|
|
246
|
-
binding.maybeLitElement = true;
|
|
247
|
-
binding.litEventNames = [this.getChangedEventName(element, propname)];
|
|
248
|
-
}
|
|
281
|
+
binding.events = [this.getChangedEventName(element, propname)];
|
|
282
|
+
if (!(element instanceof HTMLInputElement) && !(element instanceof HTMLSelectElement) && !isLit(element)) {
|
|
283
|
+
//Binding could be a lit element but not yet loaded
|
|
284
|
+
binding.maybeLitElement = true;
|
|
285
|
+
binding.litEventNames = binding.events;
|
|
249
286
|
}
|
|
250
287
|
}
|
|
251
288
|
}
|
|
@@ -258,28 +295,13 @@ export class BindingsHelper {
|
|
|
258
295
|
binding.expression = parts.pop();
|
|
259
296
|
binding.signal = parts.join(';');
|
|
260
297
|
}
|
|
261
|
-
|
|
262
|
-
return [BindingsHelper.dotToCamelCase(propname), binding];
|
|
263
|
-
if (bindingTarget === BindingTarget.attribute)
|
|
264
|
-
return [propname, binding];
|
|
265
|
-
return [PropertiesHelper.dashToCamelCase(propname), binding];
|
|
298
|
+
return [this.getParsedTargetName(propname, bindingTarget), binding];
|
|
266
299
|
}
|
|
267
300
|
let binding = JSON.parse(value);
|
|
268
301
|
binding.target = bindingTarget;
|
|
269
|
-
if (binding.twoWay && (binding.events == null || binding.events.length == 0))
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
else if (element instanceof HTMLSelectElement)
|
|
273
|
-
binding.events = ['change'];
|
|
274
|
-
else {
|
|
275
|
-
binding.events = [this.getChangedEventName(element, propname)];
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
if (bindingTarget === BindingTarget.cssvar || bindingTarget === BindingTarget.class)
|
|
279
|
-
return [BindingsHelper.dotToCamelCase(propname), binding];
|
|
280
|
-
if (bindingTarget === BindingTarget.attribute)
|
|
281
|
-
return [propname, binding];
|
|
282
|
-
return [PropertiesHelper.dashToCamelCase(propname), binding];
|
|
302
|
+
if (binding.twoWay && (binding.events == null || binding.events.length == 0))
|
|
303
|
+
binding.events = [this.getChangedEventName(element, propname)];
|
|
304
|
+
return [this.getParsedTargetName(propname, bindingTarget), binding];
|
|
283
305
|
}
|
|
284
306
|
serializeBinding(element, targetName, binding) {
|
|
285
307
|
let bindingCopy = { ...binding };
|
|
@@ -289,9 +311,7 @@ export class BindingsHelper {
|
|
|
289
311
|
delete bindingCopy.expressionTwoWay;
|
|
290
312
|
}
|
|
291
313
|
else if ((binding.events != null && binding.events.length == 1)) {
|
|
292
|
-
if (element instanceof HTMLInputElement && binding.events?.[0] == "change")
|
|
293
|
-
delete bindingCopy.events;
|
|
294
|
-
else if (element instanceof HTMLSelectElement && binding.events?.[0] == "change")
|
|
314
|
+
if ((element instanceof HTMLInputElement || element instanceof HTMLSelectElement) && binding.events?.[0] == "change")
|
|
295
315
|
delete bindingCopy.events;
|
|
296
316
|
else if (isLit(element) && binding.events?.[0] == targetName)
|
|
297
317
|
delete bindingCopy.events;
|
|
@@ -299,108 +319,28 @@ export class BindingsHelper {
|
|
|
299
319
|
delete bindingCopy.events;
|
|
300
320
|
}
|
|
301
321
|
const eventsString = bindingCopy.twoWay && bindingCopy.events?.length > 0 ? '::' + bindingCopy.events.join(',') : '';
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
if (targetName == 'innerHTML')
|
|
316
|
-
return [bindingPrefixContent + 'html', (binding.twoWay ? '=' : '') + (binding.inverted ? '!' : '') + binding.signal + (!binding.twoWay && binding.signal.includes(';') ? ';' : '') + eventsString];
|
|
317
|
-
return [bindingPrefixProperty + PropertiesHelper.camelToDashCase(targetName), (binding.twoWay ? '=' : '') + (binding.inverted ? '!' : '') + binding.signal + (!binding.twoWay && binding.signal.includes(';') ? ';' : '') + eventsString];
|
|
318
|
-
}
|
|
319
|
-
//Multi Var Expressions
|
|
320
|
-
if (!needsJson && binding.target == BindingTarget.property &&
|
|
321
|
-
binding.expression && !binding.expression.includes("\n") && !binding.expression.includes(";") &&
|
|
322
|
-
!binding.expressionTwoWay &&
|
|
323
|
-
binding.converter == null &&
|
|
324
|
-
//!binding.type &&
|
|
325
|
-
!binding.historic &&
|
|
326
|
-
!binding.writeBackSignal) {
|
|
327
|
-
if (targetName == 'textContent')
|
|
328
|
-
return [bindingPrefixContent + 'text', (binding.inverted ? '!' : '') + binding.signal + ';' + binding.expression + eventsString];
|
|
329
|
-
if (targetName == 'innerHTML')
|
|
330
|
-
return [bindingPrefixContent + 'html', (binding.inverted ? '!' : '') + binding.signal + ';' + binding.expression + eventsString];
|
|
331
|
-
return [bindingPrefixProperty + PropertiesHelper.camelToDashCase(targetName), (binding.twoWay ? '=' : '') + (binding.inverted ? '!' : '') + binding.signal + ';' + binding.expression + eventsString];
|
|
332
|
-
}
|
|
333
|
-
if (!needsJson && binding.target == BindingTarget.attribute &&
|
|
334
|
-
!binding.expression && !binding.expressionTwoWay &&
|
|
335
|
-
binding.converter == null &&
|
|
336
|
-
//!binding.type &&
|
|
337
|
-
!binding.historic &&
|
|
338
|
-
!binding.writeBackSignal) {
|
|
339
|
-
return [bindingPrefixAttribute + PropertiesHelper.camelToDashCase(targetName), (binding.twoWay ? '=' : '') + (binding.inverted ? '!' : '') + binding.signal + (!binding.twoWay && binding.signal.includes(';') ? ';' : '') + eventsString];
|
|
340
|
-
}
|
|
341
|
-
//Multi Var Expressions
|
|
342
|
-
if (!needsJson && binding.target == BindingTarget.attribute &&
|
|
343
|
-
binding.expression && !binding.expression.includes("\n") && !binding.expression.includes(";") &&
|
|
344
|
-
!binding.expressionTwoWay &&
|
|
345
|
-
binding.converter == null &&
|
|
346
|
-
//!binding.type &&
|
|
347
|
-
!binding.historic &&
|
|
348
|
-
!binding.writeBackSignal) {
|
|
349
|
-
return [bindingPrefixAttribute + PropertiesHelper.camelToDashCase(targetName), (binding.twoWay ? '=' : '') + (binding.inverted ? '!' : '') + binding.signal + ';' + binding.expression + eventsString];
|
|
350
|
-
}
|
|
351
|
-
if (!needsJson && binding.target == BindingTarget.class &&
|
|
352
|
-
!binding.expression && !binding.expressionTwoWay &&
|
|
353
|
-
binding.converter == null &&
|
|
354
|
-
//!binding.type &&
|
|
355
|
-
!binding.historic &&
|
|
356
|
-
!binding.writeBackSignal) {
|
|
357
|
-
return [bindingPrefixClass + PropertiesHelper.camelToDashCase(targetName), (binding.inverted ? '!' : '') + binding.signal + (!binding.twoWay && binding.signal.includes(';') ? ';' : '') + eventsString];
|
|
322
|
+
const needsJson = ((eventsString !== '') && !!binding.expression?.includes('::')) ||
|
|
323
|
+
!!binding.expressionTwoWay?.includes('::') || binding.signal.trim()[0] == '{';
|
|
324
|
+
const supportsShortForm = binding.target == BindingTarget.property ||
|
|
325
|
+
binding.target == BindingTarget.attribute || binding.target == BindingTarget.class ||
|
|
326
|
+
binding.target == BindingTarget.css || binding.target == BindingTarget.cssvar;
|
|
327
|
+
const hasOnlyShortFormOptions = !needsJson && supportsShortForm && !binding.expressionTwoWay &&
|
|
328
|
+
binding.converter == null && !binding.historic && !binding.writeBackSignal;
|
|
329
|
+
if (hasOnlyShortFormOptions && !binding.expression) {
|
|
330
|
+
const name = this.getShortBindingAttributeName(targetName, binding.target, false);
|
|
331
|
+
const supportsTwoWay = binding.target == BindingTarget.property || binding.target == BindingTarget.attribute;
|
|
332
|
+
const value = (supportsTwoWay && binding.twoWay ? '=' : '') + (binding.inverted ? '!' : '') +
|
|
333
|
+
binding.signal + (!binding.twoWay && binding.signal.includes(';') ? ';' : '') + eventsString;
|
|
334
|
+
return [name, value];
|
|
358
335
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
return [bindingPrefixClass + PropertiesHelper.camelToDashCase(targetName), (binding.inverted ? '!' : '') + binding.signal + ';' + binding.expression + eventsString];
|
|
368
|
-
}
|
|
369
|
-
if (!needsJson && binding.target == BindingTarget.css &&
|
|
370
|
-
!binding.expression && !binding.expressionTwoWay &&
|
|
371
|
-
binding.converter == null &&
|
|
372
|
-
//!binding.type &&
|
|
373
|
-
!binding.historic &&
|
|
374
|
-
!binding.writeBackSignal) {
|
|
375
|
-
return [bindingPrefixCss + PropertiesHelper.camelToDashCase(targetName), (binding.inverted ? '!' : '') + binding.signal + (!binding.twoWay && binding.signal.includes(';') ? ';' : '') + eventsString];
|
|
376
|
-
}
|
|
377
|
-
//Multi Var Expressions
|
|
378
|
-
if (!needsJson && binding.target == BindingTarget.css &&
|
|
379
|
-
binding.expression && !binding.expression.includes("\n") && !binding.expression.includes(";") &&
|
|
380
|
-
!binding.expressionTwoWay &&
|
|
381
|
-
binding.converter == null &&
|
|
382
|
-
//!binding.type &&
|
|
383
|
-
!binding.historic &&
|
|
384
|
-
!binding.writeBackSignal) {
|
|
385
|
-
return [bindingPrefixCss + PropertiesHelper.camelToDashCase(targetName), (binding.inverted ? '!' : '') + binding.signal + ';' + binding.expression + eventsString];
|
|
386
|
-
}
|
|
387
|
-
if (!needsJson && binding.target == BindingTarget.cssvar &&
|
|
388
|
-
!binding.expression && !binding.expressionTwoWay &&
|
|
389
|
-
binding.converter == null &&
|
|
390
|
-
//!binding.type &&
|
|
391
|
-
!binding.historic &&
|
|
392
|
-
!binding.writeBackSignal) {
|
|
393
|
-
return [bindingPrefixCssVar + BindingsHelper.camelToDotCase(targetName.substring(2)), (binding.inverted ? '!' : '') + binding.signal + (!binding.twoWay && binding.signal.includes(';') ? ';' : '') + eventsString];
|
|
394
|
-
}
|
|
395
|
-
//Multi Var Expressions
|
|
396
|
-
if (!needsJson && binding.target == BindingTarget.cssvar &&
|
|
397
|
-
binding.expression && !binding.expression.includes("\n") && !binding.expression.includes(";") &&
|
|
398
|
-
!binding.expressionTwoWay &&
|
|
399
|
-
binding.converter == null &&
|
|
400
|
-
//!binding.type &&
|
|
401
|
-
!binding.historic &&
|
|
402
|
-
!binding.writeBackSignal) {
|
|
403
|
-
return [bindingPrefixCssVar + PropertiesHelper.camelToDashCase(targetName), (binding.inverted ? '!' : '') + binding.signal + ';' + binding.expression + eventsString];
|
|
336
|
+
if (hasOnlyShortFormOptions && binding.expression &&
|
|
337
|
+
!binding.expression.includes("\n") && !binding.expression.includes(";")) {
|
|
338
|
+
const name = this.getShortBindingAttributeName(targetName, binding.target, true);
|
|
339
|
+
const supportsTwoWay = binding.target == BindingTarget.attribute ||
|
|
340
|
+
(binding.target == BindingTarget.property && targetName != 'textContent' && targetName != 'innerHTML');
|
|
341
|
+
const value = (supportsTwoWay && binding.twoWay ? '=' : '') + (binding.inverted ? '!' : '') +
|
|
342
|
+
binding.signal + ';' + binding.expression + eventsString;
|
|
343
|
+
return [name, value];
|
|
404
344
|
}
|
|
405
345
|
if (binding.inverted === null || binding.inverted === false) {
|
|
406
346
|
delete bindingCopy.inverted;
|
|
@@ -421,21 +361,39 @@ export class BindingsHelper {
|
|
|
421
361
|
if (!binding.historic) {
|
|
422
362
|
delete bindingCopy.historic;
|
|
423
363
|
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
364
|
+
return [this.getJsonBindingAttributeName(targetName, binding.target), JSON.stringify(bindingCopy)];
|
|
365
|
+
}
|
|
366
|
+
getShortBindingAttributeName(targetName, target, hasExpression) {
|
|
367
|
+
if (target == BindingTarget.property && targetName == 'textContent')
|
|
368
|
+
return bindingPrefixContent + 'text';
|
|
369
|
+
if (target == BindingTarget.property && targetName == 'innerHTML')
|
|
370
|
+
return bindingPrefixContent + 'html';
|
|
371
|
+
if (target == BindingTarget.attribute)
|
|
372
|
+
return bindingPrefixAttribute + PropertiesHelper.camelToDashCase(targetName);
|
|
373
|
+
if (target == BindingTarget.class)
|
|
374
|
+
return bindingPrefixClass + PropertiesHelper.camelToDashCase(targetName);
|
|
375
|
+
if (target == BindingTarget.css)
|
|
376
|
+
return bindingPrefixCss + PropertiesHelper.camelToDashCase(targetName);
|
|
377
|
+
if (target == BindingTarget.cssvar)
|
|
378
|
+
return hasExpression
|
|
379
|
+
? bindingPrefixCssVar + PropertiesHelper.camelToDashCase(targetName)
|
|
380
|
+
: bindingPrefixCssVar + BindingsHelper.camelToDotCase(targetName.substring(2));
|
|
381
|
+
return bindingPrefixProperty + PropertiesHelper.camelToDashCase(targetName);
|
|
382
|
+
}
|
|
383
|
+
getJsonBindingAttributeName(targetName, target) {
|
|
384
|
+
if (target == BindingTarget.content || (target == BindingTarget.property && targetName == 'innerHTML'))
|
|
385
|
+
return bindingPrefixContent + 'html';
|
|
386
|
+
if (target == BindingTarget.property && targetName == 'textContent')
|
|
387
|
+
return bindingPrefixContent + 'text';
|
|
388
|
+
if (target == BindingTarget.attribute)
|
|
389
|
+
return bindingPrefixAttribute + PropertiesHelper.camelToDashCase(targetName);
|
|
390
|
+
if (target == BindingTarget.class)
|
|
391
|
+
return bindingPrefixClass + BindingsHelper.camelToDotCase(targetName);
|
|
392
|
+
if (target == BindingTarget.css)
|
|
393
|
+
return bindingPrefixCss + PropertiesHelper.camelToDashCase(targetName);
|
|
394
|
+
if (target == BindingTarget.cssvar)
|
|
395
|
+
return bindingPrefixCssVar + BindingsHelper.camelToDotCase(targetName.substring(2));
|
|
396
|
+
return bindingPrefixProperty + PropertiesHelper.camelToDashCase(targetName);
|
|
439
397
|
}
|
|
440
398
|
getBindingAttributeName(element, propertyName, propertyTarget) {
|
|
441
399
|
if (propertyTarget == BindingTarget.attribute) {
|
|
@@ -497,12 +455,20 @@ export class BindingsHelper {
|
|
|
497
455
|
for (let b of bindings) {
|
|
498
456
|
try {
|
|
499
457
|
let applied = this.applyBinding(e, b, relativeSignalPath, root, specialValueHandler);
|
|
500
|
-
|
|
458
|
+
let bindingDisposed = false;
|
|
459
|
+
const disposeBinding = () => {
|
|
460
|
+
if (!bindingDisposed) {
|
|
461
|
+
bindingDisposed = true;
|
|
462
|
+
applied();
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
retVal.push(disposeBinding);
|
|
501
466
|
if (b[1].maybeLitElement && e.localName.includes('-') && !customElements.get(e.localName)) {
|
|
502
467
|
const el = e;
|
|
503
468
|
const bnd = b;
|
|
504
469
|
customElements.whenDefined(e.localName).then(() => {
|
|
505
|
-
if (isLit(el)) {
|
|
470
|
+
if (!bindingDisposed && isLit(el)) {
|
|
471
|
+
bindingDisposed = true;
|
|
506
472
|
applied();
|
|
507
473
|
bnd[1].events = bnd[1].litEventNames;
|
|
508
474
|
retVal.push(this.applyBinding(el, bnd, relativeSignalPath, root, specialValueHandler));
|
|
@@ -534,9 +500,11 @@ export class BindingsHelper {
|
|
|
534
500
|
}
|
|
535
501
|
static #cssBindingsVarId = 0;
|
|
536
502
|
async parseCssBindings(sheet, element, relativeSignalPath, root) {
|
|
503
|
+
if (!sheet.includes(bindingPrefixInsideCss))
|
|
504
|
+
return [cssFromString(sheet), []];
|
|
537
505
|
const parser = (await import("@node-projects/css-parser"));
|
|
538
506
|
const ast = parser.parse(sheet);
|
|
539
|
-
|
|
507
|
+
const unsub = [];
|
|
540
508
|
for (let r of ast.stylesheet.rules) {
|
|
541
509
|
if (r.type === parser.CssTypes.rule) {
|
|
542
510
|
for (const d of r.declarations) {
|
|
@@ -544,12 +512,7 @@ export class BindingsHelper {
|
|
|
544
512
|
if (d.value.includes(bindingPrefixInsideCss)) {
|
|
545
513
|
const newValue = this.parseCssBinding(d.value, element, relativeSignalPath, root);
|
|
546
514
|
d.value = newValue[0];
|
|
547
|
-
|
|
548
|
-
unsub.push(...newValue[1]);
|
|
549
|
-
}
|
|
550
|
-
else {
|
|
551
|
-
unsub = newValue[1];
|
|
552
|
-
}
|
|
515
|
+
unsub.push(...newValue[1]);
|
|
553
516
|
}
|
|
554
517
|
}
|
|
555
518
|
}
|
|
@@ -570,8 +533,10 @@ export class BindingsHelper {
|
|
|
570
533
|
for (let n = 0; n < value.length; n++) {
|
|
571
534
|
const c = value[n];
|
|
572
535
|
if (inBind) {
|
|
573
|
-
if (escape)
|
|
536
|
+
if (escape) {
|
|
574
537
|
binding += c;
|
|
538
|
+
escape = false;
|
|
539
|
+
}
|
|
575
540
|
else if (quote && c === '\\')
|
|
576
541
|
escape = true;
|
|
577
542
|
else if (c === quote)
|
|
@@ -618,258 +583,327 @@ export class BindingsHelper {
|
|
|
618
583
|
}
|
|
619
584
|
return [res + tmp, unsub];
|
|
620
585
|
}
|
|
586
|
+
/** @internal Shared fan-out avoids duplicate backend subscriptions and initial reads. */
|
|
587
|
+
subscribeToState(id, callback) {
|
|
588
|
+
let entry = this._stateSubscriptions.get(id);
|
|
589
|
+
if (entry) {
|
|
590
|
+
entry.callbacks.add(callback);
|
|
591
|
+
if (entry.hasValue) {
|
|
592
|
+
const value = entry.value;
|
|
593
|
+
queueMicrotask(() => entry.callbacks.has(callback) && callback(id, value));
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
else {
|
|
597
|
+
entry = {
|
|
598
|
+
callbacks: new Set([callback]),
|
|
599
|
+
callback: null,
|
|
600
|
+
subscriptionResult: null,
|
|
601
|
+
revision: 0,
|
|
602
|
+
hasValue: false
|
|
603
|
+
};
|
|
604
|
+
entry.callback = (changedId, value) => {
|
|
605
|
+
entry.revision++;
|
|
606
|
+
entry.hasValue = true;
|
|
607
|
+
entry.value = value;
|
|
608
|
+
for (const cb of entry.callbacks)
|
|
609
|
+
cb(changedId, value);
|
|
610
|
+
};
|
|
611
|
+
this._stateSubscriptions.set(id, entry);
|
|
612
|
+
try {
|
|
613
|
+
entry.subscriptionResult = this._visualizationHandler.subscribeState(id, entry.callback);
|
|
614
|
+
const initialRevision = entry.revision;
|
|
615
|
+
this._visualizationHandler.getState(id).then(value => {
|
|
616
|
+
if (this._stateSubscriptions.get(id) === entry && entry.revision === initialRevision)
|
|
617
|
+
entry.callback(id, value);
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
catch (error) {
|
|
621
|
+
this._stateSubscriptions.delete(id);
|
|
622
|
+
if (entry.subscriptionResult != null)
|
|
623
|
+
this._visualizationHandler.unsubscribeState(id, entry.callback, entry.subscriptionResult);
|
|
624
|
+
throw error;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
let active = true;
|
|
628
|
+
return () => {
|
|
629
|
+
if (!active)
|
|
630
|
+
return;
|
|
631
|
+
active = false;
|
|
632
|
+
entry.callbacks.delete(callback);
|
|
633
|
+
if (entry.callbacks.size === 0 && this._stateSubscriptions.get(id) === entry) {
|
|
634
|
+
this._stateSubscriptions.delete(id);
|
|
635
|
+
this._visualizationHandler.unsubscribeState(id, entry.callback, entry.subscriptionResult);
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
}
|
|
621
639
|
/**
|
|
622
|
-
* ? =
|
|
623
|
-
*
|
|
624
|
-
*
|
|
625
|
-
*
|
|
626
|
-
*
|
|
627
|
-
|
|
628
|
-
* @param relativeSignalPath
|
|
629
|
-
* @param root
|
|
630
|
-
* @returns
|
|
631
|
-
*/
|
|
640
|
+
* ? = signal name from a root property; ?? = a root property value
|
|
641
|
+
* # = signal name from a target property; ## = a target property value
|
|
642
|
+
* ?$ = signal object from a root property; #$ = signal object from a target property
|
|
643
|
+
* ?@ = a root attribute value; #@ = a target attribute value
|
|
644
|
+
* $ = a signal configuration; § = a special value
|
|
645
|
+
*/
|
|
632
646
|
applyBinding(element, binding, relativeSignalPath, root, specialValueHandler) {
|
|
633
|
-
|
|
634
|
-
let
|
|
647
|
+
const cleanupCalls = [];
|
|
648
|
+
let active = true;
|
|
635
649
|
const cleanUp = () => {
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
}
|
|
650
|
+
if (!active)
|
|
651
|
+
return;
|
|
652
|
+
active = false;
|
|
653
|
+
this._scheduledUpdates.delete(evaluateAndApply);
|
|
654
|
+
for (let i = cleanupCalls.length - 1; i >= 0; i--)
|
|
655
|
+
cleanupCalls[i]();
|
|
656
|
+
cleanupCalls.length = 0;
|
|
644
657
|
};
|
|
658
|
+
const addCleanup = (cleanup) => cleanupCalls.push(cleanup);
|
|
645
659
|
const signals = binding[1].signal.split(';');
|
|
646
660
|
const signalVars = new Array(signals.length);
|
|
647
661
|
for (let i = 0; i < signals.length; i++) {
|
|
648
|
-
let
|
|
662
|
+
let signal = signals[i];
|
|
649
663
|
signalVars[i] = '__' + i;
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
signalVars[i] =
|
|
653
|
-
|
|
654
|
-
signals[i] =
|
|
664
|
+
const aliasSeparator = signal.indexOf(':');
|
|
665
|
+
if (aliasSeparator >= 0) {
|
|
666
|
+
signalVars[i] = signal.substring(0, aliasSeparator);
|
|
667
|
+
signal = signal.substring(aliasSeparator + 1);
|
|
668
|
+
signals[i] = signal;
|
|
655
669
|
}
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
670
|
+
}
|
|
671
|
+
const values = new Array(signals.length);
|
|
672
|
+
const expressionArguments = [...signalVars, '__res', '__ctx'];
|
|
673
|
+
if (binding[1].expression && !binding[1].compiledExpression) {
|
|
674
|
+
binding[1].compiledExpression = binding[1].expression.includes('return ')
|
|
675
|
+
? new Function(expressionArguments, binding[1].expression)
|
|
676
|
+
: new Function(expressionArguments, 'return ' + binding[1].expression);
|
|
677
|
+
}
|
|
678
|
+
const compiledConverter = this.getCompiledConverter(binding[1], expressionArguments);
|
|
679
|
+
const context = { element, root, boundNames: binding[1].signal, boundTargetName: binding[0], boundTargetType: binding[1].target };
|
|
680
|
+
const writeTarget = this.createTargetWriter(element, binding);
|
|
681
|
+
let writeBackSignal = binding[1].writeBackSignal;
|
|
682
|
+
if (writeBackSignal?.[0] === '.')
|
|
683
|
+
writeBackSignal = relativeSignalPath + writeBackSignal;
|
|
684
|
+
let currentValue;
|
|
685
|
+
let previousResult;
|
|
686
|
+
let lastOutput;
|
|
687
|
+
let hasOutput = false;
|
|
688
|
+
let hasApplied = false;
|
|
689
|
+
let scheduled = false;
|
|
690
|
+
const evaluateAndApply = () => {
|
|
691
|
+
scheduled = false;
|
|
692
|
+
if (!active)
|
|
693
|
+
return;
|
|
694
|
+
let value = currentValue;
|
|
695
|
+
if (binding[1].compiledExpression) {
|
|
696
|
+
value = binding[1].compiledExpression(...values, previousResult, context);
|
|
697
|
+
previousResult = value;
|
|
679
698
|
}
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
699
|
+
if (binding[1].converter) {
|
|
700
|
+
if (typeof binding[1].converter === 'string')
|
|
701
|
+
value = this.namedConverterCallback(binding[1].converter, value, element, binding);
|
|
702
|
+
else
|
|
703
|
+
value = this.applyCompiledConverter(value, compiledConverter, values, previousResult, context, binding[1].converterDefault);
|
|
704
|
+
}
|
|
705
|
+
if (binding[1].inverted)
|
|
706
|
+
value = !value;
|
|
707
|
+
hasApplied = true;
|
|
708
|
+
const canCompareByValue = value === null || typeof value !== 'object';
|
|
709
|
+
if (hasOutput && canCompareByValue && Object.is(lastOutput, value))
|
|
710
|
+
return;
|
|
711
|
+
hasOutput = true;
|
|
712
|
+
lastOutput = value;
|
|
713
|
+
if (writeBackSignal)
|
|
714
|
+
this._visualizationHandler.setState(writeBackSignal, value, true);
|
|
715
|
+
writeTarget(value);
|
|
716
|
+
};
|
|
717
|
+
const updateValue = (value, index, noParse) => {
|
|
718
|
+
if (!active)
|
|
719
|
+
return;
|
|
720
|
+
if (!noParse && index === 0)
|
|
721
|
+
value = BindingsHelper.parseValueWithType(value, binding);
|
|
722
|
+
values[index] = value;
|
|
723
|
+
currentValue = value;
|
|
724
|
+
// Preserve immediate initialization for the common one-signal case, then
|
|
725
|
+
// collapse bursts and multi-signal initialization into one microtask.
|
|
726
|
+
if (!hasApplied && signals.length === 1) {
|
|
727
|
+
evaluateAndApply();
|
|
701
728
|
}
|
|
702
|
-
if (
|
|
703
|
-
|
|
729
|
+
else if (!scheduled) {
|
|
730
|
+
scheduled = true;
|
|
731
|
+
this.scheduleUpdate(evaluateAndApply);
|
|
704
732
|
}
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
if (
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
733
|
+
};
|
|
734
|
+
const setters = new Array(signals.length);
|
|
735
|
+
const setSetter = (index, setter) => {
|
|
736
|
+
setters[index] = setter;
|
|
737
|
+
return () => {
|
|
738
|
+
if (setters[index] === setter)
|
|
739
|
+
setters[index] = null;
|
|
740
|
+
};
|
|
741
|
+
};
|
|
742
|
+
const bindPropertyValue = (owner, propertyName, index) => {
|
|
743
|
+
const setterCleanup = setSetter(index, value => owner[propertyName] = value);
|
|
744
|
+
const propertyCleanup = observeProperty(this, owner, propertyName, value => updateValue(value, index, false));
|
|
745
|
+
return () => {
|
|
746
|
+
propertyCleanup();
|
|
747
|
+
setterCleanup();
|
|
748
|
+
};
|
|
749
|
+
};
|
|
750
|
+
const bindAttributeValue = (owner, attributeName, index) => {
|
|
751
|
+
const setterCleanup = setSetter(index, value => value == null
|
|
752
|
+
? owner.removeAttribute(attributeName)
|
|
753
|
+
: owner.setAttribute(attributeName, value));
|
|
754
|
+
const attributeCleanup = observeAttribute(owner, attributeName, value => updateValue(value, index, false));
|
|
755
|
+
return () => {
|
|
756
|
+
attributeCleanup();
|
|
757
|
+
setterCleanup();
|
|
758
|
+
};
|
|
759
|
+
};
|
|
760
|
+
const bindResolvedSource = (source, index) => {
|
|
761
|
+
if (source.startsWith('?@') || source.startsWith('#@')) {
|
|
762
|
+
const owner = source[0] === '?' ? root : element;
|
|
763
|
+
return owner ? bindAttributeValue(owner, source.substring(2), index) : () => { };
|
|
734
764
|
}
|
|
735
|
-
|
|
736
|
-
const
|
|
737
|
-
|
|
738
|
-
let disableValueChanged = false;
|
|
739
|
-
if (!disableValueChanged) {
|
|
740
|
-
disableValueChanged = true;
|
|
741
|
-
this.handleValueChanged(element, root, binding, element[nm], valuesObject, i, signalVars, false, relativeSignalPath);
|
|
742
|
-
disableValueChanged = false;
|
|
743
|
-
}
|
|
744
|
-
};
|
|
745
|
-
element.addEventListener(PropertiesHelper.camelToDashCase(nm) + '-changed', evtCallback);
|
|
746
|
-
if (!cleanupCalls)
|
|
747
|
-
cleanupCalls = [];
|
|
748
|
-
cleanupCalls.push(() => element.removeEventListener(PropertiesHelper.camelToDashCase(nm) + '-changed', evtCallback));
|
|
749
|
-
try {
|
|
750
|
-
this.handleValueChanged(element, root, binding, element[nm], valuesObject, i, signalVars, false, relativeSignalPath);
|
|
751
|
-
}
|
|
752
|
-
catch (err) {
|
|
753
|
-
console.error(err);
|
|
754
|
-
}
|
|
755
|
-
if (binding[1].twoWay && i == 0) {
|
|
756
|
-
this.addTwoWayBinding(binding, element, v => element[nm] = v);
|
|
757
|
-
}
|
|
765
|
+
if (source.startsWith('?$') || source.startsWith('#$')) {
|
|
766
|
+
const owner = source[0] === '?' ? root : element;
|
|
767
|
+
return owner ? bindPropertyValue(owner, source.substring(2), index) : () => { };
|
|
758
768
|
}
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
769
|
+
if (source[0] === '?' || source[0] === '#') {
|
|
770
|
+
const owner = source[0] === '?' ? root : element;
|
|
771
|
+
return owner ? bindPropertyValue(owner, source.substring(1), index) : () => { };
|
|
772
|
+
}
|
|
773
|
+
if (source[0] === '$') {
|
|
774
|
+
let objectName = source.substring(1);
|
|
775
|
+
if (objectName[0] === '.')
|
|
776
|
+
objectName = this._visualizationHandler.getNormalizedSignalName(objectName, relativeSignalPath, element);
|
|
777
|
+
let running = true;
|
|
778
|
+
this._visualizationHandler.getObject(objectName).then(value => {
|
|
779
|
+
if (running)
|
|
780
|
+
updateValue(value, index, true);
|
|
766
781
|
});
|
|
782
|
+
return () => running = false;
|
|
767
783
|
}
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
784
|
+
if (source[0] === '§') {
|
|
785
|
+
if (!specialValueHandler)
|
|
786
|
+
throw new Error(`No special value handler registered for "${source}"`);
|
|
787
|
+
return bindSpecialValue(specialValueHandler, source.substring(1), { element, binding, relativeSignalPath, root }, value => updateValue(value, index, true));
|
|
788
|
+
}
|
|
789
|
+
if (source.includes('{')) {
|
|
790
|
+
const indirectSignal = new IndirectSignal(this, this._visualizationHandler, source, value => updateValue(value.val, index, false), element, relativeSignalPath, root, specialValueHandler);
|
|
791
|
+
const setterCleanup = setSetter(index, value => indirectSignal.setState(value));
|
|
792
|
+
return () => {
|
|
793
|
+
indirectSignal.dispose();
|
|
794
|
+
setterCleanup();
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
let signalName = source;
|
|
798
|
+
if (signalName[0] === '.')
|
|
799
|
+
signalName = this._visualizationHandler.getNormalizedSignalName(signalName, relativeSignalPath, element);
|
|
800
|
+
if (binding[1].historic) {
|
|
801
|
+
const historic = binding[1].historic;
|
|
802
|
+
if (historic.reloadInterval) {
|
|
803
|
+
let running = true;
|
|
804
|
+
let timerId = null;
|
|
805
|
+
const loadHistoric = async () => {
|
|
806
|
+
const result = await this._visualizationHandler.getHistoricData(signalName, historic);
|
|
807
|
+
if (!active || !running)
|
|
808
|
+
return;
|
|
809
|
+
updateValue(result?.values, index, true);
|
|
810
|
+
timerId = setTimeout(loadHistoric, historic.reloadInterval);
|
|
811
|
+
};
|
|
812
|
+
loadHistoric();
|
|
813
|
+
return () => {
|
|
814
|
+
running = false;
|
|
815
|
+
if (timerId !== null)
|
|
816
|
+
clearTimeout(timerId);
|
|
817
|
+
timerId = null;
|
|
818
|
+
};
|
|
781
819
|
}
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
if (
|
|
785
|
-
|
|
786
|
-
else
|
|
787
|
-
this.handleValueChanged(element, root, binding, value, valuesObject, i, signalVars, true, relativeSignalPath);
|
|
820
|
+
let running = true;
|
|
821
|
+
this._visualizationHandler.getHistoricData(signalName, historic).then(result => {
|
|
822
|
+
if (running)
|
|
823
|
+
updateValue(result?.values, index, true);
|
|
788
824
|
});
|
|
825
|
+
return () => running = false;
|
|
789
826
|
}
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
}
|
|
827
|
+
const callback = (id, value) => updateValue(value?.val, index, false);
|
|
828
|
+
const subscriptionCleanup = this.subscribeToState(signalName, callback);
|
|
829
|
+
const setterCleanup = setSetter(index, value => this._visualizationHandler.setState(signalName, value));
|
|
830
|
+
return () => {
|
|
831
|
+
subscriptionCleanup();
|
|
832
|
+
setterCleanup();
|
|
833
|
+
};
|
|
834
|
+
};
|
|
835
|
+
const bindSource = (source, index) => {
|
|
836
|
+
if (source.startsWith('?@') || source.startsWith('#@') || source.startsWith('?$') || source.startsWith('#$'))
|
|
837
|
+
return bindResolvedSource(source, index);
|
|
838
|
+
const prefix = source[0];
|
|
839
|
+
if ((prefix !== '?' && prefix !== '#') || source[1] === prefix)
|
|
840
|
+
return bindResolvedSource(source[1] === prefix ? source.substring(1) : source, index);
|
|
841
|
+
const owner = prefix === '?' ? root : element;
|
|
842
|
+
if (!owner)
|
|
843
|
+
return () => { };
|
|
844
|
+
const propertyName = source.substring(1);
|
|
845
|
+
let sourceCleanup = () => { };
|
|
846
|
+
let currentSource;
|
|
847
|
+
const switchSource = (resolvedSource) => {
|
|
848
|
+
const nextSource = typeof resolvedSource === 'string' ? resolvedSource : null;
|
|
849
|
+
if (nextSource === currentSource)
|
|
850
|
+
return;
|
|
851
|
+
sourceCleanup();
|
|
852
|
+
currentSource = nextSource;
|
|
853
|
+
sourceCleanup = nextSource ? bindResolvedSource(nextSource, index) : () => { };
|
|
854
|
+
};
|
|
855
|
+
const propertyCleanup = observeProperty(this, owner, propertyName, switchSource);
|
|
856
|
+
return () => {
|
|
857
|
+
propertyCleanup();
|
|
858
|
+
sourceCleanup();
|
|
859
|
+
};
|
|
860
|
+
};
|
|
861
|
+
try {
|
|
862
|
+
if (binding[1].twoWay)
|
|
863
|
+
addCleanup(this.addTwoWayBinding(binding, element, value => setters[0]?.(value)));
|
|
864
|
+
for (let i = 0; i < signals.length; i++) {
|
|
865
|
+
if (typeof signals[i] === 'string')
|
|
866
|
+
addCleanup(bindSource(signals[i], i));
|
|
831
867
|
}
|
|
832
868
|
}
|
|
869
|
+
catch (error) {
|
|
870
|
+
cleanUp();
|
|
871
|
+
throw error;
|
|
872
|
+
}
|
|
833
873
|
return cleanUp;
|
|
834
874
|
}
|
|
835
875
|
addTwoWayBinding(binding, element, setter) {
|
|
836
|
-
if (binding[1].expressionTwoWay) {
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
else
|
|
841
|
-
binding[1].compiledExpressionTwoWay = new Function(['value'], 'return ' + binding[1].expressionTwoWay);
|
|
842
|
-
}
|
|
876
|
+
if (binding[1].expressionTwoWay && !binding[1].compiledExpressionTwoWay) {
|
|
877
|
+
binding[1].compiledExpressionTwoWay = binding[1].expressionTwoWay.includes('return ')
|
|
878
|
+
? new Function(['value'], binding[1].expressionTwoWay)
|
|
879
|
+
: new Function(['value'], 'return ' + binding[1].expressionTwoWay);
|
|
843
880
|
}
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
881
|
+
const cleanupCalls = [];
|
|
882
|
+
const callback = () => {
|
|
883
|
+
let value = binding[1].target == BindingTarget.attribute
|
|
884
|
+
? element.getAttribute(binding[0])
|
|
885
|
+
: element[binding[0]];
|
|
886
|
+
value = BindingsHelper.parseValueWithType(value, binding);
|
|
887
|
+
if (binding[1].compiledExpressionTwoWay)
|
|
888
|
+
value = binding[1].compiledExpressionTwoWay(value);
|
|
889
|
+
setter(value);
|
|
890
|
+
};
|
|
891
|
+
for (const eventName of binding[1].events ?? []) {
|
|
892
|
+
const event = element[eventName];
|
|
893
|
+
if (event instanceof TypedEvent) {
|
|
894
|
+
const disposable = event.on(callback);
|
|
895
|
+
cleanupCalls.push(() => disposable.dispose());
|
|
858
896
|
}
|
|
859
897
|
else {
|
|
860
|
-
element.addEventListener(
|
|
861
|
-
|
|
862
|
-
if (binding[1].target == BindingTarget.attribute)
|
|
863
|
-
v = element.getAttribute(binding[0]);
|
|
864
|
-
else
|
|
865
|
-
v = element[binding[0]];
|
|
866
|
-
v = BindingsHelper.parseValueWithType(v, binding);
|
|
867
|
-
if (binding[1].compiledExpressionTwoWay)
|
|
868
|
-
v = binding[1].compiledExpressionTwoWay(v);
|
|
869
|
-
setter(v);
|
|
870
|
-
});
|
|
898
|
+
element.addEventListener(eventName, callback);
|
|
899
|
+
cleanupCalls.push(() => element.removeEventListener(eventName, callback));
|
|
871
900
|
}
|
|
872
901
|
}
|
|
902
|
+
return () => {
|
|
903
|
+
for (const cleanup of cleanupCalls)
|
|
904
|
+
cleanup();
|
|
905
|
+
cleanupCalls.length = 0;
|
|
906
|
+
};
|
|
873
907
|
}
|
|
874
908
|
static parseValueWithType(value, binding) {
|
|
875
909
|
if (binding[1].type) {
|
|
@@ -888,144 +922,107 @@ export class BindingsHelper {
|
|
|
888
922
|
}
|
|
889
923
|
return value;
|
|
890
924
|
}
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
925
|
+
compileConverter(converter, expressionArguments) {
|
|
926
|
+
const exactValues = new Map();
|
|
927
|
+
const conditions = [];
|
|
928
|
+
const compileValue = (value) => {
|
|
929
|
+
if (typeof value !== 'string')
|
|
930
|
+
return { value };
|
|
931
|
+
const fn = new Function(expressionArguments, 'return `' + value + '`');
|
|
932
|
+
return { value, evaluate: (values, previousResult, context) => fn(...values, previousResult, context) };
|
|
933
|
+
};
|
|
934
|
+
for (const key in converter) {
|
|
935
|
+
const value = compileValue(converter[key]);
|
|
936
|
+
exactValues.set(key, value);
|
|
937
|
+
if (key.startsWith('>=')) {
|
|
938
|
+
const threshold = parseFloat(key.substring(2));
|
|
939
|
+
conditions.push({ matches: input => input >= threshold, value });
|
|
905
940
|
}
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
941
|
+
else if (key.startsWith('<=')) {
|
|
942
|
+
const threshold = parseFloat(key.substring(2));
|
|
943
|
+
conditions.push({ matches: input => input <= threshold, value });
|
|
944
|
+
}
|
|
945
|
+
else if (key[0] === '>') {
|
|
946
|
+
const threshold = parseFloat(key.substring(1));
|
|
947
|
+
conditions.push({ matches: input => input > threshold, value });
|
|
948
|
+
}
|
|
949
|
+
else if (key[0] === '<') {
|
|
950
|
+
const threshold = parseFloat(key.substring(1));
|
|
951
|
+
conditions.push({ matches: input => input < threshold, value });
|
|
913
952
|
}
|
|
914
953
|
else {
|
|
915
|
-
const
|
|
916
|
-
if (
|
|
917
|
-
const
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
else {
|
|
924
|
-
let endedWithBreak = false;
|
|
925
|
-
//@ts-ignore
|
|
926
|
-
const nr = parseFloat(v);
|
|
927
|
-
for (let c in binding[1].converter) {
|
|
928
|
-
if (c.length > 2 && c[0] === '>' && c[1] === '=') {
|
|
929
|
-
const wr = parseFloat(c.substring(2));
|
|
930
|
-
if (nr >= wr) {
|
|
931
|
-
const cvVal = binding[1].converter[c];
|
|
932
|
-
if (typeof cvVal === 'string')
|
|
933
|
-
v = new Function(signalVarNames, 'return `' + binding[1].converter[c] + '`')(...valuesObject);
|
|
934
|
-
else
|
|
935
|
-
v = cvVal;
|
|
936
|
-
endedWithBreak = true;
|
|
937
|
-
break;
|
|
938
|
-
}
|
|
939
|
-
}
|
|
940
|
-
else if (c.length > 2 && c[0] === '<' && c[1] === '=') {
|
|
941
|
-
const wr = parseFloat(c.substring(2));
|
|
942
|
-
if (nr <= wr) {
|
|
943
|
-
const cvVal = binding[1].converter[c];
|
|
944
|
-
if (typeof cvVal === 'string')
|
|
945
|
-
v = new Function(signalVarNames, 'return `' + binding[1].converter[c] + '`')(...valuesObject);
|
|
946
|
-
else
|
|
947
|
-
v = cvVal;
|
|
948
|
-
endedWithBreak = true;
|
|
949
|
-
break;
|
|
950
|
-
}
|
|
951
|
-
}
|
|
952
|
-
else if (c.length > 1 && c[0] === '>') {
|
|
953
|
-
const wr = parseFloat(c.substring(1));
|
|
954
|
-
if (nr > wr) {
|
|
955
|
-
const cvVal = binding[1].converter[c];
|
|
956
|
-
if (typeof cvVal === 'string')
|
|
957
|
-
v = new Function(signalVarNames, 'return `' + binding[1].converter[c] + '`')(...valuesObject);
|
|
958
|
-
else
|
|
959
|
-
v = cvVal;
|
|
960
|
-
endedWithBreak = true;
|
|
961
|
-
break;
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
else if (c.length > 1 && c[0] === '<') {
|
|
965
|
-
const wr = parseFloat(c.substring(1));
|
|
966
|
-
if (nr < wr) {
|
|
967
|
-
const cvVal = binding[1].converter[c];
|
|
968
|
-
if (typeof cvVal === 'string')
|
|
969
|
-
v = new Function(signalVarNames, 'return `' + binding[1].converter[c] + '`')(...valuesObject);
|
|
970
|
-
else
|
|
971
|
-
v = cvVal;
|
|
972
|
-
endedWithBreak = true;
|
|
973
|
-
break;
|
|
974
|
-
}
|
|
975
|
-
}
|
|
976
|
-
else {
|
|
977
|
-
const sp = c.split('-');
|
|
978
|
-
if (sp.length > 1) {
|
|
979
|
-
if ((sp[0] === '' || nr >= parseFloat(sp[0])) && (sp[1] === '' || parseFloat(sp[1]) >= nr)) {
|
|
980
|
-
const cvVal = binding[1].converter[c];
|
|
981
|
-
if (typeof cvVal === 'string')
|
|
982
|
-
v = new Function(signalVarNames, 'return `' + binding[1].converter[c] + '`')(...valuesObject);
|
|
983
|
-
else
|
|
984
|
-
v = cvVal;
|
|
985
|
-
endedWithBreak = true;
|
|
986
|
-
break;
|
|
987
|
-
}
|
|
988
|
-
}
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
if (!endedWithBreak && binding[1].converterDefault !== undefined)
|
|
992
|
-
v = binding[1].converterDefault;
|
|
954
|
+
const range = key.split('-');
|
|
955
|
+
if (range.length > 1) {
|
|
956
|
+
const minimum = range[0] === '' ? null : parseFloat(range[0]);
|
|
957
|
+
const maximum = range[1] === '' ? null : parseFloat(range[1]);
|
|
958
|
+
conditions.push({
|
|
959
|
+
matches: input => (minimum === null || input >= minimum) && (maximum === null || maximum >= input),
|
|
960
|
+
value
|
|
961
|
+
});
|
|
993
962
|
}
|
|
994
963
|
}
|
|
995
964
|
}
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
965
|
+
return { exactValues, conditions };
|
|
966
|
+
}
|
|
967
|
+
getCompiledConverter(binding, expressionArguments) {
|
|
968
|
+
if (!binding.converter || typeof binding.converter !== 'object')
|
|
969
|
+
return null;
|
|
970
|
+
const argumentsKey = expressionArguments.join('\0');
|
|
971
|
+
const cached = this._compiledConverters.get(binding);
|
|
972
|
+
if (cached?.converter === binding.converter && cached.argumentsKey === argumentsKey)
|
|
973
|
+
return cached.compiled;
|
|
974
|
+
const compiled = this.compileConverter(binding.converter, expressionArguments);
|
|
975
|
+
this._compiledConverters.set(binding, { converter: binding.converter, argumentsKey, compiled });
|
|
976
|
+
return compiled;
|
|
977
|
+
}
|
|
978
|
+
applyCompiledConverter(value, converter, values, previousResult, context, defaultValue) {
|
|
979
|
+
const key = String(value);
|
|
980
|
+
let convertedValue = converter.exactValues.get(key);
|
|
981
|
+
let matched = converter.exactValues.has(key);
|
|
982
|
+
if (!matched) {
|
|
983
|
+
const numberValue = parseFloat(value);
|
|
984
|
+
for (const condition of converter.conditions) {
|
|
985
|
+
if (condition.matches(numberValue)) {
|
|
986
|
+
convertedValue = condition.value;
|
|
987
|
+
matched = true;
|
|
988
|
+
break;
|
|
989
|
+
}
|
|
1002
990
|
}
|
|
1003
|
-
this._visualizationHandler.setState(wb, v, true);
|
|
1004
991
|
}
|
|
1005
|
-
if (
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
element
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
992
|
+
if (!matched)
|
|
993
|
+
return defaultValue !== undefined ? defaultValue : value;
|
|
994
|
+
return convertedValue.evaluate
|
|
995
|
+
? convertedValue.evaluate(values, previousResult, context)
|
|
996
|
+
: convertedValue.value;
|
|
997
|
+
}
|
|
998
|
+
createTargetWriter(element, binding) {
|
|
999
|
+
const name = binding[0];
|
|
1000
|
+
switch (binding[1].target) {
|
|
1001
|
+
case BindingTarget.property:
|
|
1002
|
+
return value => element[name] = value;
|
|
1003
|
+
case BindingTarget.attribute:
|
|
1004
|
+
return value => {
|
|
1005
|
+
if (typeof value === 'boolean') {
|
|
1006
|
+
if (value)
|
|
1007
|
+
element.setAttribute(name, '');
|
|
1008
|
+
else
|
|
1009
|
+
element.removeAttribute(name);
|
|
1010
|
+
}
|
|
1011
|
+
else {
|
|
1012
|
+
element.setAttribute(name, value);
|
|
1013
|
+
}
|
|
1014
|
+
};
|
|
1015
|
+
case BindingTarget.css:
|
|
1016
|
+
return value => element.style[name] = value;
|
|
1017
|
+
case BindingTarget.cssvar:
|
|
1018
|
+
return value => element.style.setProperty(name, value);
|
|
1019
|
+
case BindingTarget.class:
|
|
1020
|
+
return value => element.classList.toggle(name, !!value);
|
|
1021
|
+
case BindingTarget.visible:
|
|
1022
|
+
return value => element.style.visibility = value ? '' : 'collapse';
|
|
1023
|
+
default:
|
|
1024
|
+
return () => { };
|
|
1026
1025
|
}
|
|
1027
|
-
else if (binding[1].target == BindingTarget.visible)
|
|
1028
|
-
element.style.visibility = v ? '' : 'collapse';
|
|
1029
1026
|
}
|
|
1030
1027
|
static camelToDotCase(text) {
|
|
1031
1028
|
return text.replace(/([A-Z])/g, (g) => `.${g[0].toLowerCase()}`);
|