react-msaview 5.10.0 → 6.1.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/bundle/index.js +99 -99
- package/bundle/index.js.map +4 -4
- package/dist/clustalX.js +22 -22
- package/dist/clustalX.js.map +1 -1
- package/dist/columnCounts.d.ts +7 -0
- package/dist/columnCounts.js +18 -0
- package/dist/columnCounts.js.map +1 -1
- package/dist/components/CopyButton.d.ts +9 -0
- package/dist/components/CopyButton.js +23 -0
- package/dist/components/CopyButton.js.map +1 -0
- package/dist/components/DragHandle.d.ts +25 -0
- package/dist/components/DragHandle.js +49 -0
- package/dist/components/DragHandle.js.map +1 -0
- package/dist/components/Loading.js +2 -2
- package/dist/components/Loading.js.map +1 -1
- package/dist/components/MSAView.js +2 -2
- package/dist/components/MSAView.js.map +1 -1
- package/dist/components/ResizeHandles.d.ts +4 -2
- package/dist/components/ResizeHandles.js +35 -54
- package/dist/components/ResizeHandles.js.map +1 -1
- package/dist/components/SequenceTextArea.js +3 -10
- package/dist/components/SequenceTextArea.js.map +1 -1
- package/dist/components/VerticalScrollbar.js +11 -25
- package/dist/components/VerticalScrollbar.js.map +1 -1
- package/dist/components/dialogs/FeatureDialog.js +3 -3
- package/dist/components/dialogs/FeatureDialog.js.map +1 -1
- package/dist/components/dialogs/InterProScanDialog.js +52 -129
- package/dist/components/dialogs/InterProScanDialog.js.map +1 -1
- package/dist/components/dialogs/{UserProvidedDomainsDialog.d.ts → InterProScanFileDialog.d.ts} +2 -2
- package/dist/components/dialogs/{UserProvidedDomainsDialog.js → InterProScanFileDialog.js} +6 -10
- package/dist/components/dialogs/InterProScanFileDialog.js.map +1 -0
- package/dist/components/dialogs/TrackInfoDialog.d.ts +2 -4
- package/dist/components/dialogs/TrackInfoDialog.js +8 -14
- package/dist/components/dialogs/TrackInfoDialog.js.map +1 -1
- package/dist/components/header/FileMenu.js +3 -3
- package/dist/components/header/FileMenu.js.map +1 -1
- package/dist/components/header/GappynessSlider.js +1 -1
- package/dist/components/header/GappynessSlider.js.map +1 -1
- package/dist/components/header/{getDomainsMenu.d.ts → getAnnotationsMenu.d.ts} +1 -1
- package/dist/components/header/{getDomainsMenu.js → getAnnotationsMenu.js} +10 -9
- package/dist/components/header/getAnnotationsMenu.js.map +1 -0
- package/dist/components/header/settingsMenuItems.js +15 -0
- package/dist/components/header/settingsMenuItems.js.map +1 -1
- package/dist/components/import/ImportForm.js +1 -1
- package/dist/components/import/ImportForm.js.map +1 -1
- package/dist/components/import/ImportFormExamples.js +66 -57
- package/dist/components/import/ImportFormExamples.js.map +1 -1
- package/dist/components/minimap/Minimap.js +40 -23
- package/dist/components/minimap/Minimap.js.map +1 -1
- package/dist/components/minimap/MinimapSVG.d.ts +2 -1
- package/dist/components/minimap/MinimapSVG.js +3 -4
- package/dist/components/minimap/MinimapSVG.js.map +1 -1
- package/dist/components/minimap/minimapLayout.d.ts +10 -1
- package/dist/components/minimap/minimapLayout.js +15 -10
- package/dist/components/minimap/minimapLayout.js.map +1 -1
- package/dist/components/msa/{DomainLegend.d.ts → AnnotationLegend.d.ts} +2 -2
- package/dist/components/msa/{DomainLegend.js → AnnotationLegend.js} +4 -4
- package/dist/components/msa/AnnotationLegend.js.map +1 -0
- package/dist/components/msa/MSACanvas.js +9 -3
- package/dist/components/msa/MSACanvas.js.map +1 -1
- package/dist/components/msa/MSACanvasBlock.js +12 -3
- package/dist/components/msa/MSACanvasBlock.js.map +1 -1
- package/dist/components/msa/MSAMouseoverCanvas.js +1 -2
- package/dist/components/msa/MSAMouseoverCanvas.js.map +1 -1
- package/dist/components/msa/MSAPanel.js +2 -2
- package/dist/components/msa/MSAPanel.js.map +1 -1
- package/dist/components/msa/domainBandCursor.d.ts +17 -0
- package/dist/components/msa/domainBandCursor.js +39 -0
- package/dist/components/msa/domainBandCursor.js.map +1 -0
- package/dist/components/msa/msaRaster.d.ts +63 -0
- package/dist/components/msa/msaRaster.js +252 -0
- package/dist/components/msa/msaRaster.js.map +1 -0
- package/dist/components/msa/renderMSABlock.d.ts +2 -1
- package/dist/components/msa/renderMSABlock.js +27 -41
- package/dist/components/msa/renderMSABlock.js.map +1 -1
- package/dist/components/msa/renderMSAMouseover.js +1 -2
- package/dist/components/msa/renderMSAMouseover.js.map +1 -1
- package/dist/components/msa/tileColor.d.ts +6 -0
- package/dist/components/msa/tileColor.js +35 -0
- package/dist/components/msa/tileColor.js.map +1 -0
- package/dist/components/renderCtx.d.ts +1 -1
- package/dist/components/tracks/TrackBlocks.d.ts +14 -0
- package/dist/components/tracks/TrackBlocks.js +65 -0
- package/dist/components/tracks/TrackBlocks.js.map +1 -0
- package/dist/components/tracks/{renderTracksSvg.d.ts → drawTracks.d.ts} +22 -10
- package/dist/components/tracks/drawTracks.js +193 -0
- package/dist/components/tracks/drawTracks.js.map +1 -0
- package/dist/components/tree/TreeCanvas.js +8 -2
- package/dist/components/tree/TreeCanvas.js.map +1 -1
- package/dist/components/tree/TreeCanvasBlock.js +7 -4
- package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
- package/dist/components/tree/TreeRuler.js +5 -2
- package/dist/components/tree/TreeRuler.js.map +1 -1
- package/dist/components/tree/renderTreeCanvas.js +5 -2
- package/dist/components/tree/renderTreeCanvas.js.map +1 -1
- package/dist/constants.d.ts +3 -0
- package/dist/constants.js +8 -0
- package/dist/constants.js.map +1 -1
- package/dist/ggplotPalettes.d.ts +0 -1
- package/dist/ggplotPalettes.js +0 -3
- package/dist/ggplotPalettes.js.map +1 -1
- package/dist/hierarchy.d.ts +2 -6
- package/dist/hierarchy.js +4 -6
- package/dist/hierarchy.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/launchInterProScan.d.ts +0 -23
- package/dist/launchInterProScan.js +2 -5
- package/dist/launchInterProScan.js.map +1 -1
- package/dist/measureTextCanvas.js +21 -6
- package/dist/measureTextCanvas.js.map +1 -1
- package/dist/model/DataModel.d.ts +1 -4
- package/dist/model/DataModel.js +8 -10
- package/dist/model/DataModel.js.map +1 -1
- package/dist/model.d.ts +86 -107
- package/dist/model.js +238 -162
- package/dist/model.js.map +1 -1
- package/dist/neighborJoining.js +34 -18
- package/dist/neighborJoining.js.map +1 -1
- package/dist/renderTestEnv.d.ts +43 -0
- package/dist/renderTestEnv.js +88 -0
- package/dist/renderTestEnv.js.map +1 -0
- package/dist/renderToSvg.js +3 -3
- package/dist/renderToSvg.js.map +1 -1
- package/dist/rowCoordinateCalculations.d.ts +13 -17
- package/dist/rowCoordinateCalculations.js +45 -39
- package/dist/rowCoordinateCalculations.js.map +1 -1
- package/dist/sequenceLogo.d.ts +28 -0
- package/dist/sequenceLogo.js +47 -0
- package/dist/sequenceLogo.js.map +1 -0
- package/dist/types.d.ts +6 -11
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +5 -5
- package/src/clustalX.ts +22 -22
- package/src/colSpace.test.ts +27 -0
- package/src/columnCounts.test.ts +15 -0
- package/src/columnCounts.ts +19 -0
- package/src/components/CopyButton.tsx +44 -0
- package/src/components/DragHandle.tsx +81 -0
- package/src/components/Loading.tsx +10 -1
- package/src/components/MSAView.tsx +2 -3
- package/src/components/ResizeHandles.tsx +70 -87
- package/src/components/SequenceTextArea.tsx +3 -16
- package/src/components/VerticalScrollbar.tsx +20 -33
- package/src/components/dialogs/FeatureDialog.tsx +3 -4
- package/src/components/dialogs/InterProScanDialog.tsx +54 -131
- package/src/components/dialogs/{UserProvidedDomainsDialog.tsx → InterProScanFileDialog.tsx} +6 -14
- package/src/components/dialogs/TrackInfoDialog.tsx +18 -20
- package/src/components/header/FileMenu.tsx +3 -3
- package/src/components/header/GappynessSlider.tsx +4 -1
- package/src/components/header/{getDomainsMenu.ts → getAnnotationsMenu.ts} +10 -9
- package/src/components/header/settingsMenuItems.ts +18 -0
- package/src/components/import/ImportForm.tsx +1 -1
- package/src/components/import/ImportFormExamples.tsx +82 -133
- package/src/components/minimap/Minimap.tsx +67 -32
- package/src/components/minimap/MinimapSVG.tsx +32 -31
- package/src/components/minimap/minimapLayout.test.ts +60 -0
- package/src/components/minimap/minimapLayout.ts +15 -10
- package/src/components/msa/{DomainLegend.tsx → AnnotationLegend.tsx} +7 -3
- package/src/components/msa/MSACanvas.tsx +24 -11
- package/src/components/msa/MSACanvasBlock.tsx +13 -3
- package/src/components/msa/MSAMouseoverCanvas.tsx +1 -3
- package/src/components/msa/MSAPanel.tsx +2 -2
- package/src/components/msa/domainBandCursor.ts +41 -0
- package/src/components/msa/msaRaster.test.ts +145 -0
- package/src/components/msa/msaRaster.ts +350 -0
- package/src/components/msa/renderMSABlock.ts +34 -43
- package/src/components/msa/renderMSAMouseover.ts +1 -3
- package/src/components/msa/tileColor.ts +37 -0
- package/src/components/renderCtx.ts +2 -0
- package/src/components/tracks/TrackBlocks.tsx +111 -0
- package/src/components/tracks/drawTracks.ts +323 -0
- package/src/components/tree/TreeCanvas.tsx +16 -4
- package/src/components/tree/TreeCanvasBlock.tsx +18 -11
- package/src/components/tree/TreeRuler.tsx +7 -2
- package/src/components/tree/renderTreeCanvas.test.ts +15 -7
- package/src/components/tree/renderTreeCanvas.ts +5 -1
- package/src/constants.ts +10 -0
- package/src/domainBands.test.ts +24 -0
- package/src/domainLetterColor.test.ts +80 -0
- package/src/ggplotPalettes.ts +0 -4
- package/src/hierarchy.test.ts +7 -7
- package/src/hierarchy.ts +6 -9
- package/src/impgRender.test.tsx +2 -57
- package/src/index.ts +4 -1
- package/src/largeTree.test.ts +39 -4
- package/src/launchInterProScan.ts +3 -30
- package/src/loadErrorDisplay.test.tsx +102 -0
- package/src/measureTextCanvas.ts +22 -6
- package/src/model/DataModel.ts +11 -11
- package/src/model.ts +262 -172
- package/src/neighborJoining.ts +36 -18
- package/src/renderDomainsSvg.test.tsx +2 -57
- package/src/renderMinimapSvg.test.tsx +55 -0
- package/src/renderSequenceLogoSvg.test.tsx +176 -0
- package/src/renderTestEnv.ts +96 -0
- package/src/renderToSvg.tsx +3 -3
- package/src/rowCoordinateCalculations.ts +48 -44
- package/src/sequenceLogo.test.ts +88 -0
- package/src/sequenceLogo.ts +63 -0
- package/src/types.ts +17 -16
- package/src/version.ts +1 -1
- package/src/viewChrome.test.tsx +134 -0
- package/dist/components/ConservationTrack.d.ts +0 -8
- package/dist/components/ConservationTrack.js +0 -49
- package/dist/components/ConservationTrack.js.map +0 -1
- package/dist/components/TextTrack.d.ts +0 -8
- package/dist/components/TextTrack.js +0 -58
- package/dist/components/TextTrack.js.map +0 -1
- package/dist/components/dialogs/DomainDialog.d.ts +0 -6
- package/dist/components/dialogs/DomainDialog.js +0 -19
- package/dist/components/dialogs/DomainDialog.js.map +0 -1
- package/dist/components/dialogs/UserProvidedDomainsDialog.js.map +0 -1
- package/dist/components/header/SettingsMenu.d.ts +0 -6
- package/dist/components/header/SettingsMenu.js +0 -40
- package/dist/components/header/SettingsMenu.js.map +0 -1
- package/dist/components/header/getDomainsMenu.js.map +0 -1
- package/dist/components/msa/DomainLegend.js.map +0 -1
- package/dist/components/tracks/renderTracksSvg.js +0 -126
- package/dist/components/tracks/renderTracksSvg.js.map +0 -1
- package/src/components/ConservationTrack.tsx +0 -99
- package/src/components/TextTrack.tsx +0 -103
- package/src/components/dialogs/DomainDialog.tsx +0 -45
- package/src/components/header/SettingsMenu.tsx +0 -57
- package/src/components/tracks/renderTracksSvg.ts +0 -247
package/dist/model.js
CHANGED
|
@@ -4,18 +4,17 @@ import { ElementId, FileLocation } from '@jbrowse/core/util/types/mst';
|
|
|
4
4
|
import { addDisposer, cast, types } from '@jbrowse/mobx-state-tree';
|
|
5
5
|
import { colord } from 'colord';
|
|
6
6
|
import { autorun, transaction } from 'mobx';
|
|
7
|
-
import { generateNodeIds,
|
|
7
|
+
import { generateNodeIds, gffToAnnotations, interProScanToAnnotations, parseEmfTree, parseGFF, parseMSA, parseNewick, } from 'msa-parsers';
|
|
8
8
|
import { calculateBlocks } from './calculateBlocks.js';
|
|
9
9
|
import { clustalXColumnColors } from './clustalX.js';
|
|
10
10
|
import colorSchemes from './colorSchemes.js';
|
|
11
11
|
import { columnCountsFromRows, letterOfResidueSlot } from './columnCounts.js';
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
import { defaultAllowedGappyness, defaultColWidth, defaultColorSchemeName, defaultCurrentAlignment, defaultDrawMsaLetters, defaultHeight, defaultHideGaps, defaultRowHeight, defaultScrollX, defaultScrollY, defaultScrollZoom, defaultShowDomainLegend, defaultShowDomains, defaultSubFeatureRows, maxCellSize, minColWidth, minRowHeight, segmentFeatureTypes, segmentShades, } from './constants.js';
|
|
12
|
+
import TrackBlocks from './components/tracks/TrackBlocks.js';
|
|
13
|
+
import { defaultAllowedGappyness, defaultColWidth, defaultColorSchemeName, defaultCurrentAlignment, defaultDrawMsaLetters, defaultHeight, defaultHideGaps, defaultRowHeight, defaultScrollX, defaultScrollY, defaultScrollZoom, defaultShowDomainLegend, defaultShowDomains, defaultSubFeatureRows, labelReferenceFontSize, maxCellSize, minColWidth, minLetterColWidth, minLetterRowHeight, minRowHeight, segmentFeatureTypes, segmentShades, } from './constants.js';
|
|
15
14
|
import { createPaletteMap } from './createPaletteMap.js';
|
|
16
15
|
import { fetchTextWithProgress, isAbortError } from './fetchUtils.js';
|
|
17
16
|
import { flatToTree } from './flatToTree.js';
|
|
18
|
-
import { calcDepthToLeaf, clusterLayout, collapse,
|
|
17
|
+
import { calcDepthToLeaf, clusterLayout, collapse, collapsedSubtreeMaxLength, find, findMaxBranchLen, forEachDescendant, hierarchy, leaves, links, maxLength, setBrLength, sort, sum as hierarchySum, } from './hierarchy.js';
|
|
19
18
|
import { measureTextCanvas } from './measureTextCanvas.js';
|
|
20
19
|
import { DataModelF } from './model/DataModel.js';
|
|
21
20
|
import { DialogQueueSessionMixin } from './model/DialogQueue.js';
|
|
@@ -24,8 +23,9 @@ import { TreeModelF } from './model/treeModel.js';
|
|
|
24
23
|
import { calculateNeighborJoiningTree } from './neighborJoining.js';
|
|
25
24
|
import { parseAsn1 } from './parseAsn1.js';
|
|
26
25
|
import { calculatePropertyConservation } from './propertyConservation.js';
|
|
27
|
-
import { globalColToVisibleCol, visibleColToGlobalCol, visibleColToSeqPosForRow, } from './rowCoordinateCalculations.js';
|
|
26
|
+
import { globalColToVisibleCol, visibleColToGlobalCol, visibleColToSeqPosForRow, visibleColsBefore, } from './rowCoordinateCalculations.js';
|
|
28
27
|
import { buildSeqPosIndex } from './seqPosToGlobalCol.js';
|
|
28
|
+
import { maxBitsFor } from './sequenceLogo.js';
|
|
29
29
|
import { stripDefault } from './stripDefault.js';
|
|
30
30
|
import { computeRowInsertions, len, skipBlanks, transform } from './util.js';
|
|
31
31
|
import { saveAs } from './vendor/fileSaver.js';
|
|
@@ -35,11 +35,22 @@ function parseTreeText(text) {
|
|
|
35
35
|
}
|
|
36
36
|
return parseNewick(text.startsWith('SEQ') ? parseEmfTree(text).tree : text);
|
|
37
37
|
}
|
|
38
|
+
// Tracks that start hidden. The sequence logo answers a narrower question than
|
|
39
|
+
// conservation does and costs three times the vertical space, so it waits to be
|
|
40
|
+
// asked for.
|
|
41
|
+
const defaultOffTracks = new Set(['sequence-logo']);
|
|
42
|
+
// `turnedOffTracks` records the user's explicit choices only: an id is absent
|
|
43
|
+
// until they touch that track, and then its value is whether the track is OFF.
|
|
44
|
+
// Reading the default through this is what lets a track ship hidden without
|
|
45
|
+
// writing an entry into every snapshot and shared URL.
|
|
46
|
+
function trackIsOff(turnedOffTracks, id) {
|
|
47
|
+
return turnedOffTracks.get(id) ?? defaultOffTracks.has(id);
|
|
48
|
+
}
|
|
38
49
|
/**
|
|
39
50
|
* #stateModel MsaView
|
|
40
51
|
*
|
|
41
52
|
* The main MSAView state model. Holds the loaded alignment, tree, and optional
|
|
42
|
-
*
|
|
53
|
+
* overlay annotations, plus all display state (color scheme, zoom,
|
|
43
54
|
* scroll, collapsed clades). It composes in members from `DialogQueueSessionMixin`,
|
|
44
55
|
* `Tree`, and `MSAModel` (see Inherited members below). Data is loaded reactively
|
|
45
56
|
* from the `msaFilehandle` / `treeFilehandle` / `gffFilehandle` properties, or set
|
|
@@ -145,7 +156,7 @@ function stateModelFactory() {
|
|
|
145
156
|
treeMetadataFilehandle: types.maybe(FileLocation),
|
|
146
157
|
/**
|
|
147
158
|
* #property
|
|
148
|
-
* filehandle object for
|
|
159
|
+
* filehandle object for a GFF file of overlay annotations
|
|
149
160
|
*/
|
|
150
161
|
gffFilehandle: types.maybe(FileLocation),
|
|
151
162
|
/**
|
|
@@ -165,7 +176,10 @@ function stateModelFactory() {
|
|
|
165
176
|
showOnly: types.maybe(types.string),
|
|
166
177
|
/**
|
|
167
178
|
* #property
|
|
168
|
-
*
|
|
179
|
+
* the user's explicit show/hide choice per track id, keyed by id with
|
|
180
|
+
* the value meaning "off". A track the user has never touched is absent
|
|
181
|
+
* and falls back to its own default (see `defaultOffTracks`), so a
|
|
182
|
+
* hidden-by-default track adds nothing to the shared URL.
|
|
169
183
|
*/
|
|
170
184
|
turnedOffTracks: stripDefault(types.map(types.boolean), {}),
|
|
171
185
|
/**
|
|
@@ -272,6 +286,13 @@ function stateModelFactory() {
|
|
|
272
286
|
* #volatile
|
|
273
287
|
*/
|
|
274
288
|
conservationTrackHeight: 40,
|
|
289
|
+
/**
|
|
290
|
+
* #volatile
|
|
291
|
+
* taller than the conservation track by default: the logo spends its
|
|
292
|
+
* height on stacked glyphs, and a 40px stack of four residues leaves each
|
|
293
|
+
* one too short to identify
|
|
294
|
+
*/
|
|
295
|
+
sequenceLogoTrackHeight: 80,
|
|
275
296
|
/**
|
|
276
297
|
* #volatile
|
|
277
298
|
*/
|
|
@@ -283,8 +304,12 @@ function stateModelFactory() {
|
|
|
283
304
|
error: undefined,
|
|
284
305
|
/**
|
|
285
306
|
* #volatile
|
|
307
|
+
* overlay annotations drawn on the alignment, whatever their source.
|
|
308
|
+
* Every source -- InterProScan, GFF, a user upload -- converts to this
|
|
309
|
+
* flat list before it reaches the model, so nothing downstream of here
|
|
310
|
+
* knows which one it came from
|
|
286
311
|
*/
|
|
287
|
-
|
|
312
|
+
annotations: [],
|
|
288
313
|
}))
|
|
289
314
|
.actions(self => ({
|
|
290
315
|
/**
|
|
@@ -373,7 +398,7 @@ function stateModelFactory() {
|
|
|
373
398
|
},
|
|
374
399
|
/**
|
|
375
400
|
* #action
|
|
376
|
-
* toggle the
|
|
401
|
+
* toggle the annotation overlay on the alignment
|
|
377
402
|
*/
|
|
378
403
|
setShowDomains(arg) {
|
|
379
404
|
self.showDomains = arg;
|
|
@@ -413,13 +438,6 @@ function stateModelFactory() {
|
|
|
413
438
|
setColWidth(n) {
|
|
414
439
|
self.colWidth = n;
|
|
415
440
|
},
|
|
416
|
-
/**
|
|
417
|
-
* #action
|
|
418
|
-
* set scroll Y-offset (px)
|
|
419
|
-
*/
|
|
420
|
-
setScrollY(n) {
|
|
421
|
-
self.scrollY = n;
|
|
422
|
-
},
|
|
423
441
|
/**
|
|
424
442
|
* #action
|
|
425
443
|
*
|
|
@@ -511,7 +529,7 @@ function stateModelFactory() {
|
|
|
511
529
|
* #getter
|
|
512
530
|
*/
|
|
513
531
|
get actuallyShowDomains() {
|
|
514
|
-
return self.showDomains &&
|
|
532
|
+
return self.showDomains && self.annotations.length > 0;
|
|
515
533
|
},
|
|
516
534
|
get viewInitialized() {
|
|
517
535
|
return self.volatileWidth !== undefined;
|
|
@@ -562,7 +580,7 @@ function stateModelFactory() {
|
|
|
562
580
|
return !!this.tree.noTree;
|
|
563
581
|
},
|
|
564
582
|
get noDomains() {
|
|
565
|
-
return
|
|
583
|
+
return self.annotations.length === 0;
|
|
566
584
|
},
|
|
567
585
|
menuItems() {
|
|
568
586
|
return [];
|
|
@@ -742,12 +760,6 @@ function stateModelFactory() {
|
|
|
742
760
|
}
|
|
743
761
|
return blanks;
|
|
744
762
|
},
|
|
745
|
-
/**
|
|
746
|
-
* #getter
|
|
747
|
-
*/
|
|
748
|
-
get blanksSet() {
|
|
749
|
-
return new Set(this.blanks);
|
|
750
|
-
},
|
|
751
763
|
/**
|
|
752
764
|
* #getter
|
|
753
765
|
* Returns a map of row name to array of insertions with display position and letters
|
|
@@ -828,13 +840,6 @@ function stateModelFactory() {
|
|
|
828
840
|
get colStats() {
|
|
829
841
|
return columnCountsFromRows(this.columns2d);
|
|
830
842
|
},
|
|
831
|
-
/**
|
|
832
|
-
* #getter
|
|
833
|
-
* per-column residue totals (gaps included)
|
|
834
|
-
*/
|
|
835
|
-
get colStatsSums() {
|
|
836
|
-
return this.colStats.totals;
|
|
837
|
-
},
|
|
838
843
|
/**
|
|
839
844
|
* #getter
|
|
840
845
|
* Detects sequence type based on letters present in the alignment.
|
|
@@ -893,26 +898,25 @@ function stateModelFactory() {
|
|
|
893
898
|
* Returns values 0-1 where 1 = fully conserved, 0 = no conservation.
|
|
894
899
|
*/
|
|
895
900
|
get conservation() {
|
|
896
|
-
const { colStats,
|
|
897
|
-
const alphabetSize = sequenceType === 'amino' ? 20 : 4;
|
|
898
|
-
const maxEntropy = Math.log2(alphabetSize);
|
|
901
|
+
const { colStats, alphabetMaxBits } = this;
|
|
899
902
|
return Array.from({ length: colStats.numColumns }, (_, col) => {
|
|
900
903
|
const total = colStats.total(col);
|
|
901
904
|
const gapCount = colStats.gapCount(col);
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
colStats.forEachResidue(col, (_slot, count) => {
|
|
907
|
-
const freq = count / nonGapTotal;
|
|
908
|
-
entropy -= freq * Math.log2(freq);
|
|
909
|
-
});
|
|
910
|
-
score =
|
|
911
|
-
Math.max(0, 1 - entropy / maxEntropy) * (1 - gapCount / total);
|
|
912
|
-
}
|
|
913
|
-
return score;
|
|
905
|
+
return total > gapCount
|
|
906
|
+
? Math.max(0, 1 - colStats.entropy(col) / alphabetMaxBits) *
|
|
907
|
+
(1 - gapCount / total)
|
|
908
|
+
: 0;
|
|
914
909
|
});
|
|
915
910
|
},
|
|
911
|
+
/**
|
|
912
|
+
* #getter
|
|
913
|
+
* The information content of a fully conserved column, in bits, which
|
|
914
|
+
* depends on the alphabet. Both the entropy ceiling `conservation`
|
|
915
|
+
* normalizes against and the y-axis ceiling of the sequence logo track.
|
|
916
|
+
*/
|
|
917
|
+
get alphabetMaxBits() {
|
|
918
|
+
return maxBitsFor(this.sequenceType);
|
|
919
|
+
},
|
|
916
920
|
/**
|
|
917
921
|
* #getter
|
|
918
922
|
* Per-column conservation of physicochemical property class (amino acids
|
|
@@ -935,14 +939,13 @@ function stateModelFactory() {
|
|
|
935
939
|
const max = maxLength(r);
|
|
936
940
|
const k = max ? self.treeWidth / max : 0;
|
|
937
941
|
setBrLength(r, 0, k);
|
|
938
|
-
// for each collapsed clade, record the pixel x-
|
|
939
|
-
//
|
|
940
|
-
//
|
|
942
|
+
// for each collapsed clade, record the pixel x-position of its farthest
|
|
943
|
+
// tip so the renderer can draw a triangle spanning the branch-length
|
|
944
|
+
// extent of the hidden subtree
|
|
941
945
|
forEachDescendant(r, node => {
|
|
942
946
|
if (node._children) {
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
node.collapsedTipXFar = (node.len ?? 0) + mx * k;
|
|
947
|
+
node.collapsedTipXFar =
|
|
948
|
+
(node.len ?? 0) + collapsedSubtreeMaxLength(node) * k;
|
|
946
949
|
}
|
|
947
950
|
});
|
|
948
951
|
return r;
|
|
@@ -1018,12 +1021,22 @@ function stateModelFactory() {
|
|
|
1018
1021
|
*/
|
|
1019
1022
|
get blocksY() {
|
|
1020
1023
|
return calculateBlocks({
|
|
1021
|
-
viewportSize:
|
|
1024
|
+
viewportSize: this.visibleMsaHeight,
|
|
1022
1025
|
viewportPos: -self.scrollY,
|
|
1023
1026
|
blockSize: self.blockSize,
|
|
1024
1027
|
mapSize: self.totalHeight,
|
|
1025
1028
|
});
|
|
1026
1029
|
},
|
|
1030
|
+
/**
|
|
1031
|
+
* #getter
|
|
1032
|
+
* height of the alignment viewport, px. The same subtraction as
|
|
1033
|
+
* msaAreaHeight, which is defined later in the views chain
|
|
1034
|
+
*/
|
|
1035
|
+
get visibleMsaHeight() {
|
|
1036
|
+
return (self.height -
|
|
1037
|
+
self.headerHeight -
|
|
1038
|
+
(self.msaAreaWidth < self.totalWidth ? self.minimapHeight : 0));
|
|
1039
|
+
},
|
|
1027
1040
|
}))
|
|
1028
1041
|
.views(self => ({
|
|
1029
1042
|
/**
|
|
@@ -1047,28 +1060,25 @@ function stateModelFactory() {
|
|
|
1047
1060
|
/**
|
|
1048
1061
|
* #getter
|
|
1049
1062
|
* most-negative allowed scrollY, keeping the last row in view rather than
|
|
1050
|
-
* letting the whole alignment scroll off the top.
|
|
1051
|
-
* computed inline here because msaAreaHeight is defined later in the chain.
|
|
1063
|
+
* letting the whole alignment scroll off the top.
|
|
1052
1064
|
*/
|
|
1053
1065
|
get maxScrollY() {
|
|
1054
|
-
|
|
1055
|
-
self.headerHeight -
|
|
1056
|
-
(self.msaAreaWidth < self.totalWidth ? self.minimapHeight : 0);
|
|
1057
|
-
return Math.min(-self.totalHeight + visibleHeight, 0);
|
|
1066
|
+
return Math.min(-self.totalHeight + self.visibleMsaHeight, 0);
|
|
1058
1067
|
},
|
|
1059
1068
|
/**
|
|
1060
1069
|
* #getter
|
|
1061
1070
|
*/
|
|
1062
1071
|
get showMsaLetters() {
|
|
1063
1072
|
return (self.drawMsaLetters &&
|
|
1064
|
-
self.rowHeight >=
|
|
1073
|
+
self.rowHeight >= minLetterRowHeight &&
|
|
1074
|
+
self.colWidth >= minLetterColWidth &&
|
|
1065
1075
|
self.colWidth > self.rowHeight / 2);
|
|
1066
1076
|
},
|
|
1067
1077
|
/**
|
|
1068
1078
|
* #getter
|
|
1069
1079
|
*/
|
|
1070
1080
|
get showTreeText() {
|
|
1071
|
-
return self.drawLabels && self.rowHeight >=
|
|
1081
|
+
return self.drawLabels && self.rowHeight >= minLetterRowHeight;
|
|
1072
1082
|
},
|
|
1073
1083
|
}))
|
|
1074
1084
|
.actions(self => ({
|
|
@@ -1089,18 +1099,24 @@ function stateModelFactory() {
|
|
|
1089
1099
|
* set hovered tree node and its descendants
|
|
1090
1100
|
*/
|
|
1091
1101
|
setHoveredTreeNode(nodeId) {
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
}
|
|
1099
|
-
: undefined;
|
|
1102
|
+
// the tree's mousemove handler calls this on every event, and both the
|
|
1103
|
+
// lookup and the write are expensive: `find` walks the whole hierarchy,
|
|
1104
|
+
// and a fresh object here invalidates hoveredRowIndices and redraws the
|
|
1105
|
+
// tree and MSA overlays. Re-hovering the same node is the common case
|
|
1106
|
+
if (nodeId === self.hoveredTreeNode?.nodeId) {
|
|
1107
|
+
return;
|
|
1100
1108
|
}
|
|
1101
|
-
|
|
1109
|
+
if (!nodeId) {
|
|
1102
1110
|
self.hoveredTreeNode = undefined;
|
|
1111
|
+
return;
|
|
1103
1112
|
}
|
|
1113
|
+
const node = find(self.hierarchy, n => n.data.id === nodeId);
|
|
1114
|
+
self.hoveredTreeNode = node
|
|
1115
|
+
? {
|
|
1116
|
+
nodeId,
|
|
1117
|
+
descendantNames: leaves(node).map(leaf => leaf.data.name),
|
|
1118
|
+
}
|
|
1119
|
+
: undefined;
|
|
1104
1120
|
},
|
|
1105
1121
|
/**
|
|
1106
1122
|
* #action
|
|
@@ -1196,21 +1212,36 @@ function stateModelFactory() {
|
|
|
1196
1212
|
* #action
|
|
1197
1213
|
*/
|
|
1198
1214
|
doScrollY(deltaY) {
|
|
1199
|
-
|
|
1215
|
+
this.setScrollY(self.scrollY + deltaY);
|
|
1216
|
+
},
|
|
1217
|
+
/**
|
|
1218
|
+
* #action
|
|
1219
|
+
* set scroll Y-offset (px), clamped to keep the alignment in view
|
|
1220
|
+
*/
|
|
1221
|
+
setScrollY(n) {
|
|
1222
|
+
self.scrollY = clamp(n, self.maxScrollY, 0);
|
|
1200
1223
|
},
|
|
1201
1224
|
/**
|
|
1202
1225
|
* #action
|
|
1203
|
-
* Set
|
|
1204
|
-
*
|
|
1205
|
-
* GFF, user
|
|
1226
|
+
* Set the overlay annotations and reveal the overlay in a single step (an
|
|
1227
|
+
* empty list clears both). Every source funnels through here after its
|
|
1228
|
+
* own adapter has flattened it: InterProScan, GFF, user uploads, NCBI CDD.
|
|
1229
|
+
*/
|
|
1230
|
+
setAnnotations(annotations) {
|
|
1231
|
+
self.annotations = annotations;
|
|
1232
|
+
self.setShowDomains(annotations.length > 0);
|
|
1233
|
+
},
|
|
1234
|
+
/**
|
|
1235
|
+
* #action
|
|
1236
|
+
* set the overlay from raw InterProScan results keyed by row name. Kept
|
|
1237
|
+
* for downstream plugins that hold the EBI wire format; new code should
|
|
1238
|
+
* adapt to Annotation[] and call setAnnotations.
|
|
1206
1239
|
*/
|
|
1207
1240
|
setDomains(data) {
|
|
1208
|
-
|
|
1209
|
-
self.setShowDomains(!!data);
|
|
1241
|
+
this.setAnnotations(data ? interProScanToAnnotations(data) : []);
|
|
1210
1242
|
},
|
|
1211
1243
|
applyGFFText(gffText) {
|
|
1212
|
-
|
|
1213
|
-
this.setDomains(gffToInterProResults(gffRecords));
|
|
1244
|
+
this.setAnnotations(gffToAnnotations(parseGFF(gffText)));
|
|
1214
1245
|
},
|
|
1215
1246
|
/**
|
|
1216
1247
|
* #action
|
|
@@ -1228,12 +1259,9 @@ function stateModelFactory() {
|
|
|
1228
1259
|
* #action
|
|
1229
1260
|
*/
|
|
1230
1261
|
toggleTrack(id) {
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
else {
|
|
1235
|
-
self.turnedOffTracks.set(id, true);
|
|
1236
|
-
}
|
|
1262
|
+
// the stored value is "is off", so the current shown state is exactly
|
|
1263
|
+
// what the flipped entry should hold
|
|
1264
|
+
self.turnedOffTracks.set(id, !trackIsOff(self.turnedOffTracks, id));
|
|
1237
1265
|
},
|
|
1238
1266
|
/**
|
|
1239
1267
|
* #action
|
|
@@ -1247,17 +1275,34 @@ function stateModelFactory() {
|
|
|
1247
1275
|
* #getter
|
|
1248
1276
|
*/
|
|
1249
1277
|
get labelWidthMap() {
|
|
1250
|
-
const {
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1278
|
+
const { showTreeText, leaves, treeMetadata } = self;
|
|
1279
|
+
// gated on the same condition the renderer draws labels under, so the
|
|
1280
|
+
// gutter labelsWidth reserves and the labels actually drawn cannot
|
|
1281
|
+
// disagree -- and so turning labels off hands their space to the tree.
|
|
1282
|
+
// Measured at a fixed reference size and scaled by labelWidthScale:
|
|
1283
|
+
// re-measuring every leaf on every vertical-zoom frame cost ~200ms on a
|
|
1284
|
+
// 50k-leaf tree
|
|
1285
|
+
return showTreeText
|
|
1286
|
+
? new Map(leaves.map(node => {
|
|
1254
1287
|
const { name } = node.data;
|
|
1255
1288
|
// `||`, matching renderTreeLabels: an empty genome falls back
|
|
1256
1289
|
// to the row name, and measuring '' would size the gutter (and
|
|
1257
1290
|
// the label's click target) to nothing
|
|
1258
1291
|
const displayName = treeMetadata[name]?.genome || name;
|
|
1259
|
-
return [
|
|
1260
|
-
|
|
1292
|
+
return [
|
|
1293
|
+
name,
|
|
1294
|
+
measureTextCanvas(displayName, labelReferenceFontSize),
|
|
1295
|
+
];
|
|
1296
|
+
}))
|
|
1297
|
+
: new Map();
|
|
1298
|
+
},
|
|
1299
|
+
/**
|
|
1300
|
+
* #getter
|
|
1301
|
+
* factor turning a labelWidthMap entry into its width at the current
|
|
1302
|
+
* font size
|
|
1303
|
+
*/
|
|
1304
|
+
get labelWidthScale() {
|
|
1305
|
+
return self.fontSize / labelReferenceFontSize;
|
|
1261
1306
|
},
|
|
1262
1307
|
get labelsWidth() {
|
|
1263
1308
|
// a loop, not Math.max(...widths.values()): spreading a map of every
|
|
@@ -1271,7 +1316,7 @@ function stateModelFactory() {
|
|
|
1271
1316
|
max = width;
|
|
1272
1317
|
}
|
|
1273
1318
|
}
|
|
1274
|
-
return max;
|
|
1319
|
+
return max * this.labelWidthScale;
|
|
1275
1320
|
},
|
|
1276
1321
|
/**
|
|
1277
1322
|
* #getter
|
|
@@ -1296,10 +1341,11 @@ function stateModelFactory() {
|
|
|
1296
1341
|
.map(t => ({
|
|
1297
1342
|
model: {
|
|
1298
1343
|
...t,
|
|
1344
|
+
kind: 'text',
|
|
1299
1345
|
data: hideGapsEffective ? skipBlanks(blanks, t.data) : t.data,
|
|
1300
1346
|
height: rowHeight,
|
|
1301
1347
|
},
|
|
1302
|
-
ReactComponent:
|
|
1348
|
+
ReactComponent: TrackBlocks,
|
|
1303
1349
|
}));
|
|
1304
1350
|
},
|
|
1305
1351
|
/**
|
|
@@ -1307,34 +1353,41 @@ function stateModelFactory() {
|
|
|
1307
1353
|
*/
|
|
1308
1354
|
get tracks() {
|
|
1309
1355
|
const conservationTrack = {
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
},
|
|
1316
|
-
ReactComponent: ConservationTrack,
|
|
1356
|
+
id: 'conservation',
|
|
1357
|
+
name: 'Conservation',
|
|
1358
|
+
kind: 'bar',
|
|
1359
|
+
height: self.conservationTrackHeight,
|
|
1360
|
+
barColor: 'gray',
|
|
1317
1361
|
};
|
|
1318
1362
|
const propertyConservationTrack = {
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1363
|
+
id: 'property-conservation',
|
|
1364
|
+
name: 'Property conservation',
|
|
1365
|
+
kind: 'bar',
|
|
1366
|
+
height: self.conservationTrackHeight,
|
|
1367
|
+
barColor: '#6a51a3',
|
|
1368
|
+
};
|
|
1369
|
+
const sequenceLogoTrack = {
|
|
1370
|
+
id: 'sequence-logo',
|
|
1371
|
+
name: 'Sequence logo',
|
|
1372
|
+
kind: 'logo',
|
|
1373
|
+
height: self.sequenceLogoTrackHeight,
|
|
1326
1374
|
};
|
|
1327
1375
|
return [
|
|
1328
1376
|
...this.adapterTrackModels,
|
|
1329
|
-
|
|
1330
|
-
|
|
1377
|
+
...[
|
|
1378
|
+
conservationTrack,
|
|
1379
|
+
...(self.sequenceType === 'amino'
|
|
1380
|
+
? [propertyConservationTrack]
|
|
1381
|
+
: []),
|
|
1382
|
+
sequenceLogoTrack,
|
|
1383
|
+
].map(model => ({ model, ReactComponent: TrackBlocks })),
|
|
1331
1384
|
];
|
|
1332
1385
|
},
|
|
1333
1386
|
/**
|
|
1334
1387
|
* #getter
|
|
1335
1388
|
*/
|
|
1336
1389
|
get turnedOnTracks() {
|
|
1337
|
-
return this.tracks.filter(f => !self.turnedOffTracks
|
|
1390
|
+
return this.tracks.filter(f => !trackIsOff(self.turnedOffTracks, f.model.id));
|
|
1338
1391
|
},
|
|
1339
1392
|
/**
|
|
1340
1393
|
* #getter
|
|
@@ -1454,35 +1507,27 @@ function stateModelFactory() {
|
|
|
1454
1507
|
get totalTrackAreaHeight() {
|
|
1455
1508
|
return sum(self.turnedOnTracks.map(r => r.model.height));
|
|
1456
1509
|
},
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
start,
|
|
1473
|
-
end,
|
|
1474
|
-
strand,
|
|
1475
|
-
}))
|
|
1476
|
-
: [];
|
|
1477
|
-
}))
|
|
1478
|
-
.toSorted((a, b) => len(b) - len(a))
|
|
1479
|
-
: [];
|
|
1510
|
+
/**
|
|
1511
|
+
* one representative annotation per accession, which is what the legend,
|
|
1512
|
+
* the filter dialog and the palettes key off
|
|
1513
|
+
*/
|
|
1514
|
+
get annotationTypes() {
|
|
1515
|
+
// first occurrence wins. The representative supplies only the name,
|
|
1516
|
+
// description and -- for ordinal segments -- the start that orders
|
|
1517
|
+
// them, and those agree across an accession's instances
|
|
1518
|
+
const types = new Map();
|
|
1519
|
+
for (const annot of self.annotations) {
|
|
1520
|
+
if (!types.has(annot.accession)) {
|
|
1521
|
+
types.set(annot.accession, annot);
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
return types;
|
|
1480
1525
|
},
|
|
1481
|
-
get
|
|
1482
|
-
return
|
|
1526
|
+
get filteredAnnotations() {
|
|
1527
|
+
return self.annotations.filter(r => self.featureFilters.get(r.accession));
|
|
1483
1528
|
},
|
|
1484
|
-
get
|
|
1485
|
-
return groupBy(this.
|
|
1529
|
+
get annotationsByRow() {
|
|
1530
|
+
return groupBy(this.filteredAnnotations, r => r.id);
|
|
1486
1531
|
},
|
|
1487
1532
|
}))
|
|
1488
1533
|
.views(self => ({
|
|
@@ -1500,6 +1545,15 @@ function stateModelFactory() {
|
|
|
1500
1545
|
get verticalScrollbarWidth() {
|
|
1501
1546
|
return self.showVerticalScrollbar ? 20 : 0;
|
|
1502
1547
|
},
|
|
1548
|
+
/**
|
|
1549
|
+
* #getter
|
|
1550
|
+
* width of the alignment canvas itself: the msa area less the vertical
|
|
1551
|
+
* scrollbar sitting in it. Not usable from showHorizontalScrollbar, which
|
|
1552
|
+
* feeds msaAreaHeight -> showVerticalScrollbar and would close a cycle
|
|
1553
|
+
*/
|
|
1554
|
+
get msaCanvasWidth() {
|
|
1555
|
+
return self.msaAreaWidth - this.verticalScrollbarWidth;
|
|
1556
|
+
},
|
|
1503
1557
|
/**
|
|
1504
1558
|
* #getter
|
|
1505
1559
|
* ordinal segment types (exons etc.), ordered by sequence position so
|
|
@@ -1507,7 +1561,7 @@ function stateModelFactory() {
|
|
|
1507
1561
|
* labeled by number rather than each getting a distinct hue + legend row
|
|
1508
1562
|
*/
|
|
1509
1563
|
get segmentDomainTypes() {
|
|
1510
|
-
return [...self.
|
|
1564
|
+
return [...self.annotationTypes.values()]
|
|
1511
1565
|
.filter(d => segmentFeatureTypes.has(d.featureType ?? ''))
|
|
1512
1566
|
.toSorted((a, b) => a.start - b.start);
|
|
1513
1567
|
},
|
|
@@ -1517,7 +1571,7 @@ function stateModelFactory() {
|
|
|
1517
1571
|
* their own color and a legend entry
|
|
1518
1572
|
*/
|
|
1519
1573
|
get categoricalDomainTypes() {
|
|
1520
|
-
return [...self.
|
|
1574
|
+
return [...self.annotationTypes.values()].filter(d => !segmentFeatureTypes.has(d.featureType ?? ''));
|
|
1521
1575
|
},
|
|
1522
1576
|
get fillPalette() {
|
|
1523
1577
|
const segments = Object.fromEntries(this.segmentDomainTypes.map((d, i) => [
|
|
@@ -1558,29 +1612,39 @@ function stateModelFactory() {
|
|
|
1558
1612
|
},
|
|
1559
1613
|
/**
|
|
1560
1614
|
* #getter
|
|
1561
|
-
* every filtered-on
|
|
1562
|
-
*
|
|
1563
|
-
*
|
|
1564
|
-
*
|
|
1565
|
-
*
|
|
1566
|
-
*
|
|
1615
|
+
* every filtered-on annotation resolved to the visible column span it is
|
|
1616
|
+
* drawn across, keyed by row name. Each row is ordered longest-first so a
|
|
1617
|
+
* short domain nested inside a long one draws on top of it rather than
|
|
1618
|
+
* under it. Resolving these once here rather than inside each canvas
|
|
1619
|
+
* block removes a per-feature, per-block sequence position conversion
|
|
1620
|
+
* from every redraw, and gives the letter renderer the band colors it
|
|
1621
|
+
* needs to keep residues readable on top of the boxes.
|
|
1567
1622
|
*/
|
|
1568
1623
|
get domainBands() {
|
|
1624
|
+
const { blanks } = self;
|
|
1569
1625
|
const bands = new Map();
|
|
1570
|
-
for (const [name, annotations] of Object.entries(self.
|
|
1626
|
+
for (const [name, annotations] of Object.entries(self.annotationsByRow)) {
|
|
1571
1627
|
const rowBands = annotations
|
|
1572
|
-
.
|
|
1573
|
-
|
|
1574
|
-
//
|
|
1575
|
-
//
|
|
1576
|
-
//
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1628
|
+
.toSorted((a, b) => len(b) - len(a))
|
|
1629
|
+
.map(annotation => {
|
|
1630
|
+
// annotation positions are 1-based and inclusive. Both ends count
|
|
1631
|
+
// the visible columns in front of a global column, so endCol is
|
|
1632
|
+
// the exclusive column after the last residue's own column --
|
|
1633
|
+
// the band stops there rather than stretching across a following
|
|
1634
|
+
// gap run -- and a residue whose column is itself hidden
|
|
1635
|
+
// collapses onto the neighbouring boundary instead of dropping
|
|
1636
|
+
// the band. A band whose every column is hidden spans nothing
|
|
1637
|
+
// and is left out.
|
|
1638
|
+
const startCol = visibleColsBefore(blanks, self.seqPosToGlobalCol(name, annotation.start - 1));
|
|
1639
|
+
const endCol = visibleColsBefore(blanks, self.seqPosToGlobalCol(name, annotation.end - 1) + 1);
|
|
1640
|
+
return endCol > startCol
|
|
1641
|
+
? { annotation, startCol, endCol }
|
|
1581
1642
|
: undefined;
|
|
1582
1643
|
})
|
|
1583
|
-
.filter(notEmpty)
|
|
1644
|
+
.filter(notEmpty)
|
|
1645
|
+
// numbered after the drop, so a band that resolved to nothing does
|
|
1646
|
+
// not leave an empty sub-row behind it
|
|
1647
|
+
.map((band, stackIndex) => ({ ...band, stackIndex }));
|
|
1584
1648
|
if (rowBands.length > 0) {
|
|
1585
1649
|
bands.set(name, rowBands);
|
|
1586
1650
|
}
|
|
@@ -1669,8 +1733,11 @@ function stateModelFactory() {
|
|
|
1669
1733
|
return undefined;
|
|
1670
1734
|
}
|
|
1671
1735
|
const { colStats } = self;
|
|
1736
|
+
if (mouseCol >= colStats.numColumns) {
|
|
1737
|
+
return undefined;
|
|
1738
|
+
}
|
|
1672
1739
|
const total = colStats.total(mouseCol);
|
|
1673
|
-
if (
|
|
1740
|
+
if (!total) {
|
|
1674
1741
|
return undefined;
|
|
1675
1742
|
}
|
|
1676
1743
|
const gaps = colStats.gapCount(mouseCol);
|
|
@@ -1714,6 +1781,12 @@ function stateModelFactory() {
|
|
|
1714
1781
|
setConservationTrackHeight(arg) {
|
|
1715
1782
|
self.conservationTrackHeight = arg;
|
|
1716
1783
|
},
|
|
1784
|
+
/**
|
|
1785
|
+
* #action
|
|
1786
|
+
*/
|
|
1787
|
+
setSequenceLogoTrackHeight(arg) {
|
|
1788
|
+
self.sequenceLogoTrackHeight = arg;
|
|
1789
|
+
},
|
|
1717
1790
|
/**
|
|
1718
1791
|
* #action
|
|
1719
1792
|
*/
|
|
@@ -1730,7 +1803,7 @@ function stateModelFactory() {
|
|
|
1730
1803
|
self.setTreeFilehandle(undefined);
|
|
1731
1804
|
self.setMSAFilehandle(undefined);
|
|
1732
1805
|
self.setGFFFilehandle(undefined);
|
|
1733
|
-
self.
|
|
1806
|
+
self.setAnnotations([]);
|
|
1734
1807
|
},
|
|
1735
1808
|
/**
|
|
1736
1809
|
* #action
|
|
@@ -1770,7 +1843,10 @@ function stateModelFactory() {
|
|
|
1770
1843
|
*/
|
|
1771
1844
|
fitHorizontally() {
|
|
1772
1845
|
if (self.numColumns > 0) {
|
|
1773
|
-
|
|
1846
|
+
// fitting to msaAreaWidth instead left the last ~20px of columns off
|
|
1847
|
+
// the right edge -- and short of the width that shows a minimap, so
|
|
1848
|
+
// nothing on screen said they were there
|
|
1849
|
+
self.colWidth = clamp(self.msaCanvasWidth / self.numColumns, minColWidth, maxCellSize);
|
|
1774
1850
|
}
|
|
1775
1851
|
self.scrollX = 0;
|
|
1776
1852
|
},
|
|
@@ -1782,7 +1858,7 @@ function stateModelFactory() {
|
|
|
1782
1858
|
self.setHighlightedColumns(self.highlightColumns);
|
|
1783
1859
|
}
|
|
1784
1860
|
addDisposer(self, autorun(() => {
|
|
1785
|
-
for (const key of self.
|
|
1861
|
+
for (const key of self.annotationTypes.keys()) {
|
|
1786
1862
|
this.initFilter(key);
|
|
1787
1863
|
}
|
|
1788
1864
|
}));
|
|
@@ -1897,7 +1973,7 @@ function stateModelFactory() {
|
|
|
1897
1973
|
}
|
|
1898
1974
|
}
|
|
1899
1975
|
}));
|
|
1900
|
-
// gffFilehandle carries
|
|
1976
|
+
// gffFilehandle carries overlay annotations
|
|
1901
1977
|
loadOnFilehandleChange({
|
|
1902
1978
|
getFilehandle: () => self.gffFilehandle,
|
|
1903
1979
|
onLoad: text => {
|