@genome-spy/core 0.30.0 → 0.30.2
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.es.js +16373 -0
- package/dist/index.js +43 -43
- package/package.json +10 -7
- package/src/data/collector.js +0 -183
- package/src/data/collector.test.js +0 -84
- package/src/data/dataFlow.js +0 -148
- package/src/data/dataFlow.test.js +0 -5
- package/src/data/facetNode.js +0 -17
- package/src/data/flow.test.js +0 -72
- package/src/data/flowBatch.d.ts +0 -40
- package/src/data/flowNode.js +0 -283
- package/src/data/flowNode.test.js +0 -50
- package/src/data/flowOptimizer.js +0 -123
- package/src/data/flowOptimizer.test.js +0 -193
- package/src/data/flowTestUtils.js +0 -63
- package/src/data/formats/fasta.js +0 -32
- package/src/data/formats/fasta.test.js +0 -27
- package/src/data/sources/dataSource.js +0 -22
- package/src/data/sources/dataSourceFactory.js +0 -24
- package/src/data/sources/dataUtils.js +0 -78
- package/src/data/sources/dynamicCallbackSource.js +0 -57
- package/src/data/sources/dynamicSource.js +0 -37
- package/src/data/sources/inlineSource.js +0 -67
- package/src/data/sources/inlineSource.test.js +0 -56
- package/src/data/sources/namedSource.js +0 -79
- package/src/data/sources/sequenceSource.js +0 -46
- package/src/data/sources/sequenceSource.test.js +0 -46
- package/src/data/sources/urlSource.js +0 -74
- package/src/data/transforms/aggregate.js +0 -70
- package/src/data/transforms/clone.js +0 -40
- package/src/data/transforms/clone.test.js +0 -11
- package/src/data/transforms/coverage.js +0 -187
- package/src/data/transforms/coverage.test.js +0 -123
- package/src/data/transforms/filter.js +0 -37
- package/src/data/transforms/filter.test.js +0 -18
- package/src/data/transforms/filterScoredLabels.js +0 -134
- package/src/data/transforms/flattenCompressedExons.js +0 -57
- package/src/data/transforms/flattenDelimited.js +0 -74
- package/src/data/transforms/flattenDelimited.test.js +0 -87
- package/src/data/transforms/flattenSequence.js +0 -39
- package/src/data/transforms/flattenSequence.test.js +0 -34
- package/src/data/transforms/formula.js +0 -39
- package/src/data/transforms/formula.test.js +0 -19
- package/src/data/transforms/identifier.js +0 -108
- package/src/data/transforms/identifier.test.js +0 -83
- package/src/data/transforms/linearizeGenomicCoordinate.js +0 -101
- package/src/data/transforms/measureText.js +0 -44
- package/src/data/transforms/pileup.js +0 -128
- package/src/data/transforms/pileup.test.js +0 -70
- package/src/data/transforms/project.js +0 -41
- package/src/data/transforms/project.test.js +0 -32
- package/src/data/transforms/regexExtract.js +0 -61
- package/src/data/transforms/regexExtract.test.js +0 -67
- package/src/data/transforms/regexFold.js +0 -141
- package/src/data/transforms/regexFold.test.js +0 -160
- package/src/data/transforms/sample.js +0 -101
- package/src/data/transforms/sample.test.js +0 -38
- package/src/data/transforms/stack.js +0 -137
- package/src/data/transforms/stack.test.js +0 -91
- package/src/data/transforms/transformFactory.js +0 -60
- package/src/embedApi.d.ts +0 -67
- package/src/encoder/accessor.js +0 -82
- package/src/encoder/accessor.test.js +0 -47
- package/src/encoder/encoder.js +0 -394
- package/src/encoder/encoder.test.js +0 -98
- package/src/fonts/Lato-Regular.json +0 -1267
- package/src/fonts/Lato-Regular.png +0 -0
- package/src/fonts/OFL.txt +0 -93
- package/src/fonts/README.md +0 -3
- package/src/fonts/bmFont.d.ts +0 -58
- package/src/fonts/bmFontManager.js +0 -357
- package/src/fonts/bmFontMetrics.js +0 -108
- package/src/genome/genome.js +0 -317
- package/src/genome/genome.test.js +0 -188
- package/src/genome/genomeStore.js +0 -54
- package/src/genome/locusFormat.js +0 -31
- package/src/genome/scaleIndex.d.ts +0 -38
- package/src/genome/scaleIndex.js +0 -166
- package/src/genome/scaleIndex.test.js +0 -78
- package/src/genome/scaleLocus.d.ts +0 -11
- package/src/genome/scaleLocus.js +0 -108
- package/src/genome/scaleLocus.test.js +0 -4
- package/src/genomeSpy.js +0 -785
- package/src/gl/arrayBuilder.js +0 -199
- package/src/gl/dataToVertices.js +0 -636
- package/src/gl/includes/common.glsl +0 -63
- package/src/gl/includes/picking.fragment.glsl +0 -1
- package/src/gl/includes/picking.vertex.glsl +0 -27
- package/src/gl/includes/sampleFacet.glsl +0 -107
- package/src/gl/includes/scales.glsl +0 -112
- package/src/gl/link.fragment.glsl +0 -18
- package/src/gl/link.vertex.glsl +0 -111
- package/src/gl/point.fragment.glsl +0 -123
- package/src/gl/point.vertex.glsl +0 -129
- package/src/gl/rect.fragment.glsl +0 -51
- package/src/gl/rect.vertex.glsl +0 -114
- package/src/gl/rule.fragment.glsl +0 -52
- package/src/gl/rule.vertex.glsl +0 -89
- package/src/gl/text.fragment.glsl +0 -31
- package/src/gl/text.vertex.glsl +0 -246
- package/src/gl/webGLHelper.js +0 -504
- package/src/img/bowtie.svg +0 -1
- package/src/img/genomespy-favicon.svg +0 -34
- package/src/index.html +0 -11
- package/src/index.js +0 -128
- package/src/marks/link.js +0 -175
- package/src/marks/mark.js +0 -975
- package/src/marks/markUtils.js +0 -125
- package/src/marks/pointMark.js +0 -251
- package/src/marks/rectMark.js +0 -241
- package/src/marks/rule.js +0 -250
- package/src/marks/text.js +0 -278
- package/src/node_modules/.vitest/results.json +0 -1
- package/src/scale/colorUtils.js +0 -184
- package/src/scale/glslScaleGenerator.js +0 -502
- package/src/scale/scale.js +0 -451
- package/src/scale/scale.test.js +0 -324
- package/src/scale/ticks.js +0 -203
- package/src/scale/ticks.test.js +0 -40
- package/src/singlePageApp.js +0 -13
- package/src/spec/axis.d.ts +0 -296
- package/src/spec/channel.d.ts +0 -430
- package/src/spec/data.d.ts +0 -196
- package/src/spec/font.d.ts +0 -15
- package/src/spec/genome.d.ts +0 -35
- package/src/spec/mark.d.ts +0 -429
- package/src/spec/root.d.ts +0 -17
- package/src/spec/sampleView.d.ts +0 -180
- package/src/spec/scale.d.ts +0 -273
- package/src/spec/title.d.ts +0 -102
- package/src/spec/tooltip.d.ts +0 -9
- package/src/spec/transform.d.ts +0 -479
- package/src/spec/view.d.ts +0 -201
- package/src/styles/genome-spy.scss +0 -153
- package/src/tooltip/dataTooltipHandler.js +0 -64
- package/src/tooltip/refseqGeneTooltipHandler.js +0 -78
- package/src/tooltip/tooltipHandler.ts +0 -12
- package/src/types/filetypes.d.ts +0 -14
- package/src/types/flatqueue.d.ts +0 -53
- package/src/types/glsl.d.ts +0 -4
- package/src/types/internmap.d.ts +0 -22
- package/src/types/object.d.ts +0 -21
- package/src/types/vega-loader.d.ts +0 -1
- package/src/types/vega-scale.d.ts +0 -60
- package/src/utils/addBaseUrl.js +0 -19
- package/src/utils/addBaseUrl.test.js +0 -22
- package/src/utils/animator.js +0 -83
- package/src/utils/arrayUtils.js +0 -61
- package/src/utils/binnedIndex.js +0 -167
- package/src/utils/binnedIndex.test.js +0 -155
- package/src/utils/clamp.js +0 -8
- package/src/utils/cloner.js +0 -34
- package/src/utils/cloner.test.js +0 -24
- package/src/utils/coalesce.js +0 -11
- package/src/utils/coalesce.test.js +0 -16
- package/src/utils/concatIterables.js +0 -26
- package/src/utils/concatIterables.test.js +0 -8
- package/src/utils/debounce.js +0 -37
- package/src/utils/domainArray.js +0 -216
- package/src/utils/domainArray.test.js +0 -130
- package/src/utils/eerp.js +0 -13
- package/src/utils/expression.js +0 -32
- package/src/utils/field.js +0 -28
- package/src/utils/formatObject.js +0 -31
- package/src/utils/indexer.js +0 -43
- package/src/utils/indexer.test.js +0 -47
- package/src/utils/inertia.js +0 -124
- package/src/utils/interactionEvent.js +0 -33
- package/src/utils/iterateNestedMaps.js +0 -21
- package/src/utils/iterateNestedMaps.test.js +0 -33
- package/src/utils/kWayMerge.js +0 -42
- package/src/utils/kWayMerge.test.js +0 -26
- package/src/utils/layout/flexLayout.js +0 -368
- package/src/utils/layout/flexLayout.test.js +0 -311
- package/src/utils/layout/grid.js +0 -95
- package/src/utils/layout/grid.test.js +0 -71
- package/src/utils/layout/padding.js +0 -120
- package/src/utils/layout/point.js +0 -23
- package/src/utils/layout/rectangle.js +0 -288
- package/src/utils/layout/rectangle.test.js +0 -172
- package/src/utils/mergeObjects.js +0 -99
- package/src/utils/mergeObjects.test.js +0 -42
- package/src/utils/numberExtractor.js +0 -24
- package/src/utils/numberExtractor.test.js +0 -6
- package/src/utils/point.js +0 -14
- package/src/utils/propertyCacher.js +0 -70
- package/src/utils/propertyCacher.test.js +0 -85
- package/src/utils/propertyCoalescer.js +0 -42
- package/src/utils/propertyCoalescer.test.js +0 -22
- package/src/utils/reservationMap.js +0 -103
- package/src/utils/reservationMap.test.js +0 -20
- package/src/utils/scaleNull.js +0 -19
- package/src/utils/setOperations.js +0 -75
- package/src/utils/smoothstep.js +0 -10
- package/src/utils/throttle.js +0 -34
- package/src/utils/topK.js +0 -76
- package/src/utils/topK.test.js +0 -64
- package/src/utils/transition.js +0 -74
- package/src/utils/ui/tooltip.js +0 -189
- package/src/utils/url.js +0 -22
- package/src/utils/variableTools.js +0 -24
- package/src/utils/variableTools.test.js +0 -13
- package/src/view/axisResolution.js +0 -140
- package/src/view/axisResolution.test.js +0 -201
- package/src/view/axisView.js +0 -747
- package/src/view/concatView.js +0 -45
- package/src/view/containerView.js +0 -159
- package/src/view/facetView.js +0 -491
- package/src/view/flowBuilder.js +0 -367
- package/src/view/flowBuilder.test.js +0 -125
- package/src/view/gridView.js +0 -786
- package/src/view/implicitRootView.js +0 -14
- package/src/view/importView.js +0 -19
- package/src/view/layerView.js +0 -74
- package/src/view/rendering.d.ts +0 -44
- package/src/view/renderingContext/compositeViewRenderingContext.js +0 -51
- package/src/view/renderingContext/deferredViewRenderingContext.js +0 -176
- package/src/view/renderingContext/layoutRecorderViewRenderingContext.js +0 -128
- package/src/view/renderingContext/simpleViewRenderingContext.js +0 -64
- package/src/view/renderingContext/svgViewRenderingContext.js +0 -125
- package/src/view/renderingContext/viewRenderingContext.js +0 -41
- package/src/view/scaleResolution.js +0 -797
- package/src/view/scaleResolution.test.js +0 -572
- package/src/view/scaleResolutionApi.d.ts +0 -40
- package/src/view/testUtils.js +0 -51
- package/src/view/title.js +0 -165
- package/src/view/unitView.js +0 -382
- package/src/view/view.js +0 -612
- package/src/view/view.test.js +0 -214
- package/src/view/viewContext.d.ts +0 -62
- package/src/view/viewFactory.js +0 -181
- package/src/view/viewFactory.test.js +0 -17
- package/src/view/viewUtils.js +0 -327
- package/src/view/zoom.js +0 -89
package/src/genomeSpy.js
DELETED
|
@@ -1,785 +0,0 @@
|
|
|
1
|
-
import scaleLocus from "./genome/scaleLocus";
|
|
2
|
-
import { scale as vegaScale } from "vega-scale";
|
|
3
|
-
import { formats as vegaFormats } from "vega-loader";
|
|
4
|
-
|
|
5
|
-
import "./styles/genome-spy.scss";
|
|
6
|
-
import Tooltip from "./utils/ui/tooltip";
|
|
7
|
-
|
|
8
|
-
import AccessorFactory from "./encoder/accessor";
|
|
9
|
-
import {
|
|
10
|
-
resolveScalesAndAxes,
|
|
11
|
-
processImports,
|
|
12
|
-
setImplicitScaleNames,
|
|
13
|
-
} from "./view/viewUtils";
|
|
14
|
-
import UnitView from "./view/unitView";
|
|
15
|
-
|
|
16
|
-
import WebGLHelper from "./gl/webGLHelper";
|
|
17
|
-
import Rectangle from "./utils/layout/rectangle";
|
|
18
|
-
import DeferredViewRenderingContext from "./view/renderingContext/deferredViewRenderingContext";
|
|
19
|
-
import CompositeViewRenderingContext from "./view/renderingContext/compositeViewRenderingContext";
|
|
20
|
-
import InteractionEvent from "./utils/interactionEvent";
|
|
21
|
-
import Point from "./utils/layout/point";
|
|
22
|
-
import Animator from "./utils/animator";
|
|
23
|
-
import DataFlow from "./data/dataFlow";
|
|
24
|
-
import scaleIndex from "./genome/scaleIndex";
|
|
25
|
-
import { buildDataFlow } from "./view/flowBuilder";
|
|
26
|
-
import { optimizeDataFlow } from "./data/flowOptimizer";
|
|
27
|
-
import scaleNull from "./utils/scaleNull";
|
|
28
|
-
import GenomeStore from "./genome/genomeStore";
|
|
29
|
-
import BmFontManager from "./fonts/bmFontManager";
|
|
30
|
-
import fasta from "./data/formats/fasta";
|
|
31
|
-
import { VISIT_STOP } from "./view/view";
|
|
32
|
-
import Inertia, { makeEventTemplate } from "./utils/inertia";
|
|
33
|
-
import refseqGeneTooltipHandler from "./tooltip/refseqGeneTooltipHandler";
|
|
34
|
-
import dataTooltipHandler from "./tooltip/dataTooltipHandler";
|
|
35
|
-
import { invalidatePrefix } from "./utils/propertyCacher";
|
|
36
|
-
import { ViewFactory } from "./view/viewFactory";
|
|
37
|
-
import LayerView from "./view/layerView";
|
|
38
|
-
import ImplicitRootView from "./view/implicitRootView";
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* @typedef {import("./spec/view").UnitSpec} UnitSpec
|
|
42
|
-
* @typedef {import("./spec/view").ViewSpec} ViewSpec
|
|
43
|
-
* @typedef {import("./spec/view").ImportSpec} ImportSpec
|
|
44
|
-
* @typedef {import("./spec/view").VConcatSpec} TrackSpec
|
|
45
|
-
* @typedef {import("./spec/root").RootSpec} RootSpec
|
|
46
|
-
* @typedef {import("./spec/root").RootConfig} RootConfig
|
|
47
|
-
*/
|
|
48
|
-
|
|
49
|
-
// Register scaleLocus to Vega-Scale.
|
|
50
|
-
// Loci are discrete but the scale's domain can be adjusted in a continuous manner.
|
|
51
|
-
vegaScale("index", scaleIndex, ["continuous"]);
|
|
52
|
-
vegaScale("locus", scaleLocus, ["continuous"]);
|
|
53
|
-
vegaScale("null", scaleNull, []);
|
|
54
|
-
|
|
55
|
-
vegaFormats("fasta", fasta);
|
|
56
|
-
|
|
57
|
-
export default class GenomeSpy {
|
|
58
|
-
/**
|
|
59
|
-
*
|
|
60
|
-
* @param {HTMLElement} container
|
|
61
|
-
* @param {RootSpec} spec
|
|
62
|
-
* @param {import("./embedApi").EmbedOptions} [options]
|
|
63
|
-
*/
|
|
64
|
-
constructor(container, spec, options = {}) {
|
|
65
|
-
this.container = container;
|
|
66
|
-
|
|
67
|
-
/** Root level configuration object */
|
|
68
|
-
this.spec = spec;
|
|
69
|
-
|
|
70
|
-
this.accessorFactory = new AccessorFactory();
|
|
71
|
-
this.viewFactory = new ViewFactory();
|
|
72
|
-
|
|
73
|
-
/** @type {(function(string):object[])[]} */
|
|
74
|
-
this.namedDataProviders = [];
|
|
75
|
-
|
|
76
|
-
this.animator = new Animator(() => this.renderAll());
|
|
77
|
-
|
|
78
|
-
/** @type {GenomeStore} */
|
|
79
|
-
this.genomeStore = undefined;
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* View visibility is checked using a predicate that can be overridden
|
|
83
|
-
* for more dynamic visibility management.
|
|
84
|
-
*
|
|
85
|
-
* @type {(view: import("./view/view").default) => boolean}
|
|
86
|
-
*/
|
|
87
|
-
this.viewVisibilityPredicate = (view) => view.isVisibleInSpec();
|
|
88
|
-
|
|
89
|
-
/** @type {DeferredViewRenderingContext} */
|
|
90
|
-
this._renderingContext = undefined;
|
|
91
|
-
/** @type {DeferredViewRenderingContext} */
|
|
92
|
-
this._pickingContext = undefined;
|
|
93
|
-
|
|
94
|
-
/** Does picking buffer need to be rendered again */
|
|
95
|
-
this._dirtyPickingBuffer = false;
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Currently hovered mark and datum
|
|
99
|
-
* @type {{ mark: import("./marks/Mark").default, datum: import("./data/flowNode").Datum, uniqueId: number }}
|
|
100
|
-
*/
|
|
101
|
-
this._currentHover = undefined;
|
|
102
|
-
|
|
103
|
-
this._wheelInertia = new Inertia(this.animator);
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Keeping track so that these can be cleaned up upon finalization.
|
|
107
|
-
* @type {Map<string, (function(KeyboardEvent):void)[]>}
|
|
108
|
-
*/
|
|
109
|
-
this._keyboardListeners = new Map();
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Listers for exposed high-level events such as click on a mark instance.
|
|
113
|
-
* These should probably be in the View class and support bubbling through
|
|
114
|
-
* the hierarchy.
|
|
115
|
-
*
|
|
116
|
-
* @type {Map<string, Set<(event: any) => void>>}
|
|
117
|
-
*/
|
|
118
|
-
this._eventListeners = new Map();
|
|
119
|
-
|
|
120
|
-
/** @type {Record<string, import("./tooltip/tooltipHandler").TooltipHandler>}> */
|
|
121
|
-
this.tooltipHandlers = {
|
|
122
|
-
default: dataTooltipHandler,
|
|
123
|
-
refseqgene: refseqGeneTooltipHandler,
|
|
124
|
-
...(options.tooltipHandlers ?? {}),
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
/** @type {import("./view/view").default} */
|
|
128
|
-
this.viewRoot = undefined;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
*
|
|
133
|
-
* @param {(name: string) => any[]} provider
|
|
134
|
-
*/
|
|
135
|
-
registerNamedDataProvider(provider) {
|
|
136
|
-
this.namedDataProviders.unshift(provider);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* @param {string} name
|
|
141
|
-
*/
|
|
142
|
-
getNamedDataFromProvider(name) {
|
|
143
|
-
for (const provider of this.namedDataProviders) {
|
|
144
|
-
const data = provider(name);
|
|
145
|
-
if (data) {
|
|
146
|
-
return data;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
*
|
|
153
|
-
* @param {string} name
|
|
154
|
-
* @param {any[]} data
|
|
155
|
-
*/
|
|
156
|
-
updateNamedData(name, data) {
|
|
157
|
-
const namedSource =
|
|
158
|
-
this.viewRoot.context.dataFlow.findNamedDataSource(name);
|
|
159
|
-
if (!namedSource) {
|
|
160
|
-
throw new Error("No such named data source: " + name);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
namedSource.dataSource.updateDynamicData(data);
|
|
164
|
-
|
|
165
|
-
// Scale domains may need adjustment.
|
|
166
|
-
// TODO: Refactor so that Collectors handle scale extents etc.
|
|
167
|
-
for (const host of namedSource.hosts) {
|
|
168
|
-
host.visit((view) => {
|
|
169
|
-
for (const resolution of Object.values(
|
|
170
|
-
view.resolutions.scale
|
|
171
|
-
)) {
|
|
172
|
-
// TODO: Only update domain
|
|
173
|
-
resolution.reconfigure();
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
this.animator.requestRender();
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Broadcast a message to all views
|
|
183
|
-
*
|
|
184
|
-
* @param {string} type
|
|
185
|
-
* @param {any} [payload]
|
|
186
|
-
*/
|
|
187
|
-
broadcast(type, payload) {
|
|
188
|
-
const message = { type, payload };
|
|
189
|
-
this.viewRoot.visit((view) => view.handleBroadcast(message));
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
_prepareContainer() {
|
|
193
|
-
this.container.classList.add("genome-spy");
|
|
194
|
-
this.container.classList.add("loading");
|
|
195
|
-
|
|
196
|
-
this._glHelper = new WebGLHelper(this.container, () => {
|
|
197
|
-
if (this.viewRoot) {
|
|
198
|
-
const size = this.viewRoot
|
|
199
|
-
.getSize()
|
|
200
|
-
.addPadding(this.viewRoot.getOverhang());
|
|
201
|
-
|
|
202
|
-
// If a dimension has an absolutely specified size (in pixels), use it for the canvas size.
|
|
203
|
-
// However, if the dimension has a growing component, the canvas should be fit to the
|
|
204
|
-
// container.
|
|
205
|
-
// TODO: Enforce the minimum size (in case of both absolute and growing components).
|
|
206
|
-
|
|
207
|
-
/** @param {import("./utils/layout/flexLayout").SizeDef} dim */
|
|
208
|
-
const f = (dim) => (dim.grow > 0 ? undefined : dim.px);
|
|
209
|
-
return {
|
|
210
|
-
width: f(size.width),
|
|
211
|
-
height: f(size.height),
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
this.loadingMessageElement = document.createElement("div");
|
|
217
|
-
this.loadingMessageElement.className = "loading-message";
|
|
218
|
-
this.loadingMessageElement.innerHTML = `<div class="message">Loading<span class="ellipsis">...</span></div>`;
|
|
219
|
-
this.container.appendChild(this.loadingMessageElement);
|
|
220
|
-
|
|
221
|
-
this.tooltip = new Tooltip(this.container);
|
|
222
|
-
|
|
223
|
-
this.loadingMessageElement
|
|
224
|
-
.querySelector(".message")
|
|
225
|
-
.addEventListener("transitionend", () => {
|
|
226
|
-
/** @type {HTMLElement} */ (
|
|
227
|
-
this.loadingMessageElement
|
|
228
|
-
).style.display = "none";
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Unregisters all listeners, removes all created dom elements, removes all css classes from the container
|
|
234
|
-
*/
|
|
235
|
-
destroy() {
|
|
236
|
-
// TODO: There's a memory leak somewhere
|
|
237
|
-
|
|
238
|
-
this.container.classList.remove("genome-spy");
|
|
239
|
-
this.container.classList.remove("loading");
|
|
240
|
-
|
|
241
|
-
for (const [type, listeners] of this._keyboardListeners) {
|
|
242
|
-
for (const listener of listeners) {
|
|
243
|
-
document.removeEventListener(type, listener);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
this._glHelper.finalize();
|
|
248
|
-
|
|
249
|
-
while (this.container.firstChild) {
|
|
250
|
-
this.container.firstChild.remove();
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
async _prepareViewsAndData() {
|
|
255
|
-
if (this.spec.genome) {
|
|
256
|
-
this.genomeStore = new GenomeStore(this);
|
|
257
|
-
await this.genomeStore.initialize(this.spec.genome);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// eslint-disable-next-line consistent-this
|
|
261
|
-
const self = this;
|
|
262
|
-
|
|
263
|
-
/** @type {import("./view/viewContext").default} */
|
|
264
|
-
const context = {
|
|
265
|
-
dataFlow: new DataFlow(),
|
|
266
|
-
accessorFactory: this.accessorFactory,
|
|
267
|
-
glHelper: this._glHelper,
|
|
268
|
-
animator: this.animator,
|
|
269
|
-
genomeStore: this.genomeStore,
|
|
270
|
-
fontManager: new BmFontManager(this._glHelper),
|
|
271
|
-
requestLayoutReflow: () => {
|
|
272
|
-
// placeholder
|
|
273
|
-
},
|
|
274
|
-
updateTooltip: this.updateTooltip.bind(this),
|
|
275
|
-
getNamedDataFromProvider: this.getNamedDataFromProvider.bind(this),
|
|
276
|
-
getCurrentHover: () => this._currentHover,
|
|
277
|
-
|
|
278
|
-
addKeyboardListener: (type, listener) => {
|
|
279
|
-
// TODO: Listeners should be called only when the mouse pointer is inside the
|
|
280
|
-
// container or the app covers the full document.
|
|
281
|
-
document.addEventListener(type, listener);
|
|
282
|
-
let listeners = this._keyboardListeners.get(type);
|
|
283
|
-
if (!listeners) {
|
|
284
|
-
listeners = [];
|
|
285
|
-
this._keyboardListeners.set(type, listeners);
|
|
286
|
-
}
|
|
287
|
-
listeners.push(listener);
|
|
288
|
-
},
|
|
289
|
-
|
|
290
|
-
isViewVisible: self.viewVisibilityPredicate,
|
|
291
|
-
|
|
292
|
-
isViewSpec: (spec) => self.viewFactory.isViewSpec(spec),
|
|
293
|
-
|
|
294
|
-
createView: function (spec, parent, defaultName) {
|
|
295
|
-
return self.viewFactory.createView(
|
|
296
|
-
spec,
|
|
297
|
-
context,
|
|
298
|
-
parent,
|
|
299
|
-
defaultName
|
|
300
|
-
);
|
|
301
|
-
},
|
|
302
|
-
};
|
|
303
|
-
|
|
304
|
-
/** @type {import("./spec/view").ViewSpec & RootConfig} */
|
|
305
|
-
const rootSpec = this.spec;
|
|
306
|
-
|
|
307
|
-
if (rootSpec.datasets) {
|
|
308
|
-
this.registerNamedDataProvider((name) => rootSpec.datasets[name]);
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
// Create the view hierarchy
|
|
312
|
-
this.viewRoot = context.createView(rootSpec, null, "viewRoot");
|
|
313
|
-
|
|
314
|
-
// Replace placeholder ImportViews with actual views.
|
|
315
|
-
await processImports(this.viewRoot);
|
|
316
|
-
|
|
317
|
-
if (
|
|
318
|
-
this.viewRoot instanceof UnitView ||
|
|
319
|
-
this.viewRoot instanceof LayerView
|
|
320
|
-
) {
|
|
321
|
-
this.viewRoot = new ImplicitRootView(context, this.viewRoot);
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// Resolve scales, i.e., if possible, pull them towards the root
|
|
325
|
-
resolveScalesAndAxes(this.viewRoot);
|
|
326
|
-
setImplicitScaleNames(this.viewRoot);
|
|
327
|
-
|
|
328
|
-
// Wrap unit or layer views that need axes
|
|
329
|
-
//this.viewRoot = addDecorators(this.viewRoot);
|
|
330
|
-
|
|
331
|
-
// We should now have a complete view hierarchy. Let's update the canvas size
|
|
332
|
-
// and ensure that the loading message is visible.
|
|
333
|
-
this._glHelper.invalidateSize();
|
|
334
|
-
|
|
335
|
-
// Collect all unit views to a list because they need plenty of initialization
|
|
336
|
-
/** @type {UnitView[]} */
|
|
337
|
-
const unitViews = [];
|
|
338
|
-
this.viewRoot.visit((view) => {
|
|
339
|
-
if (view instanceof UnitView) {
|
|
340
|
-
unitViews.push(view);
|
|
341
|
-
}
|
|
342
|
-
});
|
|
343
|
-
|
|
344
|
-
// Build the data flow based on the view hierarchy
|
|
345
|
-
const flow = buildDataFlow(this.viewRoot, context.dataFlow);
|
|
346
|
-
optimizeDataFlow(flow);
|
|
347
|
-
this.broadcast("dataFlowBuilt", flow);
|
|
348
|
-
|
|
349
|
-
flow.dataSources.forEach((ds) => console.log(ds.subtreeToString()));
|
|
350
|
-
|
|
351
|
-
// Create encoders (accessors, scales and related metadata)
|
|
352
|
-
unitViews.forEach((view) => view.mark.initializeEncoders());
|
|
353
|
-
|
|
354
|
-
// Compile shaders, create or load textures, etc.
|
|
355
|
-
const graphicsInitialized = Promise.all(
|
|
356
|
-
unitViews.map((view) => view.mark.initializeGraphics())
|
|
357
|
-
);
|
|
358
|
-
|
|
359
|
-
for (const view of unitViews) {
|
|
360
|
-
flow.addObserver((collector) => {
|
|
361
|
-
view.mark.initializeData();
|
|
362
|
-
// Update WebGL buffers
|
|
363
|
-
view.mark.updateGraphicsData();
|
|
364
|
-
}, view);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
// Have to wait until asynchronous font loading is complete.
|
|
368
|
-
// Text mark's geometry builder needs font metrics before data can be
|
|
369
|
-
// converted into geometries.
|
|
370
|
-
await context.fontManager.waitUntilReady();
|
|
371
|
-
|
|
372
|
-
// Find all data sources and initiate loading
|
|
373
|
-
flow.initialize();
|
|
374
|
-
await Promise.all(
|
|
375
|
-
flow.dataSources.map((dataSource) => dataSource.load())
|
|
376
|
-
);
|
|
377
|
-
|
|
378
|
-
// Now that all data have been loaded, the domains may need adjusting
|
|
379
|
-
this.viewRoot.visit((view) => {
|
|
380
|
-
for (const resolution of Object.values(view.resolutions.scale)) {
|
|
381
|
-
// TODO: Don't reconfigure multiple times
|
|
382
|
-
// IMPORTANT TODO: Check that discrete domains and indexers match!!!!!!!!!
|
|
383
|
-
resolution.reconfigure();
|
|
384
|
-
}
|
|
385
|
-
});
|
|
386
|
-
|
|
387
|
-
// This event is needed by SampleView so that it can extract the sample ids
|
|
388
|
-
// from the data once they are loaded.
|
|
389
|
-
// TODO: It would be great if this could be attached to the data flow,
|
|
390
|
-
// because now this is somewhat a hack and is incompatible with dynamic data
|
|
391
|
-
// loading in the future.
|
|
392
|
-
this.broadcast("dataLoaded");
|
|
393
|
-
|
|
394
|
-
await graphicsInitialized;
|
|
395
|
-
|
|
396
|
-
this.viewRoot.visit((view) => {
|
|
397
|
-
for (const resolution of Object.values(view.resolutions.scale)) {
|
|
398
|
-
this._glHelper.createRangeTexture(resolution);
|
|
399
|
-
}
|
|
400
|
-
});
|
|
401
|
-
|
|
402
|
-
for (const view of unitViews) {
|
|
403
|
-
view.mark.finalizeGraphicsInitialization();
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
// Allow layout computation
|
|
407
|
-
// eslint-disable-next-line require-atomic-updates
|
|
408
|
-
context.requestLayoutReflow = this.computeLayout.bind(this);
|
|
409
|
-
|
|
410
|
-
// Invalidate cached sizes to ensure that step-based sizes are current.
|
|
411
|
-
// TODO: This should be done automatically when the domains of band/point scales are updated.
|
|
412
|
-
this.viewRoot.visit((view) => invalidatePrefix(view, "size"));
|
|
413
|
-
this._glHelper.invalidateSize();
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
/**
|
|
417
|
-
* TODO: Come up with a sensible name. And maybe this should be called at the end of the constructor.
|
|
418
|
-
* @returns {Promise<boolean>} true if the launch was successful
|
|
419
|
-
*/
|
|
420
|
-
async launch() {
|
|
421
|
-
try {
|
|
422
|
-
this._prepareContainer();
|
|
423
|
-
|
|
424
|
-
await this._prepareViewsAndData();
|
|
425
|
-
|
|
426
|
-
this.registerMouseEvents();
|
|
427
|
-
|
|
428
|
-
this.computeLayout();
|
|
429
|
-
this.animator.requestRender();
|
|
430
|
-
|
|
431
|
-
// Register resize listener after the initial layout computation to prevent
|
|
432
|
-
// incomplete layouts from accidentally polluting any caches related to sizes.
|
|
433
|
-
this._glHelper.addEventListener("resize", () => {
|
|
434
|
-
this.computeLayout();
|
|
435
|
-
// Render immediately, without RAF
|
|
436
|
-
this.renderAll();
|
|
437
|
-
});
|
|
438
|
-
|
|
439
|
-
return true;
|
|
440
|
-
} catch (reason) {
|
|
441
|
-
const message = `${
|
|
442
|
-
reason.view ? `At "${reason.view.getPathString()}": ` : ""
|
|
443
|
-
}${reason.toString()}`;
|
|
444
|
-
console.error(reason.stack);
|
|
445
|
-
createMessageBox(this.container, message);
|
|
446
|
-
|
|
447
|
-
return false;
|
|
448
|
-
} finally {
|
|
449
|
-
this.container.classList.remove("loading");
|
|
450
|
-
// Transition listener doesn't appear to work on observablehq
|
|
451
|
-
window.setTimeout(() => {
|
|
452
|
-
this.loadingMessageElement.style.display = "none";
|
|
453
|
-
}, 2000);
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
registerMouseEvents() {
|
|
458
|
-
const canvas = this._glHelper.canvas;
|
|
459
|
-
|
|
460
|
-
// TODO: This function is huge. Refactor this into a separate class
|
|
461
|
-
// that would also contain state-related stuff that currently pollute the
|
|
462
|
-
// GenomeSpy class.
|
|
463
|
-
|
|
464
|
-
/** @param {Event} event */
|
|
465
|
-
const listener = (event) => {
|
|
466
|
-
if (event instanceof MouseEvent) {
|
|
467
|
-
if (event.type == "mousemove") {
|
|
468
|
-
this.tooltip.handleMouseMove(event);
|
|
469
|
-
this._tooltipUpdateRequested = false;
|
|
470
|
-
|
|
471
|
-
if (event.buttons == 0) {
|
|
472
|
-
// Disable during dragging
|
|
473
|
-
this.renderPickingFramebuffer();
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
const rect = canvas.getBoundingClientRect();
|
|
478
|
-
const point = new Point(
|
|
479
|
-
event.clientX - rect.left - canvas.clientLeft,
|
|
480
|
-
event.clientY - rect.top - canvas.clientTop
|
|
481
|
-
);
|
|
482
|
-
|
|
483
|
-
/**
|
|
484
|
-
* @param {MouseEvent} event
|
|
485
|
-
*/
|
|
486
|
-
const dispatchEvent = (event) => {
|
|
487
|
-
this.viewRoot.propagateInteractionEvent(
|
|
488
|
-
new InteractionEvent(point, event)
|
|
489
|
-
);
|
|
490
|
-
|
|
491
|
-
if (!this._tooltipUpdateRequested) {
|
|
492
|
-
this.tooltip.clear();
|
|
493
|
-
}
|
|
494
|
-
};
|
|
495
|
-
|
|
496
|
-
if (event.type != "wheel") {
|
|
497
|
-
this._wheelInertia.cancel();
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
if (event.type == "mousemove") {
|
|
501
|
-
this._handlePicking(point.x, point.y);
|
|
502
|
-
} else if (
|
|
503
|
-
event.type == "mousedown" ||
|
|
504
|
-
event.type == "mouseup"
|
|
505
|
-
) {
|
|
506
|
-
this.renderPickingFramebuffer();
|
|
507
|
-
} else if (event.type == "wheel") {
|
|
508
|
-
this._tooltipUpdateRequested = false;
|
|
509
|
-
|
|
510
|
-
const wheelEvent = /** @type {WheelEvent} */ (event);
|
|
511
|
-
|
|
512
|
-
if (
|
|
513
|
-
Math.abs(wheelEvent.deltaX) >
|
|
514
|
-
Math.abs(wheelEvent.deltaY)
|
|
515
|
-
) {
|
|
516
|
-
// If the viewport is panned (horizontally) using the wheel (touchpad),
|
|
517
|
-
// the picking buffer becomes stale and needs redrawing. However, we
|
|
518
|
-
// optimize by just clearing the currently hovered item so that snapping
|
|
519
|
-
// doesn't work incorrectly when zooming in/out.
|
|
520
|
-
|
|
521
|
-
// TODO: More robust solution (handle at higher level such as ScaleResolution's zoom method)
|
|
522
|
-
this._currentHover = null;
|
|
523
|
-
|
|
524
|
-
this._wheelInertia.cancel();
|
|
525
|
-
} else {
|
|
526
|
-
// Vertical wheeling zooms.
|
|
527
|
-
// We use inertia to generate fake wheel events for smoother zooming
|
|
528
|
-
|
|
529
|
-
const template = makeEventTemplate(wheelEvent);
|
|
530
|
-
|
|
531
|
-
this._wheelInertia.setMomentum(
|
|
532
|
-
wheelEvent.deltaY * (wheelEvent.deltaMode ? 80 : 1),
|
|
533
|
-
(delta) => {
|
|
534
|
-
const e = new WheelEvent("wheel", {
|
|
535
|
-
...template,
|
|
536
|
-
deltaMode: 0,
|
|
537
|
-
deltaX: 0,
|
|
538
|
-
deltaY: delta,
|
|
539
|
-
});
|
|
540
|
-
dispatchEvent(e);
|
|
541
|
-
}
|
|
542
|
-
);
|
|
543
|
-
|
|
544
|
-
wheelEvent.preventDefault();
|
|
545
|
-
return;
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
// TODO: Should be handled at the view level, not globally
|
|
550
|
-
if (event.type == "click") {
|
|
551
|
-
const e = this._currentHover
|
|
552
|
-
? {
|
|
553
|
-
type: event.type,
|
|
554
|
-
viewPath: [
|
|
555
|
-
...this._currentHover.mark.unitView.getAncestors(),
|
|
556
|
-
]
|
|
557
|
-
.map((view) => view.name)
|
|
558
|
-
.reverse(),
|
|
559
|
-
datum: this._currentHover.datum,
|
|
560
|
-
}
|
|
561
|
-
: {
|
|
562
|
-
type: event.type,
|
|
563
|
-
viewPath: null,
|
|
564
|
-
datum: null,
|
|
565
|
-
};
|
|
566
|
-
|
|
567
|
-
this._eventListeners
|
|
568
|
-
.get("click")
|
|
569
|
-
?.forEach((listener) => listener(e));
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
dispatchEvent(event);
|
|
573
|
-
}
|
|
574
|
-
};
|
|
575
|
-
|
|
576
|
-
[
|
|
577
|
-
"mousedown",
|
|
578
|
-
"mouseup",
|
|
579
|
-
"wheel",
|
|
580
|
-
"click",
|
|
581
|
-
"mousemove",
|
|
582
|
-
"gesturechange",
|
|
583
|
-
"contextmenu",
|
|
584
|
-
].forEach((type) => canvas.addEventListener(type, listener));
|
|
585
|
-
|
|
586
|
-
canvas.addEventListener("mousedown", () => {
|
|
587
|
-
document.addEventListener(
|
|
588
|
-
"mouseup",
|
|
589
|
-
() => this.tooltip.popEnabledState(),
|
|
590
|
-
{ once: true }
|
|
591
|
-
);
|
|
592
|
-
this.tooltip.pushEnabledState(false);
|
|
593
|
-
});
|
|
594
|
-
|
|
595
|
-
// Prevent text selections etc while dragging
|
|
596
|
-
canvas.addEventListener("dragstart", (event) =>
|
|
597
|
-
event.stopPropagation()
|
|
598
|
-
);
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
/**
|
|
602
|
-
* @param {number} x
|
|
603
|
-
* @param {number} y
|
|
604
|
-
*/
|
|
605
|
-
_handlePicking(x, y) {
|
|
606
|
-
const pixelValue = this._glHelper.readPickingPixel(x, y);
|
|
607
|
-
|
|
608
|
-
const uniqueId =
|
|
609
|
-
pixelValue[0] | (pixelValue[1] << 8) | (pixelValue[2] << 16);
|
|
610
|
-
|
|
611
|
-
if (uniqueId == 0) {
|
|
612
|
-
this._currentHover = null;
|
|
613
|
-
return;
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
if (uniqueId !== this._currentHover?.uniqueId) {
|
|
617
|
-
this._currentHover = null;
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
if (!this._currentHover) {
|
|
621
|
-
// We are doing an exhaustive search of the data. This is a bit slow with
|
|
622
|
-
// millions of items.
|
|
623
|
-
// TODO: Optimize by indexing or something
|
|
624
|
-
|
|
625
|
-
this.viewRoot.visit((view) => {
|
|
626
|
-
if (view instanceof UnitView) {
|
|
627
|
-
if (view.mark.isPickingParticipant()) {
|
|
628
|
-
const accessor = view.mark.encoders.uniqueId.accessor;
|
|
629
|
-
view.getCollector().visitData((d) => {
|
|
630
|
-
if (accessor(d) == uniqueId) {
|
|
631
|
-
this._currentHover = {
|
|
632
|
-
mark: view.mark,
|
|
633
|
-
datum: d,
|
|
634
|
-
uniqueId,
|
|
635
|
-
};
|
|
636
|
-
}
|
|
637
|
-
});
|
|
638
|
-
}
|
|
639
|
-
if (this._currentHover) {
|
|
640
|
-
return VISIT_STOP;
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
});
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
if (this._currentHover) {
|
|
647
|
-
const mark = this._currentHover.mark;
|
|
648
|
-
this.updateTooltip(this._currentHover.datum, async (datum) => {
|
|
649
|
-
if (!mark.isPickingParticipant()) {
|
|
650
|
-
return;
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
const tooltipProps = mark.properties.tooltip;
|
|
654
|
-
|
|
655
|
-
if (tooltipProps !== null) {
|
|
656
|
-
const handlerName = tooltipProps?.handler ?? "default";
|
|
657
|
-
const handler = this.tooltipHandlers[handlerName];
|
|
658
|
-
if (!handler) {
|
|
659
|
-
throw new Error(
|
|
660
|
-
"No such tooltip handler: " + handlerName
|
|
661
|
-
);
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
return handler(datum, mark, tooltipProps?.params);
|
|
665
|
-
}
|
|
666
|
-
});
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
/**
|
|
671
|
-
* This method should be called in a mouseMove handler. If not called, the
|
|
672
|
-
* tooltip will be hidden.
|
|
673
|
-
*
|
|
674
|
-
* @param {T} datum
|
|
675
|
-
* @param {function(T):Promise<string | HTMLElement | import("lit").TemplateResult>} [converter]
|
|
676
|
-
* @template T
|
|
677
|
-
*/
|
|
678
|
-
updateTooltip(datum, converter) {
|
|
679
|
-
if (!this._tooltipUpdateRequested || !datum) {
|
|
680
|
-
this.tooltip.updateWithDatum(datum, converter);
|
|
681
|
-
this._tooltipUpdateRequested = true;
|
|
682
|
-
} else {
|
|
683
|
-
throw new Error(
|
|
684
|
-
"Tooltip has already been updated! Duplicate event handler?"
|
|
685
|
-
);
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
computeLayout() {
|
|
690
|
-
const root = this.viewRoot;
|
|
691
|
-
if (!root) {
|
|
692
|
-
return;
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
this.broadcast("layout");
|
|
696
|
-
|
|
697
|
-
const canvasSize = this._glHelper.getLogicalCanvasSize();
|
|
698
|
-
|
|
699
|
-
if (isNaN(canvasSize.width) || isNaN(canvasSize.height)) {
|
|
700
|
-
// TODO: Figure out what causes this
|
|
701
|
-
console.log(
|
|
702
|
-
`NaN in canvas size: ${canvasSize.width}x${canvasSize.height}. Skipping computeLayout().`
|
|
703
|
-
);
|
|
704
|
-
return;
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
this._renderingContext = new DeferredViewRenderingContext(
|
|
708
|
-
{
|
|
709
|
-
picking: false,
|
|
710
|
-
},
|
|
711
|
-
this._glHelper
|
|
712
|
-
);
|
|
713
|
-
this._pickingContext = new DeferredViewRenderingContext(
|
|
714
|
-
{
|
|
715
|
-
picking: true,
|
|
716
|
-
},
|
|
717
|
-
this._glHelper
|
|
718
|
-
);
|
|
719
|
-
|
|
720
|
-
root.render(
|
|
721
|
-
new CompositeViewRenderingContext(
|
|
722
|
-
this._renderingContext,
|
|
723
|
-
this._pickingContext
|
|
724
|
-
),
|
|
725
|
-
// Canvas should now be sized based on the root view or the container
|
|
726
|
-
Rectangle.create(0, 0, canvasSize.width, canvasSize.height)
|
|
727
|
-
);
|
|
728
|
-
|
|
729
|
-
this.broadcast("layoutComputed");
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
renderAll() {
|
|
733
|
-
this._renderingContext?.renderDeferred();
|
|
734
|
-
|
|
735
|
-
this._dirtyPickingBuffer = true;
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
renderPickingFramebuffer() {
|
|
739
|
-
if (!this._dirtyPickingBuffer) {
|
|
740
|
-
return;
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
this._pickingContext.renderDeferred();
|
|
744
|
-
this._dirtyPickingBuffer = false;
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
getSearchableViews() {
|
|
748
|
-
/** @type {UnitView[]} */
|
|
749
|
-
const views = [];
|
|
750
|
-
this.viewRoot.visit((view) => {
|
|
751
|
-
if (view instanceof UnitView && view.getAccessor("search")) {
|
|
752
|
-
views.push(view);
|
|
753
|
-
}
|
|
754
|
-
});
|
|
755
|
-
return views;
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
getNamedScaleResolutions() {
|
|
759
|
-
/** @type {Map<string, import("./view/scaleResolution").default>} */
|
|
760
|
-
const resolutions = new Map();
|
|
761
|
-
this.viewRoot.visit((view) => {
|
|
762
|
-
for (const resolution of Object.values(view.resolutions.scale)) {
|
|
763
|
-
if (resolution.name) {
|
|
764
|
-
resolutions.set(resolution.name, resolution);
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
});
|
|
768
|
-
return resolutions;
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
/**
|
|
773
|
-
*
|
|
774
|
-
* @param {HTMLElement} container
|
|
775
|
-
* @param {string} message
|
|
776
|
-
*/
|
|
777
|
-
function createMessageBox(container, message) {
|
|
778
|
-
// Uh, need a templating thingy
|
|
779
|
-
const messageBox = document.createElement("div");
|
|
780
|
-
messageBox.className = "message-box";
|
|
781
|
-
const messageText = document.createElement("div");
|
|
782
|
-
messageText.textContent = message;
|
|
783
|
-
messageBox.appendChild(messageText);
|
|
784
|
-
container.appendChild(messageBox);
|
|
785
|
-
}
|