@genome-spy/core 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +224 -0
- package/dist/style.css +1 -0
- package/package.json +54 -0
- package/src/data/collector.js +178 -0
- package/src/data/collector.test.js +82 -0
- package/src/data/dataFlow.js +109 -0
- package/src/data/dataFlow.test.js +3 -0
- package/src/data/facetNode.js +17 -0
- package/src/data/flow.test.js +71 -0
- package/src/data/flowBatch.d.ts +40 -0
- package/src/data/flowNode.js +283 -0
- package/src/data/flowNode.test.js +49 -0
- package/src/data/flowOptimizer.js +117 -0
- package/src/data/flowOptimizer.test.js +192 -0
- package/src/data/flowTestUtils.js +63 -0
- package/src/data/formats/fasta.js +32 -0
- package/src/data/formats/fasta.test.js +26 -0
- package/src/data/sources/dataSource.js +22 -0
- package/src/data/sources/dataSourceFactory.js +24 -0
- package/src/data/sources/dataUtils.js +31 -0
- package/src/data/sources/dynamicCallbackSource.js +56 -0
- package/src/data/sources/dynamicSource.js +36 -0
- package/src/data/sources/inlineSource.js +69 -0
- package/src/data/sources/inlineSource.test.js +55 -0
- package/src/data/sources/namedSource.js +74 -0
- package/src/data/sources/sequenceSource.js +46 -0
- package/src/data/sources/sequenceSource.test.js +45 -0
- package/src/data/sources/urlSource.js +74 -0
- package/src/data/transforms/aggregate.js +69 -0
- package/src/data/transforms/clone.js +40 -0
- package/src/data/transforms/clone.test.js +10 -0
- package/src/data/transforms/coverage.js +187 -0
- package/src/data/transforms/coverage.test.js +122 -0
- package/src/data/transforms/filter.js +37 -0
- package/src/data/transforms/filter.test.js +17 -0
- package/src/data/transforms/filterScoredLabels.js +134 -0
- package/src/data/transforms/flattenCompressedExons.js +57 -0
- package/src/data/transforms/flattenDelimited.js +68 -0
- package/src/data/transforms/flattenDelimited.test.js +86 -0
- package/src/data/transforms/flattenSequence.js +39 -0
- package/src/data/transforms/flattenSequence.test.js +33 -0
- package/src/data/transforms/formula.js +39 -0
- package/src/data/transforms/formula.test.js +18 -0
- package/src/data/transforms/identifier.js +108 -0
- package/src/data/transforms/identifier.test.js +82 -0
- package/src/data/transforms/linearizeGenomicCoordinate.js +101 -0
- package/src/data/transforms/measureText.js +44 -0
- package/src/data/transforms/pileup.js +128 -0
- package/src/data/transforms/pileup.test.js +69 -0
- package/src/data/transforms/project.js +41 -0
- package/src/data/transforms/project.test.js +31 -0
- package/src/data/transforms/regexExtract.js +61 -0
- package/src/data/transforms/regexExtract.test.js +66 -0
- package/src/data/transforms/regexFold.js +141 -0
- package/src/data/transforms/regexFold.test.js +159 -0
- package/src/data/transforms/sample.js +101 -0
- package/src/data/transforms/sample.test.js +37 -0
- package/src/data/transforms/stack.js +137 -0
- package/src/data/transforms/stack.test.js +90 -0
- package/src/data/transforms/transformFactory.js +60 -0
- package/src/encoder/accessor.js +82 -0
- package/src/encoder/accessor.test.js +46 -0
- package/src/encoder/encoder.js +369 -0
- package/src/encoder/encoder.test.js +97 -0
- package/src/fonts/Lato-Regular.json +1267 -0
- package/src/fonts/Lato-Regular.png +0 -0
- package/src/fonts/OFL.txt +93 -0
- package/src/fonts/README.md +3 -0
- package/src/fonts/bmFont.d.ts +58 -0
- package/src/fonts/bmFontManager.js +357 -0
- package/src/fonts/bmFontMetrics.js +108 -0
- package/src/genome/genome.js +305 -0
- package/src/genome/genome.test.js +152 -0
- package/src/genome/genomeStore.js +54 -0
- package/src/genome/locusFormat.js +31 -0
- package/src/genome/scaleIndex.js +199 -0
- package/src/genome/scaleIndex.test.js +61 -0
- package/src/genome/scaleLocus.js +112 -0
- package/src/genome/scaleLocus.test.js +3 -0
- package/src/genomeSpy.js +753 -0
- package/src/gl/arrayBuilder.js +199 -0
- package/src/gl/dataToVertices.js +621 -0
- package/src/gl/includes/common.glsl +63 -0
- package/src/gl/includes/fp64-arithmetic.glsl +187 -0
- package/src/gl/includes/fp64-utils.js +132 -0
- package/src/gl/includes/picking.fragment.glsl +3 -0
- package/src/gl/includes/picking.vertex.glsl +29 -0
- package/src/gl/includes/sampleFacet.glsl +107 -0
- package/src/gl/includes/scales.glsl +79 -0
- package/src/gl/includes/scales_fp64.glsl +30 -0
- package/src/gl/link.fragment.glsl +18 -0
- package/src/gl/link.vertex.glsl +111 -0
- package/src/gl/point.fragment.glsl +123 -0
- package/src/gl/point.vertex.glsl +128 -0
- package/src/gl/rect.fragment.glsl +51 -0
- package/src/gl/rect.vertex.glsl +114 -0
- package/src/gl/rule.fragment.glsl +52 -0
- package/src/gl/rule.vertex.glsl +89 -0
- package/src/gl/text.fragment.glsl +31 -0
- package/src/gl/text.vertex.glsl +246 -0
- package/src/gl/webGLHelper.js +490 -0
- package/src/img/bowtie.svg +1 -0
- package/src/img/genomespy-favicon.svg +34 -0
- package/src/index.html +11 -0
- package/src/index.js +151 -0
- package/src/marks/link.js +189 -0
- package/src/marks/mark.js +867 -0
- package/src/marks/markUtils.js +109 -0
- package/src/marks/pointMark.js +279 -0
- package/src/marks/rectMark.js +236 -0
- package/src/marks/rule.js +231 -0
- package/src/marks/text.js +274 -0
- package/src/options.d.ts +9 -0
- package/src/scale/colorUtils.js +184 -0
- package/src/scale/glslScaleGenerator.js +462 -0
- package/src/scale/scale.js +441 -0
- package/src/scale/scale.test.js +323 -0
- package/src/scale/ticks.js +198 -0
- package/src/scale/ticks.test.js +39 -0
- package/src/singlePageApp.js +13 -0
- package/src/spec/axis.d.ts +296 -0
- package/src/spec/channel.d.ts +127 -0
- package/src/spec/data.d.ts +185 -0
- package/src/spec/font.d.ts +15 -0
- package/src/spec/genome.d.ts +35 -0
- package/src/spec/mark.d.ts +432 -0
- package/src/spec/root.d.ts +22 -0
- package/src/spec/scale.d.ts +265 -0
- package/src/spec/tooltip.d.ts +9 -0
- package/src/spec/transform.d.ts +479 -0
- package/src/spec/view.d.ts +215 -0
- package/src/styles/genome-spy.scss +153 -0
- package/src/tooltip/dataTooltipHandler.js +59 -0
- package/src/tooltip/refseqGeneTooltipHandler.js +77 -0
- package/src/tooltip/tooltipHandler.ts +12 -0
- package/src/types/filetypes.d.ts +4 -0
- package/src/types/flatqueue.d.ts +53 -0
- package/src/types/glsl.d.ts +4 -0
- package/src/types/object.d.ts +21 -0
- package/src/types/vega-scale.d.ts +60 -0
- package/src/utils/animator.js +83 -0
- package/src/utils/arrayUtils.js +55 -0
- package/src/utils/binnedRangeIndex.js +83 -0
- package/src/utils/clamp.js +8 -0
- package/src/utils/cloner.js +32 -0
- package/src/utils/cloner.test.js +23 -0
- package/src/utils/coalesce.js +11 -0
- package/src/utils/coalesce.test.js +15 -0
- package/src/utils/concatIterables.js +26 -0
- package/src/utils/concatIterables.test.js +7 -0
- package/src/utils/debounce.js +37 -0
- package/src/utils/domainArray.js +224 -0
- package/src/utils/domainArray.test.js +129 -0
- package/src/utils/eerp.js +13 -0
- package/src/utils/expression.js +32 -0
- package/src/utils/field.js +28 -0
- package/src/utils/fisheye.js +60 -0
- package/src/utils/formatObject.js +31 -0
- package/src/utils/html.js +23 -0
- package/src/utils/html.test.js +13 -0
- package/src/utils/indexer.js +43 -0
- package/src/utils/indexer.test.js +46 -0
- package/src/utils/inertia.js +124 -0
- package/src/utils/interactionEvent.js +33 -0
- package/src/utils/iterateNestedMaps.js +21 -0
- package/src/utils/iterateNestedMaps.test.js +32 -0
- package/src/utils/kWayMerge.js +42 -0
- package/src/utils/kWayMerge.test.js +25 -0
- package/src/utils/layout/flexLayout.js +336 -0
- package/src/utils/layout/flexLayout.test.js +296 -0
- package/src/utils/layout/padding.js +107 -0
- package/src/utils/layout/point.js +23 -0
- package/src/utils/layout/rectangle.js +282 -0
- package/src/utils/layout/rectangle.test.js +171 -0
- package/src/utils/mergeObjects.js +99 -0
- package/src/utils/mergeObjects.test.js +41 -0
- package/src/utils/numberExtractor.js +24 -0
- package/src/utils/numberExtractor.test.js +5 -0
- package/src/utils/point.js +14 -0
- package/src/utils/propertyCacher.js +70 -0
- package/src/utils/propertyCacher.test.js +84 -0
- package/src/utils/propertyCoalescer.js +37 -0
- package/src/utils/propertyCoalescer.test.js +21 -0
- package/src/utils/reservationMap.js +103 -0
- package/src/utils/reservationMap.test.js +19 -0
- package/src/utils/scaleNull.js +19 -0
- package/src/utils/setOperations.js +75 -0
- package/src/utils/smoothstep.js +10 -0
- package/src/utils/throttle.js +34 -0
- package/src/utils/topK.js +76 -0
- package/src/utils/topK.test.js +63 -0
- package/src/utils/transition.js +74 -0
- package/src/utils/ui/tooltip.js +189 -0
- package/src/utils/url.js +22 -0
- package/src/utils/variableTools.js +24 -0
- package/src/utils/variableTools.test.js +12 -0
- package/src/view/axisResolution.js +135 -0
- package/src/view/axisResolution.test.js +200 -0
- package/src/view/axisView.js +746 -0
- package/src/view/channel.js +5 -0
- package/src/view/concatView.js +296 -0
- package/src/view/containerView.js +141 -0
- package/src/view/decoratorView.js +510 -0
- package/src/view/facetView.js +488 -0
- package/src/view/flowBuilder.js +362 -0
- package/src/view/flowBuilder.test.js +124 -0
- package/src/view/importView.js +19 -0
- package/src/view/layerView.js +60 -0
- package/src/view/rendering.d.ts +44 -0
- package/src/view/renderingContext/compositeViewRenderingContext.js +51 -0
- package/src/view/renderingContext/deferredViewRenderingContext.js +174 -0
- package/src/view/renderingContext/layoutRecorderViewRenderingContext.js +128 -0
- package/src/view/renderingContext/simpleViewRenderingContext.js +62 -0
- package/src/view/renderingContext/svgViewRenderingContext.js +121 -0
- package/src/view/renderingContext/viewRenderingContext.js +41 -0
- package/src/view/scaleResolution.js +756 -0
- package/src/view/scaleResolution.test.js +571 -0
- package/src/view/scaleResolutionApi.d.ts +40 -0
- package/src/view/testUtils.js +48 -0
- package/src/view/unitView.js +368 -0
- package/src/view/view.js +589 -0
- package/src/view/view.test.js +213 -0
- package/src/view/viewContext.d.ts +57 -0
- package/src/view/viewFactory.js +179 -0
- package/src/view/viewFactory.test.js +16 -0
- package/src/view/viewUtils.js +420 -0
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
import { isObject, isString } from "vega-util";
|
|
2
|
+
import { loader as vegaLoader } from "vega-loader";
|
|
3
|
+
|
|
4
|
+
import UnitView from "./unitView";
|
|
5
|
+
import ImportView from "./importView";
|
|
6
|
+
import LayerView from "./layerView";
|
|
7
|
+
import DecoratorView from "./decoratorView";
|
|
8
|
+
// eslint-disable-next-line no-unused-vars
|
|
9
|
+
import View, { VISIT_SKIP, VISIT_STOP } from "./view";
|
|
10
|
+
import { buildDataFlow } from "./flowBuilder";
|
|
11
|
+
import { optimizeDataFlow } from "../data/flowOptimizer";
|
|
12
|
+
import {
|
|
13
|
+
isFieldDef,
|
|
14
|
+
isValueDef,
|
|
15
|
+
primaryPositionalChannels,
|
|
16
|
+
} from "../encoder/encoder";
|
|
17
|
+
import ContainerView from "./containerView";
|
|
18
|
+
import { peek } from "../utils/arrayUtils";
|
|
19
|
+
import { rollup } from "d3-array";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @typedef {import("./viewContext").default} ViewContext
|
|
23
|
+
* @typedef {import("../spec/mark").MarkConfig} MarkConfig
|
|
24
|
+
* @typedef {import("../spec/channel").ChannelDef} ChannelDef
|
|
25
|
+
* @typedef {import("../spec/view").ContainerSpec} ContainerSpec
|
|
26
|
+
* @typedef {import("../spec/view").ViewSpec} ViewSpec
|
|
27
|
+
* @typedef {import("../spec/view").LayerSpec} LayerSpec
|
|
28
|
+
* @typedef {import("../spec/view").FacetSpec} FacetSpec
|
|
29
|
+
* @typedef {import("../spec/view").SampleSpec} SampleSpec
|
|
30
|
+
* @typedef {import("../spec/view").UnitSpec} UnitSpec
|
|
31
|
+
* @typedef {import("../spec/view").VConcatSpec} VConcatSpec
|
|
32
|
+
* @typedef {import("../spec/view").HConcatSpec} HConcatSpec
|
|
33
|
+
* @typedef {import("../spec/view").ConcatSpec} ConcatSpec
|
|
34
|
+
* @typedef {VConcatSpec | HConcatSpec | ConcatSpec} AnyConcatSpec
|
|
35
|
+
* @typedef {import("../spec/view").ImportSpec} ImportSpec
|
|
36
|
+
* @typedef {import("../spec/view").ImportConfig} ImportConfig
|
|
37
|
+
* @typedef {import("../spec/root").RootSpec} RootSpec
|
|
38
|
+
* @typedef {import("../spec/root").RootConfig} RootConfig
|
|
39
|
+
*
|
|
40
|
+
* @typedef {import("../spec/channel").FacetFieldDef} FacetFieldDef
|
|
41
|
+
* @typedef {import("../spec/view").FacetMapping} FacetMapping
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
*
|
|
46
|
+
* @param {ChannelDef | FacetMapping} def
|
|
47
|
+
* @returns {spec is FacetFieldDef}
|
|
48
|
+
*/
|
|
49
|
+
export function isFacetFieldDef(def) {
|
|
50
|
+
return def && "field" in def && isString(def.field);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
*
|
|
55
|
+
* @param {FacetFieldDef | FacetMapping} def
|
|
56
|
+
* @returns {spec is FacetMapping}
|
|
57
|
+
*/
|
|
58
|
+
export function isFacetMapping(def) {
|
|
59
|
+
return (
|
|
60
|
+
("row" in def && isObject(def.row)) ||
|
|
61
|
+
("column" in def && isObject(def.column))
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
*
|
|
67
|
+
* @param {object} config
|
|
68
|
+
* @returns {config is ImportConfig}
|
|
69
|
+
*/
|
|
70
|
+
export function isImportConfig(config) {
|
|
71
|
+
return "name" in config || "url" in config;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Returns all marks in the order (DFS) they are rendered
|
|
76
|
+
* @param {View} root
|
|
77
|
+
*/
|
|
78
|
+
export function getMarks(root) {
|
|
79
|
+
return getFlattenedViews(root)
|
|
80
|
+
.filter((view) => view instanceof UnitView)
|
|
81
|
+
.map((view) => /** @type {UnitView} */ (view).mark);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Returns the nodes of the view hierarchy in depth-first order.
|
|
86
|
+
*
|
|
87
|
+
* @param {View} root
|
|
88
|
+
*/
|
|
89
|
+
export function getFlattenedViews(root) {
|
|
90
|
+
/** @type {View[]} */
|
|
91
|
+
const views = [];
|
|
92
|
+
root.visit((view) => {
|
|
93
|
+
views.push(view);
|
|
94
|
+
});
|
|
95
|
+
return views;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* @param {View} root
|
|
100
|
+
*/
|
|
101
|
+
export function resolveScalesAndAxes(root) {
|
|
102
|
+
root.visit((view) => {
|
|
103
|
+
if (view instanceof UnitView) {
|
|
104
|
+
view.resolve("scale");
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Check that each scale resolution has a unique name
|
|
109
|
+
/** @type {Set<string>} */
|
|
110
|
+
const scaleNames = new Set();
|
|
111
|
+
root.visit((view) => {
|
|
112
|
+
for (const resolution of Object.values(view.resolutions.scale)) {
|
|
113
|
+
const name = resolution.name;
|
|
114
|
+
if (name && scaleNames.has(name)) {
|
|
115
|
+
throw new Error(
|
|
116
|
+
`The same scale name "${name}" occurs in multiple scale resolutions!`
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
scaleNames.add(name);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
root.visit((view) => {
|
|
124
|
+
if (view instanceof UnitView) {
|
|
125
|
+
view.resolve("axis");
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
root.visit((view) => view.onScalesResolved());
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Gives names to zoomable scales that have been pulled to the root. This allows
|
|
133
|
+
* the zoomed domains to be bookmarked without explicitly specifying the names.
|
|
134
|
+
* This only affects the trivial but common cases, e.g., a genome-browser-like
|
|
135
|
+
* view with a shared x scale.
|
|
136
|
+
*
|
|
137
|
+
* @param {View} root
|
|
138
|
+
*/
|
|
139
|
+
export function setImplicitScaleNames(root) {
|
|
140
|
+
for (const channel of primaryPositionalChannels) {
|
|
141
|
+
const resolution = root.getScaleResolution(channel);
|
|
142
|
+
if (resolution && !resolution.name && resolution.isZoomable()) {
|
|
143
|
+
// TODO: Should actually check that the name is not already reserved
|
|
144
|
+
resolution.name = `${channel}_at_root`;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* @param {View} root
|
|
151
|
+
*/
|
|
152
|
+
export function addDecorators(root) {
|
|
153
|
+
let newRoot = root; // If the root is wrapped...
|
|
154
|
+
|
|
155
|
+
/** @param {ChannelDef} channelDef */
|
|
156
|
+
const hasDomain = (channelDef) => channelDef && !isValueDef(channelDef);
|
|
157
|
+
|
|
158
|
+
root.visit((view) => {
|
|
159
|
+
if (view instanceof LayerView || view instanceof UnitView) {
|
|
160
|
+
const encoding = view.getEncoding();
|
|
161
|
+
if (
|
|
162
|
+
view instanceof UnitView &&
|
|
163
|
+
!hasDomain(encoding.x) &&
|
|
164
|
+
!hasDomain(encoding.y)
|
|
165
|
+
) {
|
|
166
|
+
// Don't wrap views that have no positional channels
|
|
167
|
+
// TODO: However, in future, views with borders or backgrounds should be wrapped always
|
|
168
|
+
// TODO: Also, views with "axis: null" need no wrapping.
|
|
169
|
+
// TODO: Handle LayerViews, they may have children with positional domains
|
|
170
|
+
return VISIT_SKIP;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const originalParent = view.parent;
|
|
174
|
+
const decorator = new DecoratorView(view.context, originalParent);
|
|
175
|
+
view.parent = decorator;
|
|
176
|
+
decorator.child = view;
|
|
177
|
+
decorator.name = view.name + "_decorator";
|
|
178
|
+
|
|
179
|
+
if (originalParent) {
|
|
180
|
+
if (originalParent instanceof ContainerView) {
|
|
181
|
+
originalParent.replaceChild(view, decorator);
|
|
182
|
+
} else {
|
|
183
|
+
// The situation is likely related to summaries of SampleView and the
|
|
184
|
+
// hierarchy is inconsistent. Let's try to find the SampleView.
|
|
185
|
+
|
|
186
|
+
/** @type {view} */
|
|
187
|
+
let parent;
|
|
188
|
+
root.visit(
|
|
189
|
+
stackifyVisitor((needle, stack) => {
|
|
190
|
+
if (needle === view) {
|
|
191
|
+
parent = peek(stack);
|
|
192
|
+
return VISIT_STOP;
|
|
193
|
+
}
|
|
194
|
+
})
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
if (parent instanceof ContainerView) {
|
|
198
|
+
parent.replaceChild(view, decorator);
|
|
199
|
+
} else {
|
|
200
|
+
throw new Error(
|
|
201
|
+
"Cannot find parent while decorating: " +
|
|
202
|
+
view.getPathString()
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
decorator.resolutions = view.resolutions;
|
|
209
|
+
view.resolutions = { scale: {}, axis: {} };
|
|
210
|
+
|
|
211
|
+
decorator.spec.height = view.spec.height;
|
|
212
|
+
view.spec.height = "container";
|
|
213
|
+
|
|
214
|
+
decorator.spec.width = view.spec.width;
|
|
215
|
+
view.spec.width = "container";
|
|
216
|
+
|
|
217
|
+
decorator.spec.padding = view.spec.padding;
|
|
218
|
+
view.spec.padding = undefined;
|
|
219
|
+
|
|
220
|
+
if (view === root) {
|
|
221
|
+
newRoot = decorator;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
decorator.initialize();
|
|
225
|
+
|
|
226
|
+
return VISIT_SKIP;
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
return newRoot;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* @param {View} root
|
|
235
|
+
* @param {import("../data/dataFlow").default<View>} [existingFlow] Add data flow
|
|
236
|
+
* graphs to an existing DataFlow object.
|
|
237
|
+
*/
|
|
238
|
+
export async function initializeData(root, existingFlow) {
|
|
239
|
+
const flow = buildDataFlow(root, existingFlow);
|
|
240
|
+
optimizeDataFlow(flow);
|
|
241
|
+
flow.initialize();
|
|
242
|
+
|
|
243
|
+
/** @type {Promise<void>[]} */
|
|
244
|
+
const promises = flow.dataSources.map((dataSource) => dataSource.load());
|
|
245
|
+
|
|
246
|
+
await Promise.all(promises);
|
|
247
|
+
|
|
248
|
+
return flow;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
*
|
|
253
|
+
* @param {View} view
|
|
254
|
+
*/
|
|
255
|
+
export function findEncodedFields(view) {
|
|
256
|
+
/** @type {{view: UnitView, channel: string, field: string, type: string}[]} */
|
|
257
|
+
const fieldInfos = [];
|
|
258
|
+
|
|
259
|
+
view.visit((view) => {
|
|
260
|
+
if (view instanceof UnitView) {
|
|
261
|
+
const encoding = view.getEncoding();
|
|
262
|
+
for (const [channel, def] of Object.entries(encoding)) {
|
|
263
|
+
if (isFieldDef(def)) {
|
|
264
|
+
fieldInfos.push({
|
|
265
|
+
view,
|
|
266
|
+
channel,
|
|
267
|
+
field: def.field,
|
|
268
|
+
type: def.type,
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return VISIT_SKIP; // Skip sample summaries
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
return fieldInfos;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* @param {import("../spec/view").ImportSpec} spec
|
|
281
|
+
* @param {string} baseUrl
|
|
282
|
+
* @param {ViewContext} viewContext
|
|
283
|
+
*/
|
|
284
|
+
async function loadExternalViewSpec(spec, baseUrl, viewContext) {
|
|
285
|
+
if (!spec.import.url) {
|
|
286
|
+
throw new Error(
|
|
287
|
+
"Cannot import, not an import spec: " + JSON.stringify(spec)
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const loader = vegaLoader({ baseURL: baseUrl });
|
|
292
|
+
const url = spec.import.url;
|
|
293
|
+
|
|
294
|
+
const importedSpec = JSON.parse(
|
|
295
|
+
await loader.load(url).catch((e) => {
|
|
296
|
+
throw new Error(
|
|
297
|
+
`Could not load imported view spec: ${url} \nReason: ${e.message}`
|
|
298
|
+
);
|
|
299
|
+
})
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
if (viewContext.isViewSpec(importedSpec)) {
|
|
303
|
+
importedSpec.baseUrl = url.match(/^[^?#]*\//)?.[0];
|
|
304
|
+
return importedSpec;
|
|
305
|
+
} else {
|
|
306
|
+
throw new Error(
|
|
307
|
+
`The imported spec "${url}" is not a view spec: ${JSON.stringify(
|
|
308
|
+
spec
|
|
309
|
+
)}`
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* @param {import("./view").default} viewRoot
|
|
316
|
+
*/
|
|
317
|
+
export async function processImports(viewRoot) {
|
|
318
|
+
/** @type {ImportView[]} */
|
|
319
|
+
const importViews = [];
|
|
320
|
+
|
|
321
|
+
viewRoot.visit((view) => {
|
|
322
|
+
if (view instanceof ImportView) {
|
|
323
|
+
importViews.push(view);
|
|
324
|
+
return VISIT_SKIP;
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
for (const view of importViews) {
|
|
329
|
+
const context = view.context;
|
|
330
|
+
|
|
331
|
+
// TODO: Parallelize using promises, don't use await
|
|
332
|
+
const loadedSpec = await loadExternalViewSpec(
|
|
333
|
+
view.spec,
|
|
334
|
+
view.getBaseUrl(),
|
|
335
|
+
context
|
|
336
|
+
);
|
|
337
|
+
|
|
338
|
+
// TODO: Let importSpec have a name
|
|
339
|
+
const importedView = context.createView(
|
|
340
|
+
loadedSpec,
|
|
341
|
+
view.parent,
|
|
342
|
+
view.name
|
|
343
|
+
);
|
|
344
|
+
view.parent.replaceChild(view, importedView);
|
|
345
|
+
|
|
346
|
+
// Import recursively
|
|
347
|
+
await processImports(importedView);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* @param {function(View, View[]):void} visitor
|
|
353
|
+
*/
|
|
354
|
+
export function stackifyVisitor(visitor) {
|
|
355
|
+
/** @type {View[]} */
|
|
356
|
+
const stack = [];
|
|
357
|
+
|
|
358
|
+
/** @type {import("./view").Visitor} */
|
|
359
|
+
const stackified = (view) => visitor(view, stack);
|
|
360
|
+
|
|
361
|
+
stackified.beforeChildren = (view) => {
|
|
362
|
+
stack.push(view);
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
stackified.afterChildren = (view) => {
|
|
366
|
+
stack.pop();
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
return stackified;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Finds the descendants having the given name. The root is included in the search.
|
|
374
|
+
*
|
|
375
|
+
* @param {View} root
|
|
376
|
+
* @param {string} name View name
|
|
377
|
+
* @returns {View[]}
|
|
378
|
+
*/
|
|
379
|
+
export function findDescendantsByPath(root, name) {
|
|
380
|
+
/** @type {View[]} */
|
|
381
|
+
const descendants = [];
|
|
382
|
+
|
|
383
|
+
root.visit((view) => {
|
|
384
|
+
if (view.name == name) {
|
|
385
|
+
descendants.push(view);
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
return descendants;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
*
|
|
394
|
+
* @param {View} root
|
|
395
|
+
*/
|
|
396
|
+
export function findUniqueViewNames(root) {
|
|
397
|
+
/** @type {View[]} */
|
|
398
|
+
const descendants = [];
|
|
399
|
+
|
|
400
|
+
root.visit((view) => {
|
|
401
|
+
descendants.push(view);
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
return new Set(
|
|
405
|
+
[
|
|
406
|
+
...rollup(
|
|
407
|
+
descendants,
|
|
408
|
+
(views) => views.length,
|
|
409
|
+
(view) => view.name
|
|
410
|
+
),
|
|
411
|
+
]
|
|
412
|
+
.filter(([name, count]) => count == 1 && name !== undefined)
|
|
413
|
+
.map(([name, count]) => name)
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* @param {string} name
|
|
419
|
+
*/
|
|
420
|
+
export const isCustomViewName = (name) => !/^(layer|concat)\d+$/.test(name);
|