@genome-spy/core 0.44.0 → 0.45.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.
Files changed (96) hide show
  1. package/dist/bundle/index.es.js +6123 -5482
  2. package/dist/bundle/index.js +211 -148
  3. package/dist/schema.json +329 -79
  4. package/dist/src/data/collector.d.ts.map +1 -1
  5. package/dist/src/data/collector.js +4 -2
  6. package/dist/src/data/flowOptimizer.test.js +12 -3
  7. package/dist/src/data/sources/dataUtils.d.ts.map +1 -1
  8. package/dist/src/data/sources/dataUtils.js +3 -1
  9. package/dist/src/data/sources/lazy/bigBedSource.d.ts +1 -1
  10. package/dist/src/data/sources/lazy/bigBedSource.d.ts.map +1 -1
  11. package/dist/src/data/sources/lazy/bigBedSource.js +52 -20
  12. package/dist/src/data/sources/lazy/bigWigSource.d.ts +0 -1
  13. package/dist/src/data/sources/lazy/bigWigSource.d.ts.map +1 -1
  14. package/dist/src/data/sources/lazy/bigWigSource.js +30 -4
  15. package/dist/src/data/sources/lazy/singleAxisLazySource.d.ts.map +1 -1
  16. package/dist/src/data/sources/lazy/singleAxisLazySource.js +0 -2
  17. package/dist/src/data/sources/lazy/singleAxisWindowedSource.d.ts +6 -2
  18. package/dist/src/data/sources/lazy/singleAxisWindowedSource.d.ts.map +1 -1
  19. package/dist/src/data/sources/lazy/singleAxisWindowedSource.js +38 -20
  20. package/dist/src/data/sources/sequenceSource.d.ts.map +1 -1
  21. package/dist/src/data/sources/sequenceSource.js +14 -5
  22. package/dist/src/data/sources/sequenceSource.test.js +23 -5
  23. package/dist/src/data/sources/urlSource.d.ts.map +1 -1
  24. package/dist/src/data/sources/urlSource.js +15 -2
  25. package/dist/src/data/transforms/aggregate.d.ts.map +1 -1
  26. package/dist/src/data/transforms/aggregate.js +5 -2
  27. package/dist/src/encoder/encoder.d.ts +2 -4
  28. package/dist/src/encoder/encoder.d.ts.map +1 -1
  29. package/dist/src/encoder/encoder.js +8 -8
  30. package/dist/src/encoder/encoder.test.js +3 -0
  31. package/dist/src/genomeSpy.d.ts +7 -5
  32. package/dist/src/genomeSpy.d.ts.map +1 -1
  33. package/dist/src/genomeSpy.js +109 -132
  34. package/dist/src/gl/glslScaleGenerator.js +1 -1
  35. package/dist/src/index.d.ts +1 -1
  36. package/dist/src/index.d.ts.map +1 -1
  37. package/dist/src/index.js +1 -1
  38. package/dist/src/marks/mark.d.ts +8 -5
  39. package/dist/src/marks/mark.d.ts.map +1 -1
  40. package/dist/src/marks/mark.js +56 -12
  41. package/dist/src/marks/point.common.glsl.js +1 -1
  42. package/dist/src/marks/point.d.ts +1 -4
  43. package/dist/src/marks/point.d.ts.map +1 -1
  44. package/dist/src/marks/point.js +31 -23
  45. package/dist/src/marks/point.vertex.glsl.js +1 -1
  46. package/dist/src/marks/text.d.ts.map +1 -1
  47. package/dist/src/marks/text.js +15 -7
  48. package/dist/src/spec/data.d.ts +11 -10
  49. package/dist/src/spec/mark.d.ts +11 -21
  50. package/dist/src/spec/parameter.d.ts +11 -7
  51. package/dist/src/spec/root.d.ts +0 -8
  52. package/dist/src/spec/title.d.ts +5 -4
  53. package/dist/src/spec/view.d.ts +19 -4
  54. package/dist/src/styles/genome-spy.css.d.ts +1 -1
  55. package/dist/src/styles/genome-spy.css.d.ts.map +1 -1
  56. package/dist/src/styles/genome-spy.css.js +52 -5
  57. package/dist/src/styles/genome-spy.scss +63 -10
  58. package/dist/src/styles/update.sh +6 -0
  59. package/dist/src/tooltip/dataTooltipHandler.js +1 -1
  60. package/dist/src/tooltip/refseqGeneTooltipHandler.js +1 -1
  61. package/dist/src/tooltip/tooltipHandler.d.ts +1 -1
  62. package/dist/src/tooltip/tooltipHandler.d.ts.map +1 -1
  63. package/dist/src/tooltip/tooltipHandler.ts +1 -1
  64. package/dist/src/types/embedApi.d.ts +6 -0
  65. package/dist/src/types/viewContext.d.ts +2 -3
  66. package/dist/src/utils/debounce.d.ts +2 -2
  67. package/dist/src/utils/debounce.d.ts.map +1 -1
  68. package/dist/src/utils/debounce.js +5 -2
  69. package/dist/src/utils/expression.d.ts +2 -2
  70. package/dist/src/utils/expression.d.ts.map +1 -1
  71. package/dist/src/utils/expression.js +1 -1
  72. package/dist/src/utils/formatObject.d.ts +2 -2
  73. package/dist/src/utils/formatObject.d.ts.map +1 -1
  74. package/dist/src/utils/formatObject.js +2 -2
  75. package/dist/src/utils/inputBinding.d.ts +5 -0
  76. package/dist/src/utils/inputBinding.d.ts.map +1 -0
  77. package/dist/src/utils/inputBinding.js +115 -0
  78. package/dist/src/utils/ui/tooltip.js +1 -1
  79. package/dist/src/view/paramMediator.d.ts +108 -0
  80. package/dist/src/view/paramMediator.d.ts.map +1 -0
  81. package/dist/src/view/paramMediator.js +337 -0
  82. package/dist/src/view/paramMediator.test.js +211 -0
  83. package/dist/src/view/scaleResolution.d.ts.map +1 -1
  84. package/dist/src/view/scaleResolution.js +11 -6
  85. package/dist/src/view/view.d.ts +4 -1
  86. package/dist/src/view/view.d.ts.map +1 -1
  87. package/dist/src/view/view.js +19 -5
  88. package/dist/src/view/viewFactory.d.ts.map +1 -1
  89. package/dist/src/view/viewFactory.js +45 -0
  90. package/dist/src/view/viewUtils.d.ts +5 -1
  91. package/dist/src/view/viewUtils.d.ts.map +1 -1
  92. package/dist/src/view/viewUtils.js +9 -4
  93. package/package.json +16 -17
  94. package/dist/src/paramBroker.d.ts +0 -46
  95. package/dist/src/paramBroker.d.ts.map +0 -1
  96. package/dist/src/paramBroker.js +0 -118
@@ -1 +1 @@
1
- {"version":3,"file":"viewFactory.d.ts","sourceRoot":"","sources":["../../../src/view/viewFactory.js"],"names":[],"mappings":"AAuMA;;;;GAIG;AACH,iHAEC;AAED;;;;GAIG;AACH,mHAEC;AAED;;;;GAIG;AACH,mHAOC;AAED;;;;GAIG;AACH,yIAMC;AAED;;;;GAIG;AACH,mCAHW,MAAM,gDAKhB;AAED;;;;GAIG;AACH,uHAEC;AAED;;;;GAIG;AACH,6FAEC;AAED;;;;GAIG;AACH,qHAEC;AAED;;;;GAIG;AACH,2HAOC;AAzRD,wCAAyC;AAEzC;;;;GAIG;AAEH;;GAEG;AACH;IAiBI;;OAEG;IACH,sBAFW,kBAAkB,EAoC5B;IAjCG,2CAA2C;IAC3C,SADW,SAAS,kBAAkB,CAAC,CAKtC;IA8BL;;;OAGG;IACH,qEAlDiC,OAAO,yHAC2B,OAAO,oBAAoB,EAAE,OAAO,eAAe,OAAO,WAAW,EAAE,OAAO,gBAAgB,MAAM,KAAK,IAAI,QAmD/K;IAED;;;;;;OAMG;IACH,wHAJW,OAAO,oBAAoB,EAAE,OAAO,eACpC,OAAO,WAAW,EAAE,OAAO,gBAC3B,MAAM,QAmBhB;IAED;;;;OAIG;IACH,iGAQC;IAED;;;;;;;;;;OAUG;IACH,uKALW,OAAO,oBAAoB,EAAE,OAAO,eACpC,OAAO,WAAW,EAAE,OAAO,gBAC3B,MAAM,4DACc,IAAI,iBA4DlC;;CACJ;;kBAtLa,OAAO;eACP,OAAO;;iBAfJ,WAAW"}
1
+ {"version":3,"file":"viewFactory.d.ts","sourceRoot":"","sources":["../../../src/view/viewFactory.js"],"names":[],"mappings":"AAoPA;;;;GAIG;AACH,iHAEC;AAED;;;;GAIG;AACH,mHAEC;AAED;;;;GAIG;AACH,mHAOC;AAED;;;;GAIG;AACH,yIAMC;AAED;;;;GAIG;AACH,mCAHW,MAAM,gDAKhB;AAED;;;;GAIG;AACH,uHAEC;AAED;;;;GAIG;AACH,6FAEC;AAED;;;;GAIG;AACH,qHAEC;AAED;;;;GAIG;AACH,2HAOC;AAtUD,wCAAyC;AAEzC;;;;GAIG;AAEH;;GAEG;AACH;IAiBI;;OAEG;IACH,sBAFW,kBAAkB,EAoC5B;IAjCG,2CAA2C;IAC3C,SADW,SAAS,kBAAkB,CAAC,CAKtC;IA8BL;;;OAGG;IACH,qEAlDiC,OAAO,yHAC2B,OAAO,oBAAoB,EAAE,OAAO,eAAe,OAAO,WAAW,EAAE,OAAO,gBAAgB,MAAM,KAAK,IAAI,QAmD/K;IAED;;;;;;OAMG;IACH,wHAJW,OAAO,oBAAoB,EAAE,OAAO,eACpC,OAAO,WAAW,EAAE,OAAO,gBAC3B,MAAM,QAmBhB;IAED;;;;OAIG;IACH,iGAQC;IAED;;;;;;;;;;OAUG;IACH,uKALW,OAAO,oBAAoB,EAAE,OAAO,eACpC,OAAO,WAAW,EAAE,OAAO,gBAC3B,MAAM,4DACc,IAAI,iBA8DlC;;CACJ;;kBAxLa,OAAO;eACP,OAAO;;iBAfJ,WAAW"}
@@ -158,6 +158,8 @@ export class ViewFactory {
158
158
  if (validator) {
159
159
  validator(viewSpec);
160
160
  }
161
+
162
+ applyParamsToImportedSpec(viewSpec, spec.import);
161
163
  } else {
162
164
  throw new ViewError(
163
165
  "Importing views is not allowed!",
@@ -197,6 +199,49 @@ export class ViewFactory {
197
199
  }
198
200
  }
199
201
 
202
+ /**
203
+ *
204
+ * @param {ViewSpec} importedSpec
205
+ * @param {import("../spec/view.js").ImportParams} importParams
206
+ */
207
+ function applyParamsToImportedSpec(importedSpec, importParams) {
208
+ if (importParams.name != null) {
209
+ importedSpec.name = importParams.name;
210
+ }
211
+
212
+ const params = isArray(importParams.params)
213
+ ? importParams.params
214
+ : isObject(importParams.params)
215
+ ? Object.entries(importParams.params).map(([name, value]) => ({
216
+ name,
217
+ value,
218
+ }))
219
+ : [];
220
+
221
+ if (!params.length) {
222
+ return;
223
+ }
224
+
225
+ importedSpec.params ??= [];
226
+
227
+ // Replace overridden parameters
228
+ for (const param of params) {
229
+ const index = importedSpec.params.findIndex(
230
+ (p) => p.name == param.name
231
+ );
232
+ if (index >= 0) {
233
+ importedSpec.params[index] = param;
234
+ }
235
+ }
236
+
237
+ // Add missing parameters
238
+ for (const param of params) {
239
+ if (!importedSpec.params.some((p) => p.name == param.name)) {
240
+ importedSpec.params.push(param);
241
+ }
242
+ }
243
+ }
244
+
200
245
  /**
201
246
  *
202
247
  * @param {ViewSpec} spec
@@ -82,7 +82,11 @@ export function findUniqueViewNames(root: View): Set<string>;
82
82
  /**
83
83
  * @param {View} viewRoot
84
84
  */
85
- export function calculateCanvasSize(viewRoot: View): {
85
+ export function calculateViewRootSize(viewRoot: View): import("./layout/flexLayout.js").FlexDimensions;
86
+ /**
87
+ * @param {import("./layout/flexLayout.js").FlexDimensions} viewRootSize
88
+ */
89
+ export function calculateCanvasSize(viewRootSize: import("./layout/flexLayout.js").FlexDimensions): {
86
90
  width: number;
87
91
  height: number;
88
92
  };
@@ -1 +1 @@
1
- {"version":3,"file":"viewUtils.d.ts","sourceRoot":"","sources":["../../../src/view/viewUtils.js"],"names":[],"mappings":"AAWA;;;;GAIG;AACH,qCAHW,OAAO,oBAAoB,EAAE,UAAU,GAAG,OAAO,iBAAiB,EAAE,YAAY,yBAK1F;AAED;;;;GAIG;AACH,oCAHW,OAAO,oBAAoB,EAAE,aAAa,GAAG,OAAO,iBAAiB,EAAE,YAAY,wBAQ7F;AAED;;;;GAIG;AACH,uCAHW,MAAM,0BAKhB;AAED;;;GAGG;AACH,+BAFW,IAAI,wCAMd;AAED;;;;GAIG;AACH,wCAFW,IAAI,UASd;AAED;;GAEG;AACH,kDAFW,IAAI,QAiBd;AAED;;;;;;;GAOG;AACH,4CAFW,IAAI,QAUd;AAED;;;;GAIG;AACH,qCAJW,IAAI,iBACJ,OAAO,qBAAqB,EAAE,OAAO,CAAC,IAAI,CAAC,wDAcrD;AAED;;;GAGG;AACH,wCAFW,IAAI;UAGO,QAAQ;aAAW,OAAO,oBAAoB,EAAE,OAAO;WAAS,OAAO,oBAAoB,EAAE,KAAK;UAAQ,OAAO,oBAAoB,EAAE,IAAI;IAqBhK;AAED;;;;GAIG;AACH,2CAJW,OAAO,iBAAiB,EAAE,UAAU,WACpC,MAAM,eACN,OAAO,yBAAyB,EAAE,OAAO,gBA8BnD;AAED;;GAEG;AACH,gDAFoB,IAAI,QAAE,IAAI,EAAE,KAAE,IAAI,+BAkBrC;AAED;;;;;;GAMG;AACH,4CAJW,IAAI,QACJ,MAAM,GACJ,IAAI,EAAE,CAalB;AAED;;;GAGG;AACH,0CAFW,IAAI,eAqBd;AAOD;;GAEG;AACH,8CAFW,IAAI;;;EAgBd;AAnBM,uCAFI,MAAM,WAE0D;iBAxP9B,WAAW;qBAFnC,eAAe"}
1
+ {"version":3,"file":"viewUtils.d.ts","sourceRoot":"","sources":["../../../src/view/viewUtils.js"],"names":[],"mappings":"AAWA;;;;GAIG;AACH,qCAHW,OAAO,oBAAoB,EAAE,UAAU,GAAG,OAAO,iBAAiB,EAAE,YAAY,yBAK1F;AAED;;;;GAIG;AACH,oCAHW,OAAO,oBAAoB,EAAE,aAAa,GAAG,OAAO,iBAAiB,EAAE,YAAY,wBAQ7F;AAED;;;;GAIG;AACH,uCAHW,MAAM,0BAKhB;AAED;;;GAGG;AACH,+BAFW,IAAI,wCAMd;AAED;;;;GAIG;AACH,wCAFW,IAAI,UASd;AAED;;GAEG;AACH,kDAFW,IAAI,QAiBd;AAED;;;;;;;GAOG;AACH,4CAFW,IAAI,QAUd;AAED;;;;GAIG;AACH,qCAJW,IAAI,iBACJ,OAAO,qBAAqB,EAAE,OAAO,CAAC,IAAI,CAAC,wDAcrD;AAED;;;GAGG;AACH,wCAFW,IAAI;UAGO,QAAQ;aAAW,OAAO,oBAAoB,EAAE,OAAO;WAAS,OAAO,oBAAoB,EAAE,KAAK;UAAQ,OAAO,oBAAoB,EAAE,IAAI;IAqBhK;AAED;;;;GAIG;AACH,2CAJW,OAAO,iBAAiB,EAAE,UAAU,WACpC,MAAM,eACN,OAAO,yBAAyB,EAAE,OAAO,gBA8BnD;AAED;;GAEG;AACH,gDAFoB,IAAI,QAAE,IAAI,EAAE,KAAE,IAAI,+BAkBrC;AAED;;;;;;GAMG;AACH,4CAJW,IAAI,QACJ,MAAM,GACJ,IAAI,EAAE,CAalB;AAED;;;GAGG;AACH,0CAFW,IAAI,eAqBd;AAOD;;GAEG;AACH,gDAFW,IAAI,mDAId;AAED;;GAEG;AACH,kDAFW,OAAO,wBAAwB,EAAE,cAAc;;;EAczD;AAxBM,uCAFI,MAAM,WAE0D;iBAxP9B,WAAW;qBAFnC,eAAe"}
@@ -256,9 +256,14 @@ export const isCustomViewName = (name) => !/^(layer|concat)\d+$/.test(name);
256
256
  /**
257
257
  * @param {View} viewRoot
258
258
  */
259
- export function calculateCanvasSize(viewRoot) {
260
- const size = viewRoot.getSize().addPadding(viewRoot.getOverhang());
259
+ export function calculateViewRootSize(viewRoot) {
260
+ return viewRoot.getSize().addPadding(viewRoot.getOverhang());
261
+ }
261
262
 
263
+ /**
264
+ * @param {import("./layout/flexLayout.js").FlexDimensions} viewRootSize
265
+ */
266
+ export function calculateCanvasSize(viewRootSize) {
262
267
  // If a dimension has an absolutely specified size (in pixels), use it for the canvas size.
263
268
  // However, if the dimension has a growing component, the canvas should be fit to the
264
269
  // container.
@@ -267,7 +272,7 @@ export function calculateCanvasSize(viewRoot) {
267
272
  /** @param {import("./layout/flexLayout.js").SizeDef} dim */
268
273
  const f = (dim) => (dim.grow > 0 ? undefined : dim.px);
269
274
  return {
270
- width: f(size.width),
271
- height: f(size.height),
275
+ width: f(viewRootSize.width),
276
+ height: f(viewRootSize.height),
272
277
  };
273
278
  }
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "contributors": [],
9
9
  "license": "MIT",
10
- "version": "0.44.0",
10
+ "version": "0.45.0",
11
11
  "jsdelivr": "dist/bundle/index.js",
12
12
  "unpkg": "dist/bundle/index.js",
13
13
  "browser": "dist/bundle/index.js",
@@ -43,27 +43,26 @@
43
43
  "@gmod/gff": "^1.3.0",
44
44
  "@gmod/indexedfasta": "^2.0.4",
45
45
  "@gmod/tabix": "^1.5.13",
46
- "@types/d3-array": "^3.0.2",
47
- "@types/d3-dsv": "^3.0.0",
48
- "@types/d3-ease": "^3.0.0",
49
- "@types/d3-format": "^3.0.1",
50
- "@types/d3-interpolate": "^3.0.1",
51
- "@types/d3-scale": "^4.0.2",
46
+ "@types/d3-array": "^3.2.1",
47
+ "@types/d3-dsv": "^3.0.7",
48
+ "@types/d3-ease": "^3.0.2",
49
+ "@types/d3-format": "^3.0.4",
50
+ "@types/d3-interpolate": "^3.0.4",
51
+ "@types/d3-scale": "^4.0.8",
52
52
  "buffer": "^6.0.3",
53
- "d3-array": "^3.1.1",
54
- "d3-color": "^3.0.1",
53
+ "d3-array": "^3.2.4",
54
+ "d3-color": "^3.1.0",
55
55
  "d3-ease": "^3.0.1",
56
- "d3-format": "^3.0.1",
56
+ "d3-format": "^3.1.0",
57
57
  "events": "^3.3.0",
58
58
  "flatqueue": "^2.0.3",
59
59
  "internmap": "^2.0.3",
60
- "lit": "^3.1.0",
61
- "lit-html": "^3.0.2",
60
+ "lit": "^3.1.2",
62
61
  "twgl.js": "^4.19.1",
63
- "vega-expression": "^5.0.0",
64
- "vega-loader": "^4.4.0",
65
- "vega-scale": "^7.1.1",
66
- "vega-util": "^1.16.0"
62
+ "vega-expression": "^5.1.0",
63
+ "vega-loader": "^4.5.1",
64
+ "vega-scale": "^7.3.1",
65
+ "vega-util": "^1.17.2"
67
66
  },
68
- "gitHead": "87fe1d80557bb9f1c721f9ce142b437e956204f9"
67
+ "gitHead": "7b6de39e856c85c755b2134bf451780c8b7922bb"
69
68
  }
@@ -1,46 +0,0 @@
1
- /**
2
- * A class that manages parameters and expressions. Still a work in progress.
3
- *
4
- * TODO: Write tests for this class.
5
- *
6
- * This should eventually handle the following:
7
- * - Parameter registration
8
- * - Dependency tracking
9
- * - Calling observers when a parameter changes
10
- * - Somehow saving parameter "state" (in bookmarks)
11
- * - Maybe something else
12
- *
13
- * @typedef {import("./utils/expression.js").ExpressionFunction & { addListener: (listener: () => void) => void, invalidate: () => void}} ExprRefFunction
14
- */
15
- export default class ParamBroker {
16
- /**
17
- *
18
- * @param {string} paramName
19
- * @returns {(value: any) => void}
20
- */
21
- allocateSetter(paramName: string): (value: any) => void;
22
- /**
23
- * Parse expr and return a function that returns the value of the parameter.
24
- *
25
- * @param {string} expr
26
- */
27
- createExpression(expr: string): ExprRefFunction;
28
- #private;
29
- }
30
- /**
31
- * A class that manages parameters and expressions. Still a work in progress.
32
- *
33
- * TODO: Write tests for this class.
34
- *
35
- * This should eventually handle the following:
36
- * - Parameter registration
37
- * - Dependency tracking
38
- * - Calling observers when a parameter changes
39
- * - Somehow saving parameter "state" (in bookmarks)
40
- * - Maybe something else
41
- */
42
- export type ExprRefFunction = ((datum: object) => any) & import("./utils/expression.js").ExpressionProps & {
43
- addListener: (listener: () => void) => void;
44
- invalidate: () => void;
45
- };
46
- //# sourceMappingURL=paramBroker.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"paramBroker.d.ts","sourceRoot":"","sources":["../../src/paramBroker.js"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;GAaG;AACH;IAyBI;;;;OAIG;IACH,0BAHW,MAAM,WACI,GAAG,KAAK,IAAI,CAqBhC;IAID;;;;OAIG;IACH,uBAFW,MAAM,mBA2ChB;;CACJ;;;;;;;;;;;;;;4BAtG2F,MAAM,IAAI,KAAK,IAAI;gBAAc,MAAM,IAAI"}
@@ -1,118 +0,0 @@
1
- import { isString } from "vega-util";
2
- import createFunction from "./utils/expression.js";
3
-
4
- /**
5
- * A class that manages parameters and expressions. Still a work in progress.
6
- *
7
- * TODO: Write tests for this class.
8
- *
9
- * This should eventually handle the following:
10
- * - Parameter registration
11
- * - Dependency tracking
12
- * - Calling observers when a parameter changes
13
- * - Somehow saving parameter "state" (in bookmarks)
14
- * - Maybe something else
15
- *
16
- * @typedef {import("./utils/expression.js").ExpressionFunction & { addListener: (listener: () => void) => void, invalidate: () => void}} ExprRefFunction
17
- */
18
- export default class ParamBroker {
19
- /** @type {Map<string, any>} */
20
- #params;
21
-
22
- /** @type {Set<string>} */
23
- #allocatedSetters;
24
-
25
- /** @type {Record<string, any>} */
26
- #proxy;
27
-
28
- /** @type {Map<string, Set<() => void>>} */
29
- #paramListeners;
30
-
31
- constructor() {
32
- this.#params = new Map();
33
- this.#allocatedSetters = new Set();
34
- this.#paramListeners = new Map();
35
-
36
- this.#proxy = new Proxy(this.#params, {
37
- get(target, prop) {
38
- return isString(prop) ? target.get(prop) : undefined;
39
- },
40
- });
41
- }
42
-
43
- /**
44
- *
45
- * @param {string} paramName
46
- * @returns {(value: any) => void}
47
- */
48
- allocateSetter(paramName) {
49
- if (this.#allocatedSetters.has(paramName)) {
50
- throw new Error(
51
- "Setter already allocated for parameter: " + paramName
52
- );
53
- }
54
-
55
- this.#allocatedSetters.add(paramName);
56
-
57
- return (value) => {
58
- this.#params.set(paramName, value);
59
-
60
- const listeners = this.#paramListeners.get(paramName);
61
- if (listeners) {
62
- for (const listener of listeners) {
63
- listener();
64
- }
65
- }
66
- };
67
- }
68
-
69
- // TODO: deallocateSetter
70
-
71
- /**
72
- * Parse expr and return a function that returns the value of the parameter.
73
- *
74
- * @param {string} expr
75
- */
76
- createExpression(expr) {
77
- /** @type {ExprRefFunction} */
78
- const fn = /** @type {any} */ (createFunction(expr, this.#proxy));
79
-
80
- for (const g of fn.globals) {
81
- if (!this.#allocatedSetters.has(g)) {
82
- throw new Error(
83
- `Unknown variable "${g}" in expression: ${expr}`
84
- );
85
- }
86
- }
87
-
88
- // Keep track of them so that they can be detached later
89
- const myListeners = new Set();
90
-
91
- /**
92
- *
93
- * @param {() => void} listener
94
- */
95
- fn.addListener = (listener) => {
96
- for (const g of fn.globals) {
97
- const listeners = this.#paramListeners.get(g) ?? new Set();
98
- this.#paramListeners.set(g, listeners);
99
- listeners.add(listener);
100
- myListeners.add(listener);
101
- }
102
- };
103
-
104
- /**
105
- * Detach listeners. This must be called if the expression is no longer used.
106
- */
107
- fn.invalidate = () => {
108
- for (const g of fn.globals) {
109
- const listeners = this.#paramListeners.get(g);
110
- for (const listener of myListeners) {
111
- listeners.delete(listener);
112
- }
113
- }
114
- };
115
-
116
- return fn;
117
- }
118
- }