@genome-spy/core 0.68.0 → 0.69.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/bundle/index.es.js +12119 -10681
- package/dist/bundle/index.js +119 -119
- package/dist/schema.json +6224 -6319
- package/dist/src/data/dataFlow.d.ts.map +1 -1
- package/dist/src/data/dataFlow.js +10 -0
- package/dist/src/data/flowNode.d.ts +25 -10
- package/dist/src/data/flowNode.d.ts.map +1 -1
- package/dist/src/data/flowNode.js +66 -13
- package/dist/src/data/flowTestUtils.d.ts +2 -2
- package/dist/src/data/flowTestUtils.d.ts.map +1 -1
- package/dist/src/data/flowTestUtils.js +5 -4
- package/dist/src/data/sources/dataSource.js +2 -2
- package/dist/src/data/sources/lazy/bigBedSource.d.ts.map +1 -1
- package/dist/src/data/sources/lazy/bigBedSource.js +11 -10
- package/dist/src/data/sources/lazy/bigWigSource.d.ts.map +1 -1
- package/dist/src/data/sources/lazy/bigWigSource.js +11 -10
- package/dist/src/data/sources/lazy/singleAxisWindowedSource.js +1 -1
- package/dist/src/data/sources/lazy/tabixSource.d.ts +0 -1
- package/dist/src/data/sources/lazy/tabixSource.d.ts.map +1 -1
- package/dist/src/data/sources/lazy/tabixSource.js +41 -11
- package/dist/src/data/sources/sequenceSource.d.ts.map +1 -1
- package/dist/src/data/sources/sequenceSource.js +5 -3
- package/dist/src/data/sources/urlSource.d.ts.map +1 -1
- package/dist/src/data/sources/urlSource.js +7 -3
- package/dist/src/data/transforms/filter.d.ts +4 -4
- package/dist/src/data/transforms/filter.d.ts.map +1 -1
- package/dist/src/data/transforms/filter.js +13 -7
- package/dist/src/data/transforms/filterScoredLabels.d.ts.map +1 -1
- package/dist/src/data/transforms/filterScoredLabels.js +11 -6
- package/dist/src/data/transforms/filterScoredLabels.test.d.ts +2 -0
- package/dist/src/data/transforms/filterScoredLabels.test.d.ts.map +1 -0
- package/dist/src/data/transforms/formula.d.ts +4 -4
- package/dist/src/data/transforms/formula.d.ts.map +1 -1
- package/dist/src/data/transforms/formula.js +12 -6
- package/dist/src/data/transforms/measureText.d.ts +2 -2
- package/dist/src/data/transforms/measureText.d.ts.map +1 -1
- package/dist/src/data/transforms/measureText.js +16 -12
- package/dist/src/data/transforms/transform.d.ts +2 -2
- package/dist/src/data/transforms/transform.d.ts.map +1 -1
- package/dist/src/data/transforms/transform.js +3 -3
- package/dist/src/encoder/accessor.d.ts +8 -4
- package/dist/src/encoder/accessor.d.ts.map +1 -1
- package/dist/src/encoder/accessor.js +10 -10
- package/dist/src/encoder/encoder.js +5 -5
- package/dist/src/genome/genome.d.ts +8 -0
- package/dist/src/genome/genome.d.ts.map +1 -1
- package/dist/src/genome/genome.js +16 -1
- package/dist/src/genomeSpy/inputBindingManager.js +1 -1
- package/dist/src/genomeSpy/interactionController.d.ts.map +1 -1
- package/dist/src/genomeSpy/interactionController.js +7 -1
- package/dist/src/genomeSpy.js +1 -1
- package/dist/src/gl/glslScaleGenerator.js +1 -1
- package/dist/src/marks/mark.d.ts.map +1 -1
- package/dist/src/marks/mark.js +22 -30
- package/dist/src/marks/point.d.ts.map +1 -1
- package/dist/src/marks/point.js +4 -6
- package/dist/src/paramRuntime/expressionCompiler.d.ts +7 -0
- package/dist/src/paramRuntime/expressionCompiler.d.ts.map +1 -0
- package/dist/src/paramRuntime/expressionCompiler.js +10 -0
- package/dist/src/paramRuntime/expressionRef.d.ts +20 -0
- package/dist/src/paramRuntime/expressionRef.d.ts.map +1 -0
- package/dist/src/paramRuntime/expressionRef.js +95 -0
- package/dist/src/paramRuntime/expressionRef.test.d.ts +2 -0
- package/dist/src/paramRuntime/expressionRef.test.d.ts.map +1 -0
- package/dist/src/paramRuntime/graphRuntime.d.ts +176 -0
- package/dist/src/paramRuntime/graphRuntime.d.ts.map +1 -0
- package/dist/src/paramRuntime/graphRuntime.js +628 -0
- package/dist/src/paramRuntime/graphRuntime.test.d.ts +2 -0
- package/dist/src/paramRuntime/graphRuntime.test.d.ts.map +1 -0
- package/dist/src/paramRuntime/index.d.ts +9 -0
- package/dist/src/paramRuntime/index.d.ts.map +1 -0
- package/dist/src/paramRuntime/index.js +8 -0
- package/dist/src/paramRuntime/lifecycleRegistry.d.ts +27 -0
- package/dist/src/paramRuntime/lifecycleRegistry.d.ts.map +1 -0
- package/dist/src/paramRuntime/lifecycleRegistry.js +54 -0
- package/dist/src/paramRuntime/paramRuntime.d.ts +165 -0
- package/dist/src/paramRuntime/paramRuntime.d.ts.map +1 -0
- package/dist/src/paramRuntime/paramRuntime.js +222 -0
- package/dist/src/paramRuntime/paramRuntime.test.d.ts +2 -0
- package/dist/src/paramRuntime/paramRuntime.test.d.ts.map +1 -0
- package/dist/src/paramRuntime/paramStore.d.ts +68 -0
- package/dist/src/paramRuntime/paramStore.d.ts.map +1 -0
- package/dist/src/paramRuntime/paramStore.js +148 -0
- package/dist/src/paramRuntime/paramStore.test.d.ts +2 -0
- package/dist/src/paramRuntime/paramStore.test.d.ts.map +1 -0
- package/dist/src/paramRuntime/paramUtils.d.ts +86 -0
- package/dist/src/paramRuntime/paramUtils.d.ts.map +1 -0
- package/dist/src/paramRuntime/paramUtils.js +272 -0
- package/dist/src/paramRuntime/selectionStore.d.ts +6 -0
- package/dist/src/paramRuntime/selectionStore.d.ts.map +1 -0
- package/dist/src/paramRuntime/selectionStore.js +13 -0
- package/dist/src/paramRuntime/types.d.ts +16 -0
- package/dist/src/paramRuntime/types.d.ts.map +1 -0
- package/dist/src/paramRuntime/types.js +25 -0
- package/dist/src/paramRuntime/viewParamRuntime.d.ts +164 -0
- package/dist/src/paramRuntime/viewParamRuntime.d.ts.map +1 -0
- package/dist/src/paramRuntime/viewParamRuntime.js +443 -0
- package/dist/src/scales/scaleInstanceManager.d.ts +6 -3
- package/dist/src/scales/scaleInstanceManager.d.ts.map +1 -1
- package/dist/src/scales/scaleInstanceManager.js +17 -11
- package/dist/src/scales/scaleResolution.d.ts +1 -0
- package/dist/src/scales/scaleResolution.d.ts.map +1 -1
- package/dist/src/scales/scaleResolution.js +7 -1
- package/dist/src/selection/selection.js +1 -1
- package/dist/src/spec/coreSchemaRoot.d.ts +53 -0
- package/dist/src/spec/root.d.ts +1 -1
- package/dist/src/spec/view.d.ts +114 -33
- package/dist/src/tooltip/dataTooltipHandler.d.ts +1 -1
- package/dist/src/tooltip/dataTooltipHandler.js +23 -32
- package/dist/src/tooltip/dataTooltipHandler.test.d.ts +2 -0
- package/dist/src/tooltip/dataTooltipHandler.test.d.ts.map +1 -0
- package/dist/src/tooltip/flattenDatumRows.d.ts +13 -0
- package/dist/src/tooltip/flattenDatumRows.d.ts.map +1 -0
- package/dist/src/tooltip/flattenDatumRows.js +47 -0
- package/dist/src/tooltip/flattenDatumRows.test.d.ts +2 -0
- package/dist/src/tooltip/flattenDatumRows.test.d.ts.map +1 -0
- package/dist/src/tooltip/refseqGeneTooltipHandler.d.ts +1 -1
- package/dist/src/tooltip/refseqGeneTooltipHandler.js +7 -1
- package/dist/src/tooltip/tooltipContext.d.ts +13 -0
- package/dist/src/tooltip/tooltipContext.d.ts.map +1 -0
- package/dist/src/tooltip/tooltipContext.js +543 -0
- package/dist/src/tooltip/tooltipContext.test.d.ts +2 -0
- package/dist/src/tooltip/tooltipContext.test.d.ts.map +1 -0
- package/dist/src/tooltip/tooltipHandler.d.ts +40 -1
- package/dist/src/tooltip/tooltipHandler.d.ts.map +1 -1
- package/dist/src/tooltip/tooltipHandler.ts +62 -1
- package/dist/src/types/encoder.d.ts +1 -1
- package/dist/src/utils/inputBinding.d.ts +10 -2
- package/dist/src/utils/inputBinding.d.ts.map +1 -1
- package/dist/src/utils/inputBinding.js +12 -3
- package/dist/src/view/flowBuilder.d.ts.map +1 -1
- package/dist/src/view/flowBuilder.js +12 -3
- package/dist/src/view/gridView/gridChild.d.ts.map +1 -1
- package/dist/src/view/gridView/gridChild.js +8 -3
- package/dist/src/view/gridView/selectionRect.d.ts +6 -10
- package/dist/src/view/gridView/selectionRect.d.ts.map +1 -1
- package/dist/src/view/gridView/selectionRect.js +3 -20
- package/dist/src/view/layerView.d.ts.map +1 -1
- package/dist/src/view/layerView.js +4 -2
- package/dist/src/view/multiscale.d.ts +35 -0
- package/dist/src/view/multiscale.d.ts.map +1 -0
- package/dist/src/view/multiscale.js +233 -0
- package/dist/src/view/multiscale.test.d.ts +2 -0
- package/dist/src/view/multiscale.test.d.ts.map +1 -0
- package/dist/src/view/unitView.d.ts.map +1 -1
- package/dist/src/view/unitView.js +10 -4
- package/dist/src/view/view.d.ts +5 -4
- package/dist/src/view/view.d.ts.map +1 -1
- package/dist/src/view/view.js +223 -28
- package/dist/src/view/viewFactory.d.ts +0 -12
- package/dist/src/view/viewFactory.d.ts.map +1 -1
- package/dist/src/view/viewFactory.js +35 -24
- package/dist/src/view/viewParamRuntime.test.d.ts +2 -0
- package/dist/src/view/viewParamRuntime.test.d.ts.map +1 -0
- package/dist/src/view/viewSelectors.d.ts.map +1 -1
- package/dist/src/view/viewSelectors.js +8 -5
- package/package.json +3 -3
- package/dist/src/spec/sampleView.d.ts +0 -197
- package/dist/src/view/paramMediator.d.ts +0 -168
- package/dist/src/view/paramMediator.d.ts.map +0 -1
- package/dist/src/view/paramMediator.js +0 -545
- package/dist/src/view/paramMediator.test.d.ts +0 -2
- package/dist/src/view/paramMediator.test.d.ts.map +0 -1
|
@@ -1,545 +0,0 @@
|
|
|
1
|
-
import { isString } from "vega-util";
|
|
2
|
-
import createFunction from "../utils/expression.js";
|
|
3
|
-
import {
|
|
4
|
-
asSelectionConfig,
|
|
5
|
-
createIntervalSelection,
|
|
6
|
-
createMultiPointSelection,
|
|
7
|
-
createSinglePointSelection,
|
|
8
|
-
isIntervalSelectionConfig,
|
|
9
|
-
isPointSelectionConfig,
|
|
10
|
-
} from "../selection/selection.js";
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* A class that manages parameters and expressions.
|
|
14
|
-
* Supports nesting and scoped parameters.
|
|
15
|
-
*
|
|
16
|
-
* TODO: The proposed JavaScript signals may provide a better way to implement this.
|
|
17
|
-
* https://github.com/proposal-signals/proposal-signals
|
|
18
|
-
*
|
|
19
|
-
* @typedef {import("../utils/expression.js").ExpressionFunction & { addListener: (listener: () => void) => void, removeListener: (listener: () => void) => void, invalidate: () => void, identifier: () => string}} ExprRefFunction
|
|
20
|
-
*/
|
|
21
|
-
export default class ParamMediator {
|
|
22
|
-
/**
|
|
23
|
-
* @typedef {import("../spec/parameter.js").Parameter} Parameter
|
|
24
|
-
* @typedef {(value: any) => void} ParameterSetter
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
/** @type {Map<string, any>} */
|
|
28
|
-
#paramValues;
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* @type {Map<string, Set<() => void>>}
|
|
32
|
-
* @protected
|
|
33
|
-
*/
|
|
34
|
-
paramListeners;
|
|
35
|
-
|
|
36
|
-
/** @type {Map<string, (value: any) => void>} */
|
|
37
|
-
#allocatedSetters = new Map();
|
|
38
|
-
|
|
39
|
-
/** @type {Map<string, ExprRefFunction>} */
|
|
40
|
-
#expressions = new Map();
|
|
41
|
-
|
|
42
|
-
/** @type {Map<string, Parameter>} */
|
|
43
|
-
#paramConfigs = new Map();
|
|
44
|
-
|
|
45
|
-
/** @type {() => ParamMediator} */
|
|
46
|
-
#parentFinder;
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* @param {() => ParamMediator} [parentFinder]
|
|
50
|
-
* An optional function that returns the parent mediator.
|
|
51
|
-
* N.B. The function must always return the same mediator for the same parent,
|
|
52
|
-
* i.e., the changing the structure of the hierarchy is NOT supported.
|
|
53
|
-
*/
|
|
54
|
-
constructor(parentFinder) {
|
|
55
|
-
this.#parentFinder = parentFinder ?? (() => undefined);
|
|
56
|
-
|
|
57
|
-
this.#paramValues = new Map();
|
|
58
|
-
this.paramListeners = new Map();
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* @param {Parameter} param
|
|
63
|
-
* @returns {ParameterSetter}
|
|
64
|
-
*/
|
|
65
|
-
registerParam(param) {
|
|
66
|
-
const name = param.name;
|
|
67
|
-
|
|
68
|
-
if ("value" in param && "expr" in param) {
|
|
69
|
-
throw new Error(
|
|
70
|
-
`The parameter "${name}" must not have both value and expr properties!`
|
|
71
|
-
);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/** @type {ParameterSetter} */
|
|
75
|
-
let setter;
|
|
76
|
-
let defaultValue;
|
|
77
|
-
|
|
78
|
-
if (param.push == "outer") {
|
|
79
|
-
const outerMediator = this.findMediatorForParam(name);
|
|
80
|
-
if (!outerMediator) {
|
|
81
|
-
throw new Error(
|
|
82
|
-
`Parameter "${name}" not found in outer scope!`
|
|
83
|
-
);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const outerProps = outerMediator.paramConfigs.get(name);
|
|
87
|
-
if ("expr" in outerProps || "select" in outerProps) {
|
|
88
|
-
throw new Error(
|
|
89
|
-
`The outer parameter "${name}" must not have expr or select properties!`
|
|
90
|
-
);
|
|
91
|
-
}
|
|
92
|
-
setter = outerMediator.getSetter(name);
|
|
93
|
-
// The following will become a bit fragile if the view hierarchy is going to
|
|
94
|
-
// support mutation (i.e. adding/removing children) in future.
|
|
95
|
-
this.#allocatedSetters.set(name, setter);
|
|
96
|
-
} else if ("value" in param) {
|
|
97
|
-
defaultValue = getDefaultParamValue(param, this);
|
|
98
|
-
setter = this.allocateSetter(name, defaultValue);
|
|
99
|
-
} else if ("expr" in param) {
|
|
100
|
-
const expr = this.createExpression(param.expr);
|
|
101
|
-
// TODO: getSetter(param) should return a setter that throws if
|
|
102
|
-
// modifying the value is attempted.
|
|
103
|
-
defaultValue = getDefaultParamValue(param, this, expr);
|
|
104
|
-
const realSetter = this.allocateSetter(name, defaultValue);
|
|
105
|
-
expr.addListener(() => realSetter(expr(null)));
|
|
106
|
-
// NOP
|
|
107
|
-
setter = (_) => undefined;
|
|
108
|
-
} else {
|
|
109
|
-
defaultValue = getDefaultParamValue(param, this);
|
|
110
|
-
setter = this.allocateSetter(name, defaultValue);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if ("select" in param) {
|
|
114
|
-
defaultValue ??= getDefaultParamValue(param, this);
|
|
115
|
-
// Set initial value so that production rules in shaders can be generated, etc.
|
|
116
|
-
setter(defaultValue);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
this.#paramConfigs.set(name, param);
|
|
120
|
-
|
|
121
|
-
return setter;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
*
|
|
126
|
-
* @param {string} paramName
|
|
127
|
-
* @param {T} initialValue
|
|
128
|
-
* @param {boolean} [passive] If true, the setter will not notify listeners when the value changes.
|
|
129
|
-
* @returns {(value: T) => void}
|
|
130
|
-
* @template T
|
|
131
|
-
*/
|
|
132
|
-
allocateSetter(paramName, initialValue, passive = false) {
|
|
133
|
-
validateParameterName(paramName);
|
|
134
|
-
|
|
135
|
-
if (this.#allocatedSetters.has(paramName)) {
|
|
136
|
-
throw new Error(
|
|
137
|
-
"Setter already allocated for parameter: " + paramName
|
|
138
|
-
);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/** @type {(value: any) => void} */
|
|
142
|
-
const setter = (value) => {
|
|
143
|
-
const previous = this.#paramValues.get(paramName);
|
|
144
|
-
if (value !== previous) {
|
|
145
|
-
this.#paramValues.set(paramName, value);
|
|
146
|
-
|
|
147
|
-
const listeners = this.paramListeners.get(paramName);
|
|
148
|
-
if (listeners && !passive) {
|
|
149
|
-
for (const listener of listeners) {
|
|
150
|
-
listener();
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
setter(initialValue);
|
|
157
|
-
|
|
158
|
-
this.#allocatedSetters.set(paramName, setter);
|
|
159
|
-
|
|
160
|
-
return setter;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Gets an existing setter for a parameter. Throws if the setter is not found.
|
|
165
|
-
* @param {string} paramName
|
|
166
|
-
*/
|
|
167
|
-
getSetter(paramName) {
|
|
168
|
-
const setter = this.#allocatedSetters.get(paramName);
|
|
169
|
-
if (!setter) {
|
|
170
|
-
throw new Error("Setter not found for parameter: " + paramName);
|
|
171
|
-
}
|
|
172
|
-
return setter;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Get the value of a parameter from this mediator.
|
|
177
|
-
* @param {string} paramName
|
|
178
|
-
*/
|
|
179
|
-
getValue(paramName) {
|
|
180
|
-
return this.#paramValues.get(paramName);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Subscribe to changes of a parameter's value. The listener is called only
|
|
185
|
-
* when the stored value changes. For expression parameters, the listener is
|
|
186
|
-
* called when upstream changes re-evaluate to a different value.
|
|
187
|
-
*
|
|
188
|
-
* @param {string} paramName
|
|
189
|
-
* @param {() => void} listener
|
|
190
|
-
* @returns {() => void}
|
|
191
|
-
*/
|
|
192
|
-
subscribe(paramName, listener) {
|
|
193
|
-
validateParameterName(paramName);
|
|
194
|
-
const mediator = this.findMediatorForParam(paramName);
|
|
195
|
-
if (!mediator) {
|
|
196
|
-
throw new Error("Parameter not found: " + paramName);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const listeners = mediator.paramListeners.get(paramName) ?? new Set();
|
|
200
|
-
mediator.paramListeners.set(paramName, listeners);
|
|
201
|
-
listeners.add(listener);
|
|
202
|
-
|
|
203
|
-
return () => {
|
|
204
|
-
listeners.delete(listener);
|
|
205
|
-
if (!listeners.size) {
|
|
206
|
-
mediator.paramListeners.delete(paramName);
|
|
207
|
-
}
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Get the value of a parameter from this mediator or the ancestors.
|
|
213
|
-
* @param {string} paramName
|
|
214
|
-
*/
|
|
215
|
-
findValue(paramName) {
|
|
216
|
-
const mediator = this.findMediatorForParam(paramName);
|
|
217
|
-
return mediator?.getValue(paramName);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Returns configs for all parameters that have been registered using `registerParam`.
|
|
222
|
-
*/
|
|
223
|
-
get paramConfigs() {
|
|
224
|
-
return /** @type {ReadonlyMap<string, Parameter>} */ (
|
|
225
|
-
this.#paramConfigs
|
|
226
|
-
);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
*
|
|
231
|
-
* @param {string} paramName
|
|
232
|
-
* @returns {ParamMediator}
|
|
233
|
-
*/
|
|
234
|
-
findMediatorForParam(paramName) {
|
|
235
|
-
if (this.#paramValues.has(paramName)) {
|
|
236
|
-
return this;
|
|
237
|
-
} else {
|
|
238
|
-
return this.#parentFinder()?.findMediatorForParam(paramName);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// TODO: deallocateSetter
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* Parse expr and return a function that returns the value of the parameter.
|
|
246
|
-
*
|
|
247
|
-
* @param {string} expr
|
|
248
|
-
*/
|
|
249
|
-
createExpression(expr) {
|
|
250
|
-
if (this.#expressions.has(expr)) {
|
|
251
|
-
return this.#expressions.get(expr);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
const globalObject = {};
|
|
255
|
-
|
|
256
|
-
/** @type {ExprRefFunction} */
|
|
257
|
-
const fn = /** @type {any} */ (createFunction(expr, globalObject));
|
|
258
|
-
|
|
259
|
-
/** @type {Map<string, ParamMediator>} */
|
|
260
|
-
const mediatorsForParams = new Map();
|
|
261
|
-
|
|
262
|
-
for (const param of fn.globals) {
|
|
263
|
-
const mediator = this.findMediatorForParam(param);
|
|
264
|
-
if (!mediator) {
|
|
265
|
-
throw new Error(
|
|
266
|
-
`Unknown variable "${param}" in expression: ${expr}`
|
|
267
|
-
);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
mediatorsForParams.set(param, mediator);
|
|
271
|
-
|
|
272
|
-
Object.defineProperty(globalObject, param, {
|
|
273
|
-
enumerable: true,
|
|
274
|
-
get() {
|
|
275
|
-
return mediator.getValue(param);
|
|
276
|
-
},
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
// TODO: There should be a way to "materialize" the global object when
|
|
280
|
-
// it is used in expressions in transformation batches, i.e., when the same
|
|
281
|
-
// expression is applied to multiple data objects. In that case, the global
|
|
282
|
-
// object remains constant and the Map lookups cause unnecessary overhead.
|
|
283
|
-
|
|
284
|
-
// Keep track of them so that they can be detached later
|
|
285
|
-
const myListeners = new Set();
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
*
|
|
289
|
-
* @param {() => void} listener
|
|
290
|
-
*/
|
|
291
|
-
fn.addListener = (listener) => {
|
|
292
|
-
for (const [param, mediator] of mediatorsForParams) {
|
|
293
|
-
const listeners =
|
|
294
|
-
mediator.paramListeners.get(param) ?? new Set();
|
|
295
|
-
mediator.paramListeners.set(param, listeners);
|
|
296
|
-
|
|
297
|
-
listeners.add(listener);
|
|
298
|
-
myListeners.add(listener);
|
|
299
|
-
}
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* @param {() => void} listener
|
|
304
|
-
*/
|
|
305
|
-
fn.removeListener = (listener) => {
|
|
306
|
-
for (const [param, mediator] of mediatorsForParams) {
|
|
307
|
-
mediator.paramListeners.get(param)?.delete(listener);
|
|
308
|
-
}
|
|
309
|
-
myListeners.delete(listener);
|
|
310
|
-
};
|
|
311
|
-
|
|
312
|
-
/**
|
|
313
|
-
* Detach listeners. This must be called if the expression is no longer used.
|
|
314
|
-
* TODO: What if the expression is used in multiple places?
|
|
315
|
-
*/
|
|
316
|
-
fn.invalidate = () => {
|
|
317
|
-
for (const [param, mediator] of mediatorsForParams) {
|
|
318
|
-
for (const listener of myListeners) {
|
|
319
|
-
mediator.paramListeners.get(param)?.delete(listener);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
myListeners.clear();
|
|
323
|
-
};
|
|
324
|
-
|
|
325
|
-
// TODO: This should contain unique identifier for each parameter.
|
|
326
|
-
// As the same parameter name may be used in different branches of the
|
|
327
|
-
// hierarchy, they should be distinguished by a unique identifier, e.g.,
|
|
328
|
-
// a serial number of something similar.
|
|
329
|
-
fn.identifier = () => fn.code;
|
|
330
|
-
|
|
331
|
-
this.#expressions.set(expr, fn);
|
|
332
|
-
|
|
333
|
-
return fn;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
/**
|
|
337
|
-
* A convenience method for evaluating an expression.
|
|
338
|
-
*
|
|
339
|
-
* @param {string} expr
|
|
340
|
-
*/
|
|
341
|
-
evaluateAndGet(expr) {
|
|
342
|
-
const fn = this.createExpression(expr);
|
|
343
|
-
return fn();
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
/**
|
|
347
|
-
* Returns true if this ParamMediator has any parameters that are point selections.
|
|
348
|
-
* Point selections necessitate the use of uniqueIds in the data.
|
|
349
|
-
*
|
|
350
|
-
* @returns {boolean}
|
|
351
|
-
*/
|
|
352
|
-
hasPointSelections() {
|
|
353
|
-
for (const param of this.#paramConfigs.values()) {
|
|
354
|
-
if (isSelectionParameter(param)) {
|
|
355
|
-
const select = param.select;
|
|
356
|
-
if (isString(select)) {
|
|
357
|
-
if (select == "point") {
|
|
358
|
-
return true;
|
|
359
|
-
}
|
|
360
|
-
} else if (select.type == "point") {
|
|
361
|
-
return true;
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
return false;
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
/**
|
|
371
|
-
* @param {any} x
|
|
372
|
-
* @returns {x is import("../spec/parameter.js").ExprRef}
|
|
373
|
-
*/
|
|
374
|
-
export function isExprRef(x) {
|
|
375
|
-
return typeof x == "object" && x != null && "expr" in x && isString(x.expr);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
/**
|
|
379
|
-
* Removes ExprRef from the type and checks that the value is not an ExprRef.
|
|
380
|
-
* This is designed to be used with `activateExprRefProps`.
|
|
381
|
-
*
|
|
382
|
-
* @param {T | import("../spec/parameter.js").ExprRef} x
|
|
383
|
-
* @template T
|
|
384
|
-
* @returns {T}
|
|
385
|
-
*/
|
|
386
|
-
export function withoutExprRef(x) {
|
|
387
|
-
if (isExprRef(x)) {
|
|
388
|
-
throw new Error(
|
|
389
|
-
`ExprRef ${JSON.stringify(
|
|
390
|
-
x
|
|
391
|
-
)} not allowed here. Expected a scalar value.`
|
|
392
|
-
);
|
|
393
|
-
}
|
|
394
|
-
return /** @type {T} */ (x);
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
/**
|
|
398
|
-
* @param {Parameter} param
|
|
399
|
-
* @returns {param is import("../spec/parameter.js").VariableParameter}
|
|
400
|
-
*/
|
|
401
|
-
export function isVariableParameter(param) {
|
|
402
|
-
return ("expr" in param || "bind" in param) && !("select" in param);
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
/**
|
|
406
|
-
* @param {Parameter} param
|
|
407
|
-
* @returns {param is import("../spec/parameter.js").SelectionParameter}
|
|
408
|
-
*/
|
|
409
|
-
export function isSelectionParameter(param) {
|
|
410
|
-
return !("expr" in param || "bind" in param) && "select" in param;
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
/**
|
|
414
|
-
* Computes the default value for a parameter specification.
|
|
415
|
-
*
|
|
416
|
-
* @param {Parameter} param
|
|
417
|
-
* @param {ParamMediator} [paramMediator]
|
|
418
|
-
* @param {ExprRefFunction} [exprFn]
|
|
419
|
-
* @returns {any}
|
|
420
|
-
*/
|
|
421
|
-
export function getDefaultParamValue(param, paramMediator, exprFn) {
|
|
422
|
-
if ("select" in param) {
|
|
423
|
-
const select = asSelectionConfig(param.select);
|
|
424
|
-
if (isPointSelectionConfig(select)) {
|
|
425
|
-
return select.toggle
|
|
426
|
-
? createMultiPointSelection()
|
|
427
|
-
: createSinglePointSelection(null);
|
|
428
|
-
}
|
|
429
|
-
if (isIntervalSelectionConfig(select)) {
|
|
430
|
-
if (!select.encodings) {
|
|
431
|
-
throw new Error(
|
|
432
|
-
`Interval selection "${param.name}" must have encodings defined!`
|
|
433
|
-
);
|
|
434
|
-
}
|
|
435
|
-
return createIntervalSelection(select.encodings);
|
|
436
|
-
}
|
|
437
|
-
throw new Error(
|
|
438
|
-
`Unknown selection config for parameter "${param.name}".`
|
|
439
|
-
);
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
if ("expr" in param) {
|
|
443
|
-
const expr =
|
|
444
|
-
exprFn ??
|
|
445
|
-
paramMediator?.createExpression(/** @type {string} */ (param.expr));
|
|
446
|
-
if (!expr) {
|
|
447
|
-
throw new Error(
|
|
448
|
-
`Cannot evaluate expression for parameter "${param.name}".`
|
|
449
|
-
);
|
|
450
|
-
}
|
|
451
|
-
return expr(null);
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
if ("value" in param) {
|
|
455
|
-
return param.value;
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
return null;
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
/**
|
|
462
|
-
* Takes a record of properties that may have ExprRefs as values. Converts the
|
|
463
|
-
* ExprRefs to getters and setups a listener that is called when any of the
|
|
464
|
-
* expressions (upstream parameters) change.
|
|
465
|
-
*
|
|
466
|
-
* @param {ParamMediator} paramMediator
|
|
467
|
-
* @param {T} props The properties object
|
|
468
|
-
* @param {(props: (keyof T)[]) => void} [listener] Listener to be called when any of the expressions change
|
|
469
|
-
* @returns T
|
|
470
|
-
* @template {Record<string, any | import("../spec/parameter.js").ExprRef>} T
|
|
471
|
-
*/
|
|
472
|
-
export function activateExprRefProps(paramMediator, props, listener) {
|
|
473
|
-
/** @type {Record<string, any | import("../spec/parameter.js").ExprRef>} */
|
|
474
|
-
const activatedProps = { ...props };
|
|
475
|
-
|
|
476
|
-
/** @type {(keyof T)[]} */
|
|
477
|
-
const alteredProps = [];
|
|
478
|
-
|
|
479
|
-
const batchPropertyChange = (/** @type {keyof T} */ prop) => {
|
|
480
|
-
alteredProps.push(prop);
|
|
481
|
-
if (alteredProps.length === 1) {
|
|
482
|
-
queueMicrotask(() => {
|
|
483
|
-
listener(alteredProps.slice());
|
|
484
|
-
alteredProps.length = 0;
|
|
485
|
-
});
|
|
486
|
-
}
|
|
487
|
-
};
|
|
488
|
-
|
|
489
|
-
for (const [key, value] of Object.entries(props)) {
|
|
490
|
-
if (isExprRef(value)) {
|
|
491
|
-
const fn = paramMediator.createExpression(value.expr);
|
|
492
|
-
if (listener) {
|
|
493
|
-
fn.addListener(() => batchPropertyChange(key));
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
Object.defineProperty(activatedProps, key, {
|
|
497
|
-
enumerable: true,
|
|
498
|
-
get() {
|
|
499
|
-
return fn();
|
|
500
|
-
},
|
|
501
|
-
});
|
|
502
|
-
} else {
|
|
503
|
-
activatedProps[key] = value;
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
return /** @type {T} */ (activatedProps);
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
/**
|
|
511
|
-
* Validates a parameter name. If the name is invalid, throws an error.
|
|
512
|
-
* Otherwise, returns the name.
|
|
513
|
-
*
|
|
514
|
-
* @param {string} name
|
|
515
|
-
* @returns {string} the name
|
|
516
|
-
*/
|
|
517
|
-
export function validateParameterName(name) {
|
|
518
|
-
if (!/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(name)) {
|
|
519
|
-
throw new Error(
|
|
520
|
-
`Invalid parameter name: ${name}. Must be a valid JavaScript identifier.`
|
|
521
|
-
);
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
return name;
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
/**
|
|
528
|
-
* Creates a function that always returns the same value.
|
|
529
|
-
* Provides functionality for creating a constant expression reference.
|
|
530
|
-
* They just do nothing.
|
|
531
|
-
*
|
|
532
|
-
* @param {any} value
|
|
533
|
-
* @returns {ExprRefFunction}
|
|
534
|
-
*/
|
|
535
|
-
export function makeConstantExprRef(value) {
|
|
536
|
-
return Object.assign(() => value, {
|
|
537
|
-
addListener: () => /** @type {void} */ (undefined),
|
|
538
|
-
removeListener: () => /** @type {void} */ (undefined),
|
|
539
|
-
invalidate: () => /** @type {void} */ (undefined),
|
|
540
|
-
identifier: () => "constant",
|
|
541
|
-
fields: [],
|
|
542
|
-
globals: [],
|
|
543
|
-
code: JSON.stringify(value),
|
|
544
|
-
});
|
|
545
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"paramMediator.test.d.ts","sourceRoot":"","sources":["../../../src/view/paramMediator.test.js"],"names":[],"mappings":""}
|