@node-projects/web-component-designer-visualization-addons 0.1.152 → 0.3.1
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 +11 -5
- package/dist/components/BindingsEditorHelp.d.ts +1 -1
- package/dist/components/BindingsEditorHelp.js +4 -0
- package/dist/components/ClassBindingsPropertiesService.d.ts +23 -0
- package/dist/components/ClassBindingsPropertiesService.js +73 -0
- package/dist/components/ClassBindingsPropertyGridExtensionProvider.d.ts +5 -0
- package/dist/components/ClassBindingsPropertyGridExtensionProvider.js +15 -0
- package/dist/components/EventAssignment.d.ts +1 -0
- package/dist/components/EventAssignment.js +5 -2
- package/dist/components/PropertyGridClassBindings.d.ts +17 -0
- package/dist/components/PropertyGridClassBindings.js +231 -0
- package/dist/helpers/BindingsHelper.d.ts +25 -13
- package/dist/helpers/BindingsHelper.js +605 -605
- package/dist/index-min.js +124 -55
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/services/VisualizationBindingsRefactorService.js +20 -23
- package/dist/services/VisualizationBindingsService.js +3 -0
- package/package.json +1 -1
- package/tests/BindingsHelper.test.ts +554 -0
- package/tests/PropertyGridClassBindings.test.ts +167 -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];
|
|
358
|
-
}
|
|
359
|
-
//Multi Var Expressions
|
|
360
|
-
if (!needsJson && binding.target == BindingTarget.class &&
|
|
361
|
-
binding.expression && !binding.expression.includes("\n") && !binding.expression.includes(";") &&
|
|
362
|
-
!binding.expressionTwoWay &&
|
|
363
|
-
binding.converter == null &&
|
|
364
|
-
//!binding.type &&
|
|
365
|
-
!binding.historic &&
|
|
366
|
-
!binding.writeBackSignal) {
|
|
367
|
-
return [bindingPrefixClass + PropertiesHelper.camelToDashCase(targetName), (binding.inverted ? '!' : '') + binding.signal + ';' + binding.expression + 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];
|
|
368
335
|
}
|
|
369
|
-
if (
|
|
370
|
-
!binding.expression && !binding.
|
|
371
|
-
binding.
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
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;
|
|
@@ -411,6 +351,9 @@ export class BindingsHelper {
|
|
|
411
351
|
if (binding.expressionTwoWay === null || binding.expressionTwoWay === '') {
|
|
412
352
|
delete bindingCopy.expressionTwoWay;
|
|
413
353
|
}
|
|
354
|
+
if (!binding.writeBackSignal) {
|
|
355
|
+
delete bindingCopy.writeBackSignal;
|
|
356
|
+
}
|
|
414
357
|
if (binding.twoWay === null || binding.twoWay === false) {
|
|
415
358
|
delete bindingCopy.twoWay;
|
|
416
359
|
}
|
|
@@ -421,21 +364,39 @@ export class BindingsHelper {
|
|
|
421
364
|
if (!binding.historic) {
|
|
422
365
|
delete bindingCopy.historic;
|
|
423
366
|
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
367
|
+
return [this.getJsonBindingAttributeName(targetName, binding.target), JSON.stringify(bindingCopy)];
|
|
368
|
+
}
|
|
369
|
+
getShortBindingAttributeName(targetName, target, hasExpression) {
|
|
370
|
+
if (target == BindingTarget.property && targetName == 'textContent')
|
|
371
|
+
return bindingPrefixContent + 'text';
|
|
372
|
+
if (target == BindingTarget.property && targetName == 'innerHTML')
|
|
373
|
+
return bindingPrefixContent + 'html';
|
|
374
|
+
if (target == BindingTarget.attribute)
|
|
375
|
+
return bindingPrefixAttribute + PropertiesHelper.camelToDashCase(targetName);
|
|
376
|
+
if (target == BindingTarget.class)
|
|
377
|
+
return bindingPrefixClass + PropertiesHelper.camelToDashCase(targetName);
|
|
378
|
+
if (target == BindingTarget.css)
|
|
379
|
+
return bindingPrefixCss + PropertiesHelper.camelToDashCase(targetName);
|
|
380
|
+
if (target == BindingTarget.cssvar)
|
|
381
|
+
return hasExpression
|
|
382
|
+
? bindingPrefixCssVar + PropertiesHelper.camelToDashCase(targetName)
|
|
383
|
+
: bindingPrefixCssVar + BindingsHelper.camelToDotCase(targetName.substring(2));
|
|
384
|
+
return bindingPrefixProperty + PropertiesHelper.camelToDashCase(targetName);
|
|
385
|
+
}
|
|
386
|
+
getJsonBindingAttributeName(targetName, target) {
|
|
387
|
+
if (target == BindingTarget.content || (target == BindingTarget.property && targetName == 'innerHTML'))
|
|
388
|
+
return bindingPrefixContent + 'html';
|
|
389
|
+
if (target == BindingTarget.property && targetName == 'textContent')
|
|
390
|
+
return bindingPrefixContent + 'text';
|
|
391
|
+
if (target == BindingTarget.attribute)
|
|
392
|
+
return bindingPrefixAttribute + PropertiesHelper.camelToDashCase(targetName);
|
|
393
|
+
if (target == BindingTarget.class)
|
|
394
|
+
return bindingPrefixClass + BindingsHelper.camelToDotCase(targetName);
|
|
395
|
+
if (target == BindingTarget.css)
|
|
396
|
+
return bindingPrefixCss + PropertiesHelper.camelToDashCase(targetName);
|
|
397
|
+
if (target == BindingTarget.cssvar)
|
|
398
|
+
return bindingPrefixCssVar + BindingsHelper.camelToDotCase(targetName.substring(2));
|
|
399
|
+
return bindingPrefixProperty + PropertiesHelper.camelToDashCase(targetName);
|
|
439
400
|
}
|
|
440
401
|
getBindingAttributeName(element, propertyName, propertyTarget) {
|
|
441
402
|
if (propertyTarget == BindingTarget.attribute) {
|
|
@@ -497,12 +458,20 @@ export class BindingsHelper {
|
|
|
497
458
|
for (let b of bindings) {
|
|
498
459
|
try {
|
|
499
460
|
let applied = this.applyBinding(e, b, relativeSignalPath, root, specialValueHandler);
|
|
500
|
-
|
|
461
|
+
let bindingDisposed = false;
|
|
462
|
+
const disposeBinding = () => {
|
|
463
|
+
if (!bindingDisposed) {
|
|
464
|
+
bindingDisposed = true;
|
|
465
|
+
applied();
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
retVal.push(disposeBinding);
|
|
501
469
|
if (b[1].maybeLitElement && e.localName.includes('-') && !customElements.get(e.localName)) {
|
|
502
470
|
const el = e;
|
|
503
471
|
const bnd = b;
|
|
504
472
|
customElements.whenDefined(e.localName).then(() => {
|
|
505
|
-
if (isLit(el)) {
|
|
473
|
+
if (!bindingDisposed && isLit(el)) {
|
|
474
|
+
bindingDisposed = true;
|
|
506
475
|
applied();
|
|
507
476
|
bnd[1].events = bnd[1].litEventNames;
|
|
508
477
|
retVal.push(this.applyBinding(el, bnd, relativeSignalPath, root, specialValueHandler));
|
|
@@ -534,9 +503,11 @@ export class BindingsHelper {
|
|
|
534
503
|
}
|
|
535
504
|
static #cssBindingsVarId = 0;
|
|
536
505
|
async parseCssBindings(sheet, element, relativeSignalPath, root) {
|
|
506
|
+
if (!sheet.includes(bindingPrefixInsideCss))
|
|
507
|
+
return [cssFromString(sheet), []];
|
|
537
508
|
const parser = (await import("@node-projects/css-parser"));
|
|
538
509
|
const ast = parser.parse(sheet);
|
|
539
|
-
|
|
510
|
+
const unsub = [];
|
|
540
511
|
for (let r of ast.stylesheet.rules) {
|
|
541
512
|
if (r.type === parser.CssTypes.rule) {
|
|
542
513
|
for (const d of r.declarations) {
|
|
@@ -544,12 +515,7 @@ export class BindingsHelper {
|
|
|
544
515
|
if (d.value.includes(bindingPrefixInsideCss)) {
|
|
545
516
|
const newValue = this.parseCssBinding(d.value, element, relativeSignalPath, root);
|
|
546
517
|
d.value = newValue[0];
|
|
547
|
-
|
|
548
|
-
unsub.push(...newValue[1]);
|
|
549
|
-
}
|
|
550
|
-
else {
|
|
551
|
-
unsub = newValue[1];
|
|
552
|
-
}
|
|
518
|
+
unsub.push(...newValue[1]);
|
|
553
519
|
}
|
|
554
520
|
}
|
|
555
521
|
}
|
|
@@ -570,8 +536,10 @@ export class BindingsHelper {
|
|
|
570
536
|
for (let n = 0; n < value.length; n++) {
|
|
571
537
|
const c = value[n];
|
|
572
538
|
if (inBind) {
|
|
573
|
-
if (escape)
|
|
539
|
+
if (escape) {
|
|
574
540
|
binding += c;
|
|
541
|
+
escape = false;
|
|
542
|
+
}
|
|
575
543
|
else if (quote && c === '\\')
|
|
576
544
|
escape = true;
|
|
577
545
|
else if (c === quote)
|
|
@@ -618,258 +586,327 @@ export class BindingsHelper {
|
|
|
618
586
|
}
|
|
619
587
|
return [res + tmp, unsub];
|
|
620
588
|
}
|
|
589
|
+
/** @internal Shared fan-out avoids duplicate backend subscriptions and initial reads. */
|
|
590
|
+
subscribeToState(id, callback) {
|
|
591
|
+
let entry = this._stateSubscriptions.get(id);
|
|
592
|
+
if (entry) {
|
|
593
|
+
entry.callbacks.add(callback);
|
|
594
|
+
if (entry.hasValue) {
|
|
595
|
+
const value = entry.value;
|
|
596
|
+
queueMicrotask(() => entry.callbacks.has(callback) && callback(id, value));
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
else {
|
|
600
|
+
entry = {
|
|
601
|
+
callbacks: new Set([callback]),
|
|
602
|
+
callback: null,
|
|
603
|
+
subscriptionResult: null,
|
|
604
|
+
revision: 0,
|
|
605
|
+
hasValue: false
|
|
606
|
+
};
|
|
607
|
+
entry.callback = (changedId, value) => {
|
|
608
|
+
entry.revision++;
|
|
609
|
+
entry.hasValue = true;
|
|
610
|
+
entry.value = value;
|
|
611
|
+
for (const cb of entry.callbacks)
|
|
612
|
+
cb(changedId, value);
|
|
613
|
+
};
|
|
614
|
+
this._stateSubscriptions.set(id, entry);
|
|
615
|
+
try {
|
|
616
|
+
entry.subscriptionResult = this._visualizationHandler.subscribeState(id, entry.callback);
|
|
617
|
+
const initialRevision = entry.revision;
|
|
618
|
+
this._visualizationHandler.getState(id).then(value => {
|
|
619
|
+
if (this._stateSubscriptions.get(id) === entry && entry.revision === initialRevision)
|
|
620
|
+
entry.callback(id, value);
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
catch (error) {
|
|
624
|
+
this._stateSubscriptions.delete(id);
|
|
625
|
+
if (entry.subscriptionResult != null)
|
|
626
|
+
this._visualizationHandler.unsubscribeState(id, entry.callback, entry.subscriptionResult);
|
|
627
|
+
throw error;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
let active = true;
|
|
631
|
+
return () => {
|
|
632
|
+
if (!active)
|
|
633
|
+
return;
|
|
634
|
+
active = false;
|
|
635
|
+
entry.callbacks.delete(callback);
|
|
636
|
+
if (entry.callbacks.size === 0 && this._stateSubscriptions.get(id) === entry) {
|
|
637
|
+
this._stateSubscriptions.delete(id);
|
|
638
|
+
this._visualizationHandler.unsubscribeState(id, entry.callback, entry.subscriptionResult);
|
|
639
|
+
}
|
|
640
|
+
};
|
|
641
|
+
}
|
|
621
642
|
/**
|
|
622
|
-
* ? =
|
|
623
|
-
*
|
|
624
|
-
*
|
|
625
|
-
*
|
|
626
|
-
*
|
|
627
|
-
|
|
628
|
-
* @param relativeSignalPath
|
|
629
|
-
* @param root
|
|
630
|
-
* @returns
|
|
631
|
-
*/
|
|
643
|
+
* ? = signal name from a root property; ?? = a root property value
|
|
644
|
+
* # = signal name from a target property; ## = a target property value
|
|
645
|
+
* ?$ = signal object from a root property; #$ = signal object from a target property
|
|
646
|
+
* ?@ = a root attribute value; #@ = a target attribute value
|
|
647
|
+
* $ = a signal configuration; § = a special value
|
|
648
|
+
*/
|
|
632
649
|
applyBinding(element, binding, relativeSignalPath, root, specialValueHandler) {
|
|
633
|
-
|
|
634
|
-
let
|
|
650
|
+
const cleanupCalls = [];
|
|
651
|
+
let active = true;
|
|
635
652
|
const cleanUp = () => {
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
}
|
|
653
|
+
if (!active)
|
|
654
|
+
return;
|
|
655
|
+
active = false;
|
|
656
|
+
this._scheduledUpdates.delete(evaluateAndApply);
|
|
657
|
+
for (let i = cleanupCalls.length - 1; i >= 0; i--)
|
|
658
|
+
cleanupCalls[i]();
|
|
659
|
+
cleanupCalls.length = 0;
|
|
644
660
|
};
|
|
661
|
+
const addCleanup = (cleanup) => cleanupCalls.push(cleanup);
|
|
645
662
|
const signals = binding[1].signal.split(';');
|
|
646
663
|
const signalVars = new Array(signals.length);
|
|
647
664
|
for (let i = 0; i < signals.length; i++) {
|
|
648
|
-
let
|
|
665
|
+
let signal = signals[i];
|
|
649
666
|
signalVars[i] = '__' + i;
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
signalVars[i] =
|
|
653
|
-
|
|
654
|
-
signals[i] =
|
|
667
|
+
const aliasSeparator = signal.indexOf(':');
|
|
668
|
+
if (aliasSeparator >= 0) {
|
|
669
|
+
signalVars[i] = signal.substring(0, aliasSeparator);
|
|
670
|
+
signal = signal.substring(aliasSeparator + 1);
|
|
671
|
+
signals[i] = signal;
|
|
655
672
|
}
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
673
|
+
}
|
|
674
|
+
const values = new Array(signals.length);
|
|
675
|
+
const expressionArguments = [...signalVars, '__res', '__ctx'];
|
|
676
|
+
if (binding[1].expression && !binding[1].compiledExpression) {
|
|
677
|
+
binding[1].compiledExpression = binding[1].expression.includes('return ')
|
|
678
|
+
? new Function(expressionArguments, binding[1].expression)
|
|
679
|
+
: new Function(expressionArguments, 'return ' + binding[1].expression);
|
|
680
|
+
}
|
|
681
|
+
const compiledConverter = this.getCompiledConverter(binding[1], expressionArguments);
|
|
682
|
+
const context = { element, root, boundNames: binding[1].signal, boundTargetName: binding[0], boundTargetType: binding[1].target };
|
|
683
|
+
const writeTarget = this.createTargetWriter(element, binding);
|
|
684
|
+
let writeBackSignal = binding[1].writeBackSignal;
|
|
685
|
+
if (writeBackSignal?.[0] === '.')
|
|
686
|
+
writeBackSignal = relativeSignalPath + writeBackSignal;
|
|
687
|
+
let currentValue;
|
|
688
|
+
let previousResult;
|
|
689
|
+
let lastOutput;
|
|
690
|
+
let hasOutput = false;
|
|
691
|
+
let hasApplied = false;
|
|
692
|
+
let scheduled = false;
|
|
693
|
+
const evaluateAndApply = () => {
|
|
694
|
+
scheduled = false;
|
|
695
|
+
if (!active)
|
|
696
|
+
return;
|
|
697
|
+
let value = currentValue;
|
|
698
|
+
if (binding[1].compiledExpression) {
|
|
699
|
+
value = binding[1].compiledExpression(...values, previousResult, context);
|
|
700
|
+
previousResult = value;
|
|
679
701
|
}
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
else {
|
|
686
|
-
signals[i] = root[s];
|
|
687
|
-
if (s[0] === '$') {
|
|
688
|
-
s = s.substring(1);
|
|
689
|
-
signals[i] = '$' + root[s];
|
|
690
|
-
}
|
|
691
|
-
let evtCallback = () => {
|
|
692
|
-
cleanUp();
|
|
693
|
-
this.applyBinding(element, binding, relativeSignalPath, root, specialValueHandler);
|
|
694
|
-
};
|
|
695
|
-
const evtNm = this.getChangedEventName(element, s);
|
|
696
|
-
root.addEventListener(evtNm, evtCallback);
|
|
697
|
-
if (!cleanupCalls)
|
|
698
|
-
cleanupCalls = [];
|
|
699
|
-
cleanupCalls.push(() => root.removeEventListener(evtNm, evtCallback));
|
|
700
|
-
}
|
|
702
|
+
if (binding[1].converter) {
|
|
703
|
+
if (typeof binding[1].converter === 'string')
|
|
704
|
+
value = this.namedConverterCallback(binding[1].converter, value, element, binding);
|
|
705
|
+
else
|
|
706
|
+
value = this.applyCompiledConverter(value, compiledConverter, values, previousResult, context, binding[1].converterDefault);
|
|
701
707
|
}
|
|
702
|
-
if (
|
|
703
|
-
|
|
708
|
+
if (binding[1].inverted)
|
|
709
|
+
value = !value;
|
|
710
|
+
hasApplied = true;
|
|
711
|
+
const canCompareByValue = value === null || typeof value !== 'object';
|
|
712
|
+
if (hasOutput && canCompareByValue && Object.is(lastOutput, value))
|
|
713
|
+
return;
|
|
714
|
+
hasOutput = true;
|
|
715
|
+
lastOutput = value;
|
|
716
|
+
if (writeBackSignal)
|
|
717
|
+
this._visualizationHandler.setState(writeBackSignal, value, true);
|
|
718
|
+
writeTarget(value);
|
|
719
|
+
};
|
|
720
|
+
const updateValue = (value, index, noParse) => {
|
|
721
|
+
if (!active)
|
|
722
|
+
return;
|
|
723
|
+
if (!noParse && index === 0)
|
|
724
|
+
value = BindingsHelper.parseValueWithType(value, binding);
|
|
725
|
+
values[index] = value;
|
|
726
|
+
currentValue = value;
|
|
727
|
+
// Preserve immediate initialization for the common one-signal case, then
|
|
728
|
+
// collapse bursts and multi-signal initialization into one microtask.
|
|
729
|
+
if (!hasApplied && signals.length === 1) {
|
|
730
|
+
evaluateAndApply();
|
|
704
731
|
}
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
const s = signals[i];
|
|
709
|
-
if (s[0] === '?') {
|
|
710
|
-
if (root) {
|
|
711
|
-
const nm = s.substring(1);
|
|
712
|
-
let evtCallback = () => {
|
|
713
|
-
let disableValueChanged = false;
|
|
714
|
-
if (!disableValueChanged) {
|
|
715
|
-
disableValueChanged = true;
|
|
716
|
-
this.handleValueChanged(element, root, binding, root[nm], valuesObject, i, signalVars, false, relativeSignalPath);
|
|
717
|
-
disableValueChanged = false;
|
|
718
|
-
}
|
|
719
|
-
};
|
|
720
|
-
root.addEventListener(PropertiesHelper.camelToDashCase(nm) + '-changed', evtCallback);
|
|
721
|
-
if (!cleanupCalls)
|
|
722
|
-
cleanupCalls = [];
|
|
723
|
-
cleanupCalls.push(() => root.removeEventListener(PropertiesHelper.camelToDashCase(nm) + '-changed', evtCallback));
|
|
724
|
-
try {
|
|
725
|
-
this.handleValueChanged(element, root, binding, root[nm], valuesObject, i, signalVars, false, relativeSignalPath);
|
|
726
|
-
}
|
|
727
|
-
catch (err) {
|
|
728
|
-
console.error(err);
|
|
729
|
-
}
|
|
730
|
-
if (binding[1].twoWay && i == 0) {
|
|
731
|
-
this.addTwoWayBinding(binding, element, v => root[nm] = v);
|
|
732
|
-
}
|
|
733
|
-
}
|
|
732
|
+
else if (!scheduled) {
|
|
733
|
+
scheduled = true;
|
|
734
|
+
this.scheduleUpdate(evaluateAndApply);
|
|
734
735
|
}
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
736
|
+
};
|
|
737
|
+
const setters = new Array(signals.length);
|
|
738
|
+
const setSetter = (index, setter) => {
|
|
739
|
+
setters[index] = setter;
|
|
740
|
+
return () => {
|
|
741
|
+
if (setters[index] === setter)
|
|
742
|
+
setters[index] = null;
|
|
743
|
+
};
|
|
744
|
+
};
|
|
745
|
+
const bindPropertyValue = (owner, propertyName, index) => {
|
|
746
|
+
const setterCleanup = setSetter(index, value => owner[propertyName] = value);
|
|
747
|
+
const propertyCleanup = observeProperty(this, owner, propertyName, value => updateValue(value, index, false));
|
|
748
|
+
return () => {
|
|
749
|
+
propertyCleanup();
|
|
750
|
+
setterCleanup();
|
|
751
|
+
};
|
|
752
|
+
};
|
|
753
|
+
const bindAttributeValue = (owner, attributeName, index) => {
|
|
754
|
+
const setterCleanup = setSetter(index, value => value == null
|
|
755
|
+
? owner.removeAttribute(attributeName)
|
|
756
|
+
: owner.setAttribute(attributeName, value));
|
|
757
|
+
const attributeCleanup = observeAttribute(owner, attributeName, value => updateValue(value, index, false));
|
|
758
|
+
return () => {
|
|
759
|
+
attributeCleanup();
|
|
760
|
+
setterCleanup();
|
|
761
|
+
};
|
|
762
|
+
};
|
|
763
|
+
const bindResolvedSource = (source, index) => {
|
|
764
|
+
if (source.startsWith('?@') || source.startsWith('#@')) {
|
|
765
|
+
const owner = source[0] === '?' ? root : element;
|
|
766
|
+
return owner ? bindAttributeValue(owner, source.substring(2), index) : () => { };
|
|
758
767
|
}
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
768
|
+
if (source.startsWith('?$') || source.startsWith('#$')) {
|
|
769
|
+
const owner = source[0] === '?' ? root : element;
|
|
770
|
+
return owner ? bindPropertyValue(owner, source.substring(2), index) : () => { };
|
|
771
|
+
}
|
|
772
|
+
if (source[0] === '?' || source[0] === '#') {
|
|
773
|
+
const owner = source[0] === '?' ? root : element;
|
|
774
|
+
return owner ? bindPropertyValue(owner, source.substring(1), index) : () => { };
|
|
775
|
+
}
|
|
776
|
+
if (source[0] === '$') {
|
|
777
|
+
let objectName = source.substring(1);
|
|
778
|
+
if (objectName[0] === '.')
|
|
779
|
+
objectName = this._visualizationHandler.getNormalizedSignalName(objectName, relativeSignalPath, element);
|
|
780
|
+
let running = true;
|
|
781
|
+
this._visualizationHandler.getObject(objectName).then(value => {
|
|
782
|
+
if (running)
|
|
783
|
+
updateValue(value, index, true);
|
|
766
784
|
});
|
|
785
|
+
return () => running = false;
|
|
767
786
|
}
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
787
|
+
if (source[0] === '§') {
|
|
788
|
+
if (!specialValueHandler)
|
|
789
|
+
throw new Error(`No special value handler registered for "${source}"`);
|
|
790
|
+
return bindSpecialValue(specialValueHandler, source.substring(1), { element, binding, relativeSignalPath, root }, value => updateValue(value, index, true));
|
|
791
|
+
}
|
|
792
|
+
if (source.includes('{')) {
|
|
793
|
+
const indirectSignal = new IndirectSignal(this, this._visualizationHandler, source, value => updateValue(value.val, index, false), element, relativeSignalPath, root, specialValueHandler);
|
|
794
|
+
const setterCleanup = setSetter(index, value => indirectSignal.setState(value));
|
|
795
|
+
return () => {
|
|
796
|
+
indirectSignal.dispose();
|
|
797
|
+
setterCleanup();
|
|
798
|
+
};
|
|
799
|
+
}
|
|
800
|
+
let signalName = source;
|
|
801
|
+
if (signalName[0] === '.')
|
|
802
|
+
signalName = this._visualizationHandler.getNormalizedSignalName(signalName, relativeSignalPath, element);
|
|
803
|
+
if (binding[1].historic) {
|
|
804
|
+
const historic = binding[1].historic;
|
|
805
|
+
if (historic.reloadInterval) {
|
|
806
|
+
let running = true;
|
|
807
|
+
let timerId = null;
|
|
808
|
+
const loadHistoric = async () => {
|
|
809
|
+
const result = await this._visualizationHandler.getHistoricData(signalName, historic);
|
|
810
|
+
if (!active || !running)
|
|
811
|
+
return;
|
|
812
|
+
updateValue(result?.values, index, true);
|
|
813
|
+
timerId = setTimeout(loadHistoric, historic.reloadInterval);
|
|
814
|
+
};
|
|
815
|
+
loadHistoric();
|
|
816
|
+
return () => {
|
|
817
|
+
running = false;
|
|
818
|
+
if (timerId !== null)
|
|
819
|
+
clearTimeout(timerId);
|
|
820
|
+
timerId = null;
|
|
821
|
+
};
|
|
781
822
|
}
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
if (
|
|
785
|
-
|
|
786
|
-
else
|
|
787
|
-
this.handleValueChanged(element, root, binding, value, valuesObject, i, signalVars, true, relativeSignalPath);
|
|
823
|
+
let running = true;
|
|
824
|
+
this._visualizationHandler.getHistoricData(signalName, historic).then(result => {
|
|
825
|
+
if (running)
|
|
826
|
+
updateValue(result?.values, index, true);
|
|
788
827
|
});
|
|
828
|
+
return () => running = false;
|
|
789
829
|
}
|
|
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
|
-
}
|
|
830
|
+
const callback = (id, value) => updateValue(value?.val, index, false);
|
|
831
|
+
const subscriptionCleanup = this.subscribeToState(signalName, callback);
|
|
832
|
+
const setterCleanup = setSetter(index, value => this._visualizationHandler.setState(signalName, value));
|
|
833
|
+
return () => {
|
|
834
|
+
subscriptionCleanup();
|
|
835
|
+
setterCleanup();
|
|
836
|
+
};
|
|
837
|
+
};
|
|
838
|
+
const bindSource = (source, index) => {
|
|
839
|
+
if (source.startsWith('?@') || source.startsWith('#@') || source.startsWith('?$') || source.startsWith('#$'))
|
|
840
|
+
return bindResolvedSource(source, index);
|
|
841
|
+
const prefix = source[0];
|
|
842
|
+
if ((prefix !== '?' && prefix !== '#') || source[1] === prefix)
|
|
843
|
+
return bindResolvedSource(source[1] === prefix ? source.substring(1) : source, index);
|
|
844
|
+
const owner = prefix === '?' ? root : element;
|
|
845
|
+
if (!owner)
|
|
846
|
+
return () => { };
|
|
847
|
+
const propertyName = source.substring(1);
|
|
848
|
+
let sourceCleanup = () => { };
|
|
849
|
+
let currentSource;
|
|
850
|
+
const switchSource = (resolvedSource) => {
|
|
851
|
+
const nextSource = typeof resolvedSource === 'string' ? resolvedSource : null;
|
|
852
|
+
if (nextSource === currentSource)
|
|
853
|
+
return;
|
|
854
|
+
sourceCleanup();
|
|
855
|
+
currentSource = nextSource;
|
|
856
|
+
sourceCleanup = nextSource ? bindResolvedSource(nextSource, index) : () => { };
|
|
857
|
+
};
|
|
858
|
+
const propertyCleanup = observeProperty(this, owner, propertyName, switchSource);
|
|
859
|
+
return () => {
|
|
860
|
+
propertyCleanup();
|
|
861
|
+
sourceCleanup();
|
|
862
|
+
};
|
|
863
|
+
};
|
|
864
|
+
try {
|
|
865
|
+
if (binding[1].twoWay)
|
|
866
|
+
addCleanup(this.addTwoWayBinding(binding, element, value => setters[0]?.(value)));
|
|
867
|
+
for (let i = 0; i < signals.length; i++) {
|
|
868
|
+
if (typeof signals[i] === 'string')
|
|
869
|
+
addCleanup(bindSource(signals[i], i));
|
|
831
870
|
}
|
|
832
871
|
}
|
|
872
|
+
catch (error) {
|
|
873
|
+
cleanUp();
|
|
874
|
+
throw error;
|
|
875
|
+
}
|
|
833
876
|
return cleanUp;
|
|
834
877
|
}
|
|
835
878
|
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
|
-
}
|
|
879
|
+
if (binding[1].expressionTwoWay && !binding[1].compiledExpressionTwoWay) {
|
|
880
|
+
binding[1].compiledExpressionTwoWay = binding[1].expressionTwoWay.includes('return ')
|
|
881
|
+
? new Function(['value'], binding[1].expressionTwoWay)
|
|
882
|
+
: new Function(['value'], 'return ' + binding[1].expressionTwoWay);
|
|
843
883
|
}
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
884
|
+
const cleanupCalls = [];
|
|
885
|
+
const callback = () => {
|
|
886
|
+
let value = binding[1].target == BindingTarget.attribute
|
|
887
|
+
? element.getAttribute(binding[0])
|
|
888
|
+
: element[binding[0]];
|
|
889
|
+
value = BindingsHelper.parseValueWithType(value, binding);
|
|
890
|
+
if (binding[1].compiledExpressionTwoWay)
|
|
891
|
+
value = binding[1].compiledExpressionTwoWay(value);
|
|
892
|
+
setter(value);
|
|
893
|
+
};
|
|
894
|
+
for (const eventName of binding[1].events ?? []) {
|
|
895
|
+
const event = element[eventName];
|
|
896
|
+
if (event instanceof TypedEvent) {
|
|
897
|
+
const disposable = event.on(callback);
|
|
898
|
+
cleanupCalls.push(() => disposable.dispose());
|
|
858
899
|
}
|
|
859
900
|
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
|
-
});
|
|
901
|
+
element.addEventListener(eventName, callback);
|
|
902
|
+
cleanupCalls.push(() => element.removeEventListener(eventName, callback));
|
|
871
903
|
}
|
|
872
904
|
}
|
|
905
|
+
return () => {
|
|
906
|
+
for (const cleanup of cleanupCalls)
|
|
907
|
+
cleanup();
|
|
908
|
+
cleanupCalls.length = 0;
|
|
909
|
+
};
|
|
873
910
|
}
|
|
874
911
|
static parseValueWithType(value, binding) {
|
|
875
912
|
if (binding[1].type) {
|
|
@@ -888,144 +925,107 @@ export class BindingsHelper {
|
|
|
888
925
|
}
|
|
889
926
|
return value;
|
|
890
927
|
}
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
928
|
+
compileConverter(converter, expressionArguments) {
|
|
929
|
+
const exactValues = new Map();
|
|
930
|
+
const conditions = [];
|
|
931
|
+
const compileValue = (value) => {
|
|
932
|
+
if (typeof value !== 'string')
|
|
933
|
+
return { value };
|
|
934
|
+
const fn = new Function(expressionArguments, 'return `' + value + '`');
|
|
935
|
+
return { value, evaluate: (values, previousResult, context) => fn(...values, previousResult, context) };
|
|
936
|
+
};
|
|
937
|
+
for (const key in converter) {
|
|
938
|
+
const value = compileValue(converter[key]);
|
|
939
|
+
exactValues.set(key, value);
|
|
940
|
+
if (key.startsWith('>=')) {
|
|
941
|
+
const threshold = parseFloat(key.substring(2));
|
|
942
|
+
conditions.push({ matches: input => input >= threshold, value });
|
|
905
943
|
}
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
944
|
+
else if (key.startsWith('<=')) {
|
|
945
|
+
const threshold = parseFloat(key.substring(2));
|
|
946
|
+
conditions.push({ matches: input => input <= threshold, value });
|
|
947
|
+
}
|
|
948
|
+
else if (key[0] === '>') {
|
|
949
|
+
const threshold = parseFloat(key.substring(1));
|
|
950
|
+
conditions.push({ matches: input => input > threshold, value });
|
|
951
|
+
}
|
|
952
|
+
else if (key[0] === '<') {
|
|
953
|
+
const threshold = parseFloat(key.substring(1));
|
|
954
|
+
conditions.push({ matches: input => input < threshold, value });
|
|
913
955
|
}
|
|
914
956
|
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;
|
|
957
|
+
const range = key.split('-');
|
|
958
|
+
if (range.length > 1) {
|
|
959
|
+
const minimum = range[0] === '' ? null : parseFloat(range[0]);
|
|
960
|
+
const maximum = range[1] === '' ? null : parseFloat(range[1]);
|
|
961
|
+
conditions.push({
|
|
962
|
+
matches: input => (minimum === null || input >= minimum) && (maximum === null || maximum >= input),
|
|
963
|
+
value
|
|
964
|
+
});
|
|
993
965
|
}
|
|
994
966
|
}
|
|
995
967
|
}
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
968
|
+
return { exactValues, conditions };
|
|
969
|
+
}
|
|
970
|
+
getCompiledConverter(binding, expressionArguments) {
|
|
971
|
+
if (!binding.converter || typeof binding.converter !== 'object')
|
|
972
|
+
return null;
|
|
973
|
+
const argumentsKey = expressionArguments.join('\0');
|
|
974
|
+
const cached = this._compiledConverters.get(binding);
|
|
975
|
+
if (cached?.converter === binding.converter && cached.argumentsKey === argumentsKey)
|
|
976
|
+
return cached.compiled;
|
|
977
|
+
const compiled = this.compileConverter(binding.converter, expressionArguments);
|
|
978
|
+
this._compiledConverters.set(binding, { converter: binding.converter, argumentsKey, compiled });
|
|
979
|
+
return compiled;
|
|
980
|
+
}
|
|
981
|
+
applyCompiledConverter(value, converter, values, previousResult, context, defaultValue) {
|
|
982
|
+
const key = String(value);
|
|
983
|
+
let convertedValue = converter.exactValues.get(key);
|
|
984
|
+
let matched = converter.exactValues.has(key);
|
|
985
|
+
if (!matched) {
|
|
986
|
+
const numberValue = parseFloat(value);
|
|
987
|
+
for (const condition of converter.conditions) {
|
|
988
|
+
if (condition.matches(numberValue)) {
|
|
989
|
+
convertedValue = condition.value;
|
|
990
|
+
matched = true;
|
|
991
|
+
break;
|
|
992
|
+
}
|
|
1002
993
|
}
|
|
1003
|
-
this._visualizationHandler.setState(wb, v, true);
|
|
1004
994
|
}
|
|
1005
|
-
if (
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
element
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
995
|
+
if (!matched)
|
|
996
|
+
return defaultValue !== undefined ? defaultValue : value;
|
|
997
|
+
return convertedValue.evaluate
|
|
998
|
+
? convertedValue.evaluate(values, previousResult, context)
|
|
999
|
+
: convertedValue.value;
|
|
1000
|
+
}
|
|
1001
|
+
createTargetWriter(element, binding) {
|
|
1002
|
+
const name = binding[0];
|
|
1003
|
+
switch (binding[1].target) {
|
|
1004
|
+
case BindingTarget.property:
|
|
1005
|
+
return value => element[name] = value;
|
|
1006
|
+
case BindingTarget.attribute:
|
|
1007
|
+
return value => {
|
|
1008
|
+
if (typeof value === 'boolean') {
|
|
1009
|
+
if (value)
|
|
1010
|
+
element.setAttribute(name, '');
|
|
1011
|
+
else
|
|
1012
|
+
element.removeAttribute(name);
|
|
1013
|
+
}
|
|
1014
|
+
else {
|
|
1015
|
+
element.setAttribute(name, value);
|
|
1016
|
+
}
|
|
1017
|
+
};
|
|
1018
|
+
case BindingTarget.css:
|
|
1019
|
+
return value => element.style[name] = value;
|
|
1020
|
+
case BindingTarget.cssvar:
|
|
1021
|
+
return value => element.style.setProperty(name, value);
|
|
1022
|
+
case BindingTarget.class:
|
|
1023
|
+
return value => element.classList.toggle(name, !!value);
|
|
1024
|
+
case BindingTarget.visible:
|
|
1025
|
+
return value => element.style.visibility = value ? '' : 'collapse';
|
|
1026
|
+
default:
|
|
1027
|
+
return () => { };
|
|
1026
1028
|
}
|
|
1027
|
-
else if (binding[1].target == BindingTarget.visible)
|
|
1028
|
-
element.style.visibility = v ? '' : 'collapse';
|
|
1029
1029
|
}
|
|
1030
1030
|
static camelToDotCase(text) {
|
|
1031
1031
|
return text.replace(/([A-Z])/g, (g) => `.${g[0].toLowerCase()}`);
|