react-msaview 3.1.7 → 3.1.8
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 +32 -32
- package/dist/components/Checkbox2.d.ts +7 -0
- package/dist/components/Checkbox2.js +10 -0
- package/dist/components/Checkbox2.js.map +1 -0
- package/dist/components/Loading.js +12 -4
- package/dist/components/Loading.js.map +1 -1
- package/dist/components/MSAView.js +5 -8
- package/dist/components/MSAView.js.map +1 -1
- package/dist/components/SequenceTextArea.d.ts +4 -0
- package/dist/components/SequenceTextArea.js +38 -0
- package/dist/components/SequenceTextArea.js.map +1 -0
- package/dist/components/Track.js +9 -8
- package/dist/components/Track.js.map +1 -1
- package/dist/components/dialogs/AddTrackDialog.js +0 -1
- package/dist/components/dialogs/AddTrackDialog.js.map +1 -1
- package/dist/components/{ExportSVGDialog.d.ts → dialogs/ExportSVGDialog.d.ts} +1 -1
- package/dist/components/{ExportSVGDialog.js → dialogs/ExportSVGDialog.js} +3 -4
- package/dist/components/dialogs/ExportSVGDialog.js.map +1 -0
- package/dist/components/dialogs/FeatureDialog.d.ts +7 -0
- package/dist/components/dialogs/FeatureDialog.js +52 -0
- package/dist/components/dialogs/FeatureDialog.js.map +1 -0
- package/dist/components/dialogs/InterProScanDialog.d.ts +7 -0
- package/dist/components/dialogs/InterProScanDialog.js +163 -0
- package/dist/components/dialogs/InterProScanDialog.js.map +1 -0
- package/dist/components/dialogs/MetadataDialog.js +6 -3
- package/dist/components/dialogs/MetadataDialog.js.map +1 -1
- package/dist/components/dialogs/SettingsDialog.js +6 -11
- package/dist/components/dialogs/SettingsDialog.js.map +1 -1
- package/dist/components/{Header.d.ts → header/Header.d.ts} +1 -1
- package/dist/components/header/Header.js +30 -0
- package/dist/components/header/Header.js.map +1 -0
- package/dist/components/{HeaderInfoArea.d.ts → header/HeaderInfoArea.d.ts} +2 -2
- package/dist/components/header/HeaderInfoArea.js +20 -0
- package/dist/components/header/HeaderInfoArea.js.map +1 -0
- package/dist/components/header/HeaderMenu.d.ts +6 -0
- package/dist/components/header/HeaderMenu.js +40 -0
- package/dist/components/header/HeaderMenu.js.map +1 -0
- package/dist/components/header/HeaderMenuExtra.d.ts +6 -0
- package/dist/components/header/HeaderMenuExtra.js +92 -0
- package/dist/components/header/HeaderMenuExtra.js.map +1 -0
- package/dist/components/header/HeaderStatusArea.d.ts +6 -0
- package/dist/components/header/HeaderStatusArea.js +20 -0
- package/dist/components/header/HeaderStatusArea.js.map +1 -0
- package/dist/components/{MultiAlignmentSelector.d.ts → header/MultiAlignmentSelector.d.ts} +1 -1
- package/dist/components/header/MultiAlignmentSelector.js.map +1 -0
- package/dist/components/{ZoomControls.d.ts → header/ZoomControls.d.ts} +1 -1
- package/dist/components/header/ZoomControls.js +15 -0
- package/dist/components/header/ZoomControls.js.map +1 -0
- package/dist/components/{ImportForm/index.js → import/ImportForm.js} +1 -1
- package/dist/components/import/ImportForm.js.map +1 -0
- package/dist/components/{ImportForm → import}/ImportFormExamples.js +6 -2
- package/dist/components/import/ImportFormExamples.js.map +1 -0
- package/dist/components/import/data/seq2.js.map +1 -0
- package/dist/components/import/util.js +10 -0
- package/dist/components/import/util.js.map +1 -0
- package/dist/components/{Minimap.d.ts → minimap/Minimap.d.ts} +1 -1
- package/dist/components/minimap/Minimap.js.map +1 -0
- package/dist/components/{MinimapSVG.d.ts → minimap/MinimapSVG.d.ts} +1 -1
- package/dist/components/minimap/MinimapSVG.js.map +1 -0
- package/dist/components/msa/Loading.js.map +1 -0
- package/dist/components/{MSAPanel → msa}/MSACanvas.js +3 -2
- package/dist/components/msa/MSACanvas.js.map +1 -0
- package/dist/components/{MSAPanel/MSABlock.js → msa/MSACanvasBlock.js} +21 -9
- package/dist/components/msa/MSACanvasBlock.js.map +1 -0
- package/dist/components/msa/MSAMouseoverCanvas.js.map +1 -0
- package/dist/components/msa/MSAPanel.d.ts +6 -0
- package/dist/components/{MSAPanel/index.js → msa/MSAPanel.js} +6 -3
- package/dist/components/msa/MSAPanel.js.map +1 -0
- package/dist/components/msa/renderBoxFeatureCanvasBlock.d.ts +9 -0
- package/dist/components/msa/renderBoxFeatureCanvasBlock.js +44 -0
- package/dist/components/msa/renderBoxFeatureCanvasBlock.js.map +1 -0
- package/dist/components/{MSAPanel → msa}/renderMSABlock.js +24 -20
- package/dist/components/msa/renderMSABlock.js.map +1 -0
- package/dist/components/msa/renderMSAMouseover.js.map +1 -0
- package/dist/components/tree/TreeBranchMenu.js.map +1 -0
- package/dist/components/{TreePanel → tree}/TreeCanvas.js +1 -1
- package/dist/components/tree/TreeCanvas.js.map +1 -0
- package/dist/components/{TreePanel → tree}/TreeCanvasBlock.js +1 -1
- package/dist/components/tree/TreeCanvasBlock.js.map +1 -0
- package/dist/components/{TreePanel → tree}/TreeNodeMenu.js +2 -33
- package/dist/components/tree/TreeNodeMenu.js.map +1 -0
- package/dist/components/{TreePanel/index.js → tree/TreePanel.js} +2 -2
- package/dist/components/tree/TreePanel.js.map +1 -0
- package/dist/components/tree/TreeRuler.js.map +1 -0
- package/dist/components/{TreePanel → tree}/dialogs/TreeNodeInfoDialog.js +6 -2
- package/dist/components/tree/dialogs/TreeNodeInfoDialog.js.map +1 -0
- package/dist/components/{TreePanel → tree}/renderTreeCanvas.js +23 -37
- package/dist/components/tree/renderTreeCanvas.js.map +1 -0
- package/dist/fetchUtils.d.ts +5 -0
- package/dist/fetchUtils.js +23 -0
- package/dist/fetchUtils.js.map +1 -0
- package/dist/ggplotPalettes.d.ts +3 -0
- package/dist/ggplotPalettes.js +24 -0
- package/dist/ggplotPalettes.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/launchInterProScan.d.ts +32 -0
- package/dist/launchInterProScan.js +47 -0
- package/dist/launchInterProScan.js.map +1 -0
- package/dist/model/DataModel.js.map +1 -0
- package/dist/model/DialogQueue.js.map +1 -0
- package/dist/model/msaModel.d.ts +14 -0
- package/dist/model/msaModel.js +36 -0
- package/dist/model/msaModel.js.map +1 -0
- package/dist/model/treeModel.d.ts +46 -0
- package/dist/model/treeModel.js +105 -0
- package/dist/model/treeModel.js.map +1 -0
- package/dist/model.d.ts +261 -273
- package/dist/model.js +1043 -1029
- package/dist/model.js.map +1 -1
- package/dist/parseGFF.d.ts +10 -0
- package/dist/parseGFF.js +29 -0
- package/dist/parseGFF.js.map +1 -0
- package/dist/renderToSvg.js +23 -9
- package/dist/renderToSvg.js.map +1 -1
- package/dist/reparseTree.d.ts +2 -0
- package/dist/reparseTree.js +13 -0
- package/dist/reparseTree.js.map +1 -0
- package/dist/util.d.ts +4 -10
- package/dist/util.js +3 -28
- package/dist/util.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +12 -2
- package/src/components/Checkbox2.tsx +34 -0
- package/src/components/Loading.tsx +27 -11
- package/src/components/MSAView.tsx +6 -10
- package/src/components/SequenceTextArea.tsx +63 -0
- package/src/components/Track.tsx +8 -13
- package/src/components/dialogs/AddTrackDialog.tsx +0 -1
- package/src/components/{ExportSVGDialog.tsx → dialogs/ExportSVGDialog.tsx} +9 -16
- package/src/components/dialogs/FeatureDialog.tsx +109 -0
- package/src/components/dialogs/InterProScanDialog.tsx +230 -0
- package/src/components/dialogs/MetadataDialog.tsx +9 -2
- package/src/components/dialogs/SettingsDialog.tsx +10 -30
- package/src/components/header/Header.tsx +44 -0
- package/src/components/header/HeaderInfoArea.tsx +27 -0
- package/src/components/header/HeaderMenu.tsx +54 -0
- package/src/components/header/HeaderMenuExtra.tsx +108 -0
- package/src/components/header/HeaderStatusArea.tsx +31 -0
- package/src/components/{MultiAlignmentSelector.tsx → header/MultiAlignmentSelector.tsx} +1 -1
- package/src/components/header/ZoomControls.tsx +28 -0
- package/src/components/{ImportForm → import}/ImportFormExamples.tsx +12 -1
- package/src/components/{ImportForm → import}/util.ts +5 -10
- package/src/components/{Minimap.tsx → minimap/Minimap.tsx} +1 -1
- package/src/components/{MinimapSVG.tsx → minimap/MinimapSVG.tsx} +1 -1
- package/src/components/{MSAPanel → msa}/MSACanvas.tsx +3 -2
- package/src/components/{MSAPanel/MSABlock.tsx → msa/MSACanvasBlock.tsx} +25 -12
- package/src/components/{MSAPanel/index.tsx → msa/MSAPanel.tsx} +8 -2
- package/src/components/msa/renderBoxFeatureCanvasBlock.ts +88 -0
- package/src/components/{MSAPanel → msa}/renderMSABlock.ts +26 -20
- package/src/components/{TreePanel → tree}/TreeCanvas.tsx +1 -1
- package/src/components/{TreePanel → tree}/TreeCanvasBlock.tsx +1 -1
- package/src/components/{TreePanel → tree}/TreeNodeMenu.tsx +1 -53
- package/src/components/{TreePanel/index.tsx → tree/TreePanel.tsx} +1 -1
- package/src/components/{TreePanel → tree}/dialogs/TreeNodeInfoDialog.tsx +9 -2
- package/src/components/{TreePanel → tree}/renderTreeCanvas.ts +25 -41
- package/src/fetchUtils.ts +30 -0
- package/src/ggplotPalettes.ts +25 -0
- package/src/index.ts +1 -1
- package/src/launchInterProScan.ts +98 -0
- package/src/model/msaModel.ts +39 -0
- package/src/model/treeModel.ts +116 -0
- package/src/model.ts +1124 -1126
- package/src/parseGFF.ts +32 -0
- package/src/renderToSvg.tsx +27 -8
- package/src/reparseTree.ts +16 -0
- package/src/util.ts +4 -33
- package/src/version.ts +1 -1
- package/dist/DataModel.js.map +0 -1
- package/dist/DialogQueue.js.map +0 -1
- package/dist/SelectedStructuresMixin.d.ts +0 -46
- package/dist/SelectedStructuresMixin.js +0 -52
- package/dist/SelectedStructuresMixin.js.map +0 -1
- package/dist/StructureModel.d.ts +0 -9
- package/dist/StructureModel.js +0 -11
- package/dist/StructureModel.js.map +0 -1
- package/dist/UniprotTrack.d.ts +0 -27
- package/dist/UniprotTrack.js +0 -53
- package/dist/UniprotTrack.js.map +0 -1
- package/dist/components/BoxTrack.d.ts +0 -7
- package/dist/components/BoxTrack.js +0 -15
- package/dist/components/BoxTrack.js.map +0 -1
- package/dist/components/BoxTrackBlock.d.ts +0 -8
- package/dist/components/BoxTrackBlock.js +0 -136
- package/dist/components/BoxTrackBlock.js.map +0 -1
- package/dist/components/ExportSVGDialog.js.map +0 -1
- package/dist/components/Header.js +0 -62
- package/dist/components/Header.js.map +0 -1
- package/dist/components/HeaderInfoArea.js +0 -12
- package/dist/components/HeaderInfoArea.js.map +0 -1
- package/dist/components/ImportForm/ImportFormExamples.js.map +0 -1
- package/dist/components/ImportForm/data/seq2.js.map +0 -1
- package/dist/components/ImportForm/index.js.map +0 -1
- package/dist/components/ImportForm/util.js +0 -16
- package/dist/components/ImportForm/util.js.map +0 -1
- package/dist/components/MSAPanel/Loading.js.map +0 -1
- package/dist/components/MSAPanel/MSABlock.js.map +0 -1
- package/dist/components/MSAPanel/MSACanvas.js.map +0 -1
- package/dist/components/MSAPanel/MSAMouseoverCanvas.js.map +0 -1
- package/dist/components/MSAPanel/index.d.ts +0 -5
- package/dist/components/MSAPanel/index.js.map +0 -1
- package/dist/components/MSAPanel/renderMSABlock.js.map +0 -1
- package/dist/components/MSAPanel/renderMSAMouseover.js.map +0 -1
- package/dist/components/Minimap.js.map +0 -1
- package/dist/components/MinimapSVG.js.map +0 -1
- package/dist/components/MultiAlignmentSelector.js.map +0 -1
- package/dist/components/TreePanel/TreeBranchMenu.js.map +0 -1
- package/dist/components/TreePanel/TreeCanvas.js.map +0 -1
- package/dist/components/TreePanel/TreeCanvasBlock.js.map +0 -1
- package/dist/components/TreePanel/TreeNodeMenu.js.map +0 -1
- package/dist/components/TreePanel/TreeRuler.js.map +0 -1
- package/dist/components/TreePanel/dialogs/TreeNodeInfoDialog.js.map +0 -1
- package/dist/components/TreePanel/index.js.map +0 -1
- package/dist/components/TreePanel/renderTreeCanvas.js.map +0 -1
- package/dist/components/VerticalGuide.d.ts +0 -7
- package/dist/components/VerticalGuide.js +0 -30
- package/dist/components/VerticalGuide.js.map +0 -1
- package/dist/components/ZoomControls.js +0 -59
- package/dist/components/ZoomControls.js.map +0 -1
- package/src/SelectedStructuresMixin.ts +0 -59
- package/src/StructureModel.ts +0 -11
- package/src/UniprotTrack.ts +0 -59
- package/src/components/BoxTrack.tsx +0 -33
- package/src/components/BoxTrackBlock.tsx +0 -200
- package/src/components/Header.tsx +0 -99
- package/src/components/HeaderInfoArea.tsx +0 -21
- package/src/components/VerticalGuide.tsx +0 -50
- package/src/components/ZoomControls.tsx +0 -86
- package/dist/components/{MultiAlignmentSelector.js → header/MultiAlignmentSelector.js} +0 -0
- package/dist/components/{ImportForm/index.d.ts → import/ImportForm.d.ts} +0 -0
- package/dist/components/{ImportForm → import}/ImportFormExamples.d.ts +0 -0
- package/dist/components/{ImportForm → import}/data/seq2.d.ts +0 -0
- package/dist/components/{ImportForm → import}/data/seq2.js +0 -0
- package/dist/components/{ImportForm → import}/util.d.ts +0 -0
- package/dist/components/{Minimap.js → minimap/Minimap.js} +0 -0
- package/dist/components/{MinimapSVG.js → minimap/MinimapSVG.js} +0 -0
- package/dist/components/{MSAPanel → msa}/Loading.d.ts +0 -0
- package/dist/components/{MSAPanel → msa}/Loading.js +0 -0
- package/dist/components/{MSAPanel → msa}/MSACanvas.d.ts +0 -0
- package/dist/components/{MSAPanel/MSABlock.d.ts → msa/MSACanvasBlock.d.ts} +0 -0
- package/dist/components/{MSAPanel → msa}/MSAMouseoverCanvas.d.ts +0 -0
- package/dist/components/{MSAPanel → msa}/MSAMouseoverCanvas.js +0 -0
- package/dist/components/{MSAPanel → msa}/renderMSABlock.d.ts +1 -1
- /package/dist/components/{MSAPanel → msa}/renderMSAMouseover.d.ts +0 -0
- /package/dist/components/{MSAPanel → msa}/renderMSAMouseover.js +0 -0
- /package/dist/components/{TreePanel → tree}/TreeBranchMenu.d.ts +0 -0
- /package/dist/components/{TreePanel → tree}/TreeBranchMenu.js +0 -0
- /package/dist/components/{TreePanel → tree}/TreeCanvas.d.ts +0 -0
- /package/dist/components/{TreePanel → tree}/TreeCanvasBlock.d.ts +0 -0
- /package/dist/components/{TreePanel → tree}/TreeNodeMenu.d.ts +0 -0
- /package/dist/components/{TreePanel/index.d.ts → tree/TreePanel.d.ts} +0 -0
- /package/dist/components/{TreePanel → tree}/TreeRuler.d.ts +0 -0
- /package/dist/components/{TreePanel → tree}/TreeRuler.js +0 -0
- /package/dist/components/{TreePanel → tree}/dialogs/TreeNodeInfoDialog.d.ts +0 -0
- /package/dist/components/{TreePanel → tree}/renderTreeCanvas.d.ts +0 -0
- /package/dist/{DataModel.d.ts → model/DataModel.d.ts} +0 -0
- /package/dist/{DataModel.js → model/DataModel.js} +0 -0
- /package/dist/{DialogQueue.d.ts → model/DialogQueue.d.ts} +0 -0
- /package/dist/{DialogQueue.js → model/DialogQueue.js} +0 -0
- /package/src/components/{ImportForm/index.tsx → import/ImportForm.tsx} +0 -0
- /package/src/components/{ImportForm → import}/data/seq2.ts +0 -0
- /package/src/components/{MSAPanel → msa}/Loading.tsx +0 -0
- /package/src/components/{MSAPanel → msa}/MSAMouseoverCanvas.tsx +0 -0
- /package/src/components/{MSAPanel → msa}/renderMSAMouseover.ts +0 -0
- /package/src/components/{TreePanel → tree}/TreeBranchMenu.tsx +0 -0
- /package/src/components/{TreePanel → tree}/TreeRuler.tsx +0 -0
- /package/src/{DataModel.ts → model/DataModel.ts} +0 -0
- /package/src/{DialogQueue.ts → model/DialogQueue.ts} +0 -0
package/src/model.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
-
import { autorun } from 'mobx'
|
|
2
|
+
import { autorun, transaction } from 'mobx'
|
|
3
3
|
import { Instance, cast, types, addDisposer } from 'mobx-state-tree'
|
|
4
4
|
import { hierarchy, cluster, HierarchyNode } from 'd3-hierarchy'
|
|
5
5
|
import { ascending } from 'd3-array'
|
|
@@ -11,8 +11,13 @@ import { Theme } from '@mui/material'
|
|
|
11
11
|
import { FileLocation, ElementId } from '@jbrowse/core/util/types/mst'
|
|
12
12
|
import { FileLocation as FileLocationType } from '@jbrowse/core/util/types'
|
|
13
13
|
import { openLocation } from '@jbrowse/core/util/io'
|
|
14
|
-
import {
|
|
15
|
-
|
|
14
|
+
import {
|
|
15
|
+
groupBy,
|
|
16
|
+
localStorageGetItem,
|
|
17
|
+
localStorageSetItem,
|
|
18
|
+
notEmpty,
|
|
19
|
+
sum,
|
|
20
|
+
} from '@jbrowse/core/util'
|
|
16
21
|
|
|
17
22
|
// locals
|
|
18
23
|
import {
|
|
@@ -24,14 +29,16 @@ import {
|
|
|
24
29
|
skipBlanks,
|
|
25
30
|
NodeWithIds,
|
|
26
31
|
NodeWithIdsAndLength,
|
|
32
|
+
len,
|
|
27
33
|
} from './util'
|
|
28
|
-
|
|
34
|
+
import { colord } from 'colord'
|
|
35
|
+
import { reparseTree } from './reparseTree'
|
|
29
36
|
import { blocksX, blocksY } from './calculateBlocks'
|
|
30
37
|
import { measureTextCanvas } from './measureTextCanvas'
|
|
38
|
+
import palettes from './ggplotPalettes'
|
|
31
39
|
|
|
32
40
|
// components
|
|
33
41
|
import TextTrack from './components/TextTrack'
|
|
34
|
-
import BoxTrack from './components/BoxTrack'
|
|
35
42
|
|
|
36
43
|
// parsers
|
|
37
44
|
import ClustalMSA from './parsers/ClustalMSA'
|
|
@@ -41,1257 +48,1248 @@ import parseNewick from './parseNewick'
|
|
|
41
48
|
import colorSchemes from './colorSchemes'
|
|
42
49
|
|
|
43
50
|
// models
|
|
44
|
-
import {
|
|
45
|
-
import {
|
|
46
|
-
import {
|
|
47
|
-
import {
|
|
51
|
+
import { DataModelF } from './model/DataModel'
|
|
52
|
+
import { DialogQueueSessionMixin } from './model/DialogQueue'
|
|
53
|
+
import { TreeF } from './model/treeModel'
|
|
54
|
+
import { MSAModelF } from './model/msaModel'
|
|
55
|
+
import {
|
|
56
|
+
InterProScanResults,
|
|
57
|
+
launchInterProScan,
|
|
58
|
+
loadInterProScanResults,
|
|
59
|
+
} from './launchInterProScan'
|
|
48
60
|
|
|
49
|
-
|
|
50
|
-
|
|
61
|
+
interface Accession {
|
|
62
|
+
accession: string
|
|
51
63
|
name: string
|
|
52
|
-
|
|
64
|
+
description: string
|
|
53
65
|
}
|
|
54
|
-
|
|
55
66
|
export interface BasicTrackModel {
|
|
56
67
|
id: string
|
|
57
68
|
name: string
|
|
58
69
|
associatedRowName?: string
|
|
59
70
|
height: number
|
|
60
71
|
}
|
|
61
|
-
export interface Structure {
|
|
62
|
-
pdb: string
|
|
63
|
-
startPos: number
|
|
64
|
-
endPos: number
|
|
65
|
-
}
|
|
66
72
|
|
|
67
73
|
export interface TextTrackModel extends BasicTrackModel {
|
|
68
74
|
customColorScheme?: Record<string, string>
|
|
69
75
|
data: string
|
|
70
76
|
}
|
|
71
77
|
|
|
72
|
-
export interface BoxTrackModel extends BasicTrackModel {
|
|
73
|
-
features: {
|
|
74
|
-
start: number
|
|
75
|
-
end: number
|
|
76
|
-
}[]
|
|
77
|
-
}
|
|
78
78
|
export interface ITextTrack {
|
|
79
79
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
80
80
|
ReactComponent: React.FC<any>
|
|
81
81
|
model: TextTrackModel
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
export
|
|
85
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
86
|
-
ReactComponent: React.FC<any>
|
|
87
|
-
model: BoxTrackModel
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export type BasicTrack = IBoxTrack | ITextTrack
|
|
84
|
+
export type BasicTrack = ITextTrack
|
|
91
85
|
|
|
92
86
|
/**
|
|
93
87
|
* #stateModel MsaView
|
|
94
88
|
* extends
|
|
95
|
-
* - BaseViewModel
|
|
96
89
|
* - DialogQueueSessionMixin
|
|
97
|
-
* -
|
|
90
|
+
* - MSAModel
|
|
91
|
+
* - Tree
|
|
98
92
|
*/
|
|
99
|
-
function
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
93
|
+
function stateModelFactory() {
|
|
94
|
+
return types
|
|
95
|
+
.compose(
|
|
96
|
+
DialogQueueSessionMixin(),
|
|
97
|
+
TreeF(),
|
|
98
|
+
MSAModelF(),
|
|
99
|
+
types.model('MsaView', {
|
|
100
|
+
/**
|
|
101
|
+
* #property
|
|
102
|
+
* id of view, randomly generated if not provided
|
|
103
|
+
*/
|
|
104
|
+
id: ElementId,
|
|
105
|
+
/**
|
|
106
|
+
* #property
|
|
107
|
+
*/
|
|
108
|
+
featureMode: false,
|
|
109
|
+
/**
|
|
110
|
+
* #property
|
|
111
|
+
*/
|
|
112
|
+
subFeatureRows: false,
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* #property
|
|
116
|
+
* hardcoded view type
|
|
117
|
+
*/
|
|
118
|
+
type: types.literal('MsaView'),
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* #property
|
|
122
|
+
* height of the div containing the view, px
|
|
123
|
+
*/
|
|
124
|
+
height: types.optional(types.number, 550),
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* #property
|
|
128
|
+
* height of each row, px
|
|
129
|
+
*/
|
|
130
|
+
rowHeight: 20,
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* #property
|
|
134
|
+
* scroll position, Y-offset, px
|
|
135
|
+
*/
|
|
136
|
+
scrollY: 0,
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* #property
|
|
140
|
+
* scroll position, X-offset, px
|
|
141
|
+
*/
|
|
142
|
+
scrollX: 0,
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* #property
|
|
146
|
+
* width of columns, px
|
|
147
|
+
*/
|
|
148
|
+
colWidth: 16,
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* #property
|
|
152
|
+
* filehandle object for the tree
|
|
153
|
+
*/
|
|
154
|
+
treeFilehandle: types.maybe(FileLocation),
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* #property
|
|
158
|
+
* filehandle object for the MSA (which could contain a tree e.g. with
|
|
159
|
+
* stockholm files)
|
|
160
|
+
*/
|
|
161
|
+
msaFilehandle: types.maybe(FileLocation),
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* #property
|
|
165
|
+
* filehandle object for tree metadata
|
|
166
|
+
*/
|
|
167
|
+
treeMetadataFilehandle: types.maybe(FileLocation),
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* #property
|
|
171
|
+
*
|
|
172
|
+
*/
|
|
173
|
+
currentAlignment: 0,
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* #property
|
|
177
|
+
* array of tree parent nodes that are 'collapsed'
|
|
178
|
+
*/
|
|
179
|
+
collapsed: types.array(types.string),
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* #property
|
|
183
|
+
* array of tree leaf nodes that are 'collapsed'
|
|
184
|
+
*/
|
|
185
|
+
collapsed2: types.array(types.string),
|
|
186
|
+
/**
|
|
187
|
+
* #property
|
|
188
|
+
* focus on particular subtree
|
|
189
|
+
*/
|
|
190
|
+
showOnly: types.maybe(types.string),
|
|
191
|
+
/**
|
|
192
|
+
* #property
|
|
193
|
+
* turned off tracks
|
|
194
|
+
*/
|
|
195
|
+
turnedOffTracks: types.map(types.boolean),
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* #property
|
|
199
|
+
* data from the loaded tree/msa/treeMetadata, generally loaded by
|
|
200
|
+
* autorun
|
|
201
|
+
*/
|
|
202
|
+
data: types.optional(DataModelF(), { tree: '', msa: '' }),
|
|
203
|
+
/**
|
|
204
|
+
* #property
|
|
205
|
+
*/
|
|
206
|
+
featureFilters: types.map(types.boolean),
|
|
207
|
+
}),
|
|
208
|
+
)
|
|
209
|
+
.volatile(() => ({
|
|
210
|
+
status: undefined as { msg: string; url?: string } | undefined,
|
|
211
|
+
/**
|
|
212
|
+
* #volatile
|
|
213
|
+
* high resolution scale factor, helps make canvas look better on hi-dpi
|
|
214
|
+
* screens
|
|
215
|
+
*/
|
|
216
|
+
highResScaleFactor: 1,
|
|
217
|
+
/**
|
|
218
|
+
* #volatile
|
|
219
|
+
*/
|
|
220
|
+
loadingMSA: false,
|
|
221
|
+
/**
|
|
222
|
+
* #volatile
|
|
223
|
+
*/
|
|
224
|
+
loadingTree: false,
|
|
225
|
+
/**
|
|
226
|
+
* #volatile
|
|
227
|
+
*/
|
|
228
|
+
width: 800,
|
|
229
|
+
/**
|
|
230
|
+
* #volatile
|
|
231
|
+
* resize handle width between tree and msa area, px
|
|
232
|
+
*/
|
|
233
|
+
resizeHandleWidth: 5,
|
|
115
234
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
235
|
+
/**
|
|
236
|
+
* #volatile
|
|
237
|
+
* size of blocks of content to be drawn, px
|
|
238
|
+
*/
|
|
239
|
+
blockSize: 1000,
|
|
121
240
|
|
|
122
|
-
const model = types
|
|
123
|
-
.compose(
|
|
124
|
-
BaseViewModel,
|
|
125
|
-
DialogQueueSessionMixin(),
|
|
126
|
-
SelectedStructuresMixin(),
|
|
127
|
-
types.model('MsaView', {
|
|
128
241
|
/**
|
|
129
|
-
* #
|
|
130
|
-
*
|
|
242
|
+
* #volatile
|
|
243
|
+
* the currently mouse-hovered row
|
|
131
244
|
*/
|
|
132
|
-
|
|
245
|
+
mouseRow: undefined as number | undefined,
|
|
133
246
|
|
|
134
247
|
/**
|
|
135
|
-
* #
|
|
136
|
-
*
|
|
248
|
+
* #volatile
|
|
249
|
+
* the currently mouse-hovered column
|
|
137
250
|
*/
|
|
138
|
-
|
|
251
|
+
mouseCol: undefined as number | undefined,
|
|
139
252
|
|
|
140
253
|
/**
|
|
141
|
-
* #
|
|
142
|
-
*
|
|
254
|
+
* #volatile
|
|
255
|
+
* the currently mouse-click row
|
|
143
256
|
*/
|
|
144
|
-
|
|
257
|
+
mouseClickRow: undefined as number | undefined,
|
|
145
258
|
|
|
146
259
|
/**
|
|
147
|
-
* #
|
|
148
|
-
*
|
|
260
|
+
* #volatile
|
|
261
|
+
* the currently mouse-click column
|
|
149
262
|
*/
|
|
150
|
-
|
|
263
|
+
mouseClickCol: undefined as number | undefined,
|
|
151
264
|
|
|
152
265
|
/**
|
|
153
|
-
* #
|
|
154
|
-
*
|
|
266
|
+
* #volatile
|
|
267
|
+
* a dummy variable that is incremented when ref changes so autorun for
|
|
268
|
+
* drawing canvas commands will run
|
|
155
269
|
*/
|
|
156
|
-
|
|
270
|
+
nref: 0,
|
|
157
271
|
|
|
158
272
|
/**
|
|
159
|
-
* #
|
|
160
|
-
* synchronization that matches treeWidth to treeAreaWidth
|
|
273
|
+
* #volatile
|
|
161
274
|
*/
|
|
162
|
-
|
|
275
|
+
minimapHeight: 56,
|
|
163
276
|
|
|
164
277
|
/**
|
|
165
|
-
* #
|
|
166
|
-
* height of each row, px
|
|
278
|
+
* #volatile
|
|
167
279
|
*/
|
|
168
|
-
|
|
280
|
+
marginLeft: 20,
|
|
169
281
|
|
|
170
282
|
/**
|
|
171
|
-
* #
|
|
172
|
-
* scroll position, Y-offset, px
|
|
283
|
+
* #volatile
|
|
173
284
|
*/
|
|
174
|
-
|
|
285
|
+
error: undefined as unknown,
|
|
175
286
|
|
|
176
287
|
/**
|
|
177
|
-
* #
|
|
178
|
-
* scroll position, X-offset, px
|
|
288
|
+
* #volatile
|
|
179
289
|
*/
|
|
180
|
-
|
|
290
|
+
annotPos: undefined as { left: number; right: number } | undefined,
|
|
181
291
|
|
|
182
292
|
/**
|
|
183
|
-
* #
|
|
184
|
-
*
|
|
293
|
+
* #volatile
|
|
294
|
+
*
|
|
295
|
+
*/
|
|
296
|
+
loadedInterProAnnotations: undefined as
|
|
297
|
+
| undefined
|
|
298
|
+
| Record<string, InterProScanResults>,
|
|
299
|
+
/**
|
|
300
|
+
* #volatile
|
|
301
|
+
*/
|
|
302
|
+
interProScanJobIds: JSON.parse(
|
|
303
|
+
localStorageGetItem('msaview-interproscanqueries') || '[]',
|
|
304
|
+
) as { jobId: string; date: number }[],
|
|
305
|
+
}))
|
|
306
|
+
.actions(self => ({
|
|
307
|
+
/**
|
|
308
|
+
* #action
|
|
309
|
+
*/
|
|
310
|
+
setLoadingMSA(arg: boolean) {
|
|
311
|
+
self.loadingMSA = arg
|
|
312
|
+
},
|
|
313
|
+
/**
|
|
314
|
+
* #action
|
|
315
|
+
*/
|
|
316
|
+
setLoadingTree(arg: boolean) {
|
|
317
|
+
self.loadingTree = arg
|
|
318
|
+
},
|
|
319
|
+
/**
|
|
320
|
+
* #action
|
|
185
321
|
*/
|
|
186
|
-
|
|
322
|
+
setWidth(arg: number) {
|
|
323
|
+
self.width = arg
|
|
324
|
+
},
|
|
325
|
+
/**
|
|
326
|
+
* #action
|
|
327
|
+
* set the height of the view in px
|
|
328
|
+
*/
|
|
329
|
+
setHeight(height: number) {
|
|
330
|
+
self.height = height
|
|
331
|
+
},
|
|
187
332
|
|
|
188
333
|
/**
|
|
189
|
-
* #
|
|
190
|
-
*
|
|
334
|
+
* #action
|
|
335
|
+
* set error state
|
|
191
336
|
*/
|
|
192
|
-
|
|
337
|
+
setError(error?: unknown) {
|
|
338
|
+
self.error = error
|
|
339
|
+
},
|
|
193
340
|
|
|
194
341
|
/**
|
|
195
|
-
* #
|
|
196
|
-
*
|
|
197
|
-
* lengths. if false, the layout is a "cladogram" that does not take into
|
|
198
|
-
* account evolutionary distances
|
|
342
|
+
* #action
|
|
343
|
+
* set mouse position (row, column) in the MSA
|
|
199
344
|
*/
|
|
200
|
-
|
|
345
|
+
setMousePos(col?: number, row?: number) {
|
|
346
|
+
self.mouseCol = col
|
|
347
|
+
self.mouseRow = row
|
|
348
|
+
},
|
|
201
349
|
/**
|
|
202
|
-
* #
|
|
203
|
-
* draw MSA tiles with a background color
|
|
350
|
+
* #action
|
|
204
351
|
*/
|
|
205
|
-
|
|
352
|
+
setFeatureMode(arg: boolean) {
|
|
353
|
+
self.featureMode = arg
|
|
354
|
+
},
|
|
206
355
|
|
|
207
356
|
/**
|
|
208
|
-
* #
|
|
209
|
-
|
|
357
|
+
* #action
|
|
358
|
+
*/
|
|
359
|
+
setSubFeatureRows(arg: boolean) {
|
|
360
|
+
self.subFeatureRows = arg
|
|
361
|
+
},
|
|
362
|
+
/**
|
|
363
|
+
* #action
|
|
364
|
+
* set mouse click position (row, column) in the MSA
|
|
210
365
|
*/
|
|
211
|
-
|
|
366
|
+
setMouseClickPos(col?: number, row?: number) {
|
|
367
|
+
self.mouseClickCol = col
|
|
368
|
+
self.mouseClickRow = row
|
|
369
|
+
},
|
|
212
370
|
|
|
213
371
|
/**
|
|
214
|
-
* #
|
|
215
|
-
*
|
|
372
|
+
* #action
|
|
373
|
+
* set row height (px)
|
|
216
374
|
*/
|
|
217
|
-
|
|
375
|
+
setRowHeight(n: number) {
|
|
376
|
+
self.rowHeight = n
|
|
377
|
+
},
|
|
218
378
|
|
|
219
379
|
/**
|
|
220
|
-
* #
|
|
221
|
-
*
|
|
222
|
-
* screens
|
|
380
|
+
* #action
|
|
381
|
+
* set col width (px)
|
|
223
382
|
*/
|
|
224
|
-
|
|
383
|
+
setColWidth(n: number) {
|
|
384
|
+
self.colWidth = n
|
|
385
|
+
},
|
|
225
386
|
|
|
226
387
|
/**
|
|
227
|
-
* #
|
|
228
|
-
*
|
|
388
|
+
* #action
|
|
389
|
+
* set scroll Y-offset (px)
|
|
229
390
|
*/
|
|
230
|
-
|
|
391
|
+
setScrollY(n: number) {
|
|
392
|
+
self.scrollY = n
|
|
393
|
+
},
|
|
231
394
|
|
|
232
395
|
/**
|
|
233
|
-
* #
|
|
234
|
-
*
|
|
396
|
+
* #action
|
|
397
|
+
*
|
|
235
398
|
*/
|
|
236
|
-
|
|
399
|
+
setCurrentAlignment(n: number) {
|
|
400
|
+
self.currentAlignment = n
|
|
401
|
+
},
|
|
237
402
|
|
|
238
403
|
/**
|
|
239
|
-
* #
|
|
240
|
-
* filehandle object for the MSA (which could contain a tree e.g. with
|
|
241
|
-
* stockholm files)
|
|
404
|
+
* #action
|
|
242
405
|
*/
|
|
243
|
-
|
|
406
|
+
toggleCollapsed(node: string) {
|
|
407
|
+
if (self.collapsed.includes(node)) {
|
|
408
|
+
self.collapsed.remove(node)
|
|
409
|
+
} else {
|
|
410
|
+
self.collapsed.push(node)
|
|
411
|
+
}
|
|
412
|
+
},
|
|
244
413
|
|
|
245
414
|
/**
|
|
246
|
-
* #
|
|
247
|
-
* filehandle object for tree metadata
|
|
415
|
+
* #action
|
|
248
416
|
*/
|
|
249
|
-
|
|
417
|
+
toggleCollapsed2(node: string) {
|
|
418
|
+
if (self.collapsed2.includes(node)) {
|
|
419
|
+
self.collapsed2.remove(node)
|
|
420
|
+
} else {
|
|
421
|
+
self.collapsed2.push(node)
|
|
422
|
+
}
|
|
423
|
+
},
|
|
424
|
+
/**
|
|
425
|
+
* #action
|
|
426
|
+
*/
|
|
427
|
+
setShowOnly(node?: string) {
|
|
428
|
+
self.showOnly = node
|
|
429
|
+
},
|
|
250
430
|
|
|
251
431
|
/**
|
|
252
|
-
* #
|
|
253
|
-
*
|
|
432
|
+
* #action
|
|
254
433
|
*/
|
|
255
|
-
|
|
434
|
+
setData(data: { msa?: string; tree?: string }) {
|
|
435
|
+
self.data = cast(data)
|
|
436
|
+
},
|
|
256
437
|
|
|
257
438
|
/**
|
|
258
|
-
* #
|
|
259
|
-
* array of tree nodes that are 'collapsed'
|
|
439
|
+
* #action
|
|
260
440
|
*/
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
441
|
+
setMSAFilehandle(msaFilehandle?: FileLocationType) {
|
|
442
|
+
self.msaFilehandle = msaFilehandle
|
|
443
|
+
},
|
|
444
|
+
|
|
264
445
|
/**
|
|
265
|
-
* #
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
showOnly: types.maybe(types.string),
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* #property
|
|
272
|
-
* a list of "tracks" to display, as box-like glyphs (e.g. protein
|
|
273
|
-
* domains)
|
|
274
|
-
*/
|
|
275
|
-
boxTracks: types.array(UniprotTrack),
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* #property
|
|
279
|
-
* turned off tracks
|
|
280
|
-
*/
|
|
281
|
-
turnedOffTracks: types.map(types.boolean),
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* #property
|
|
285
|
-
* data from the loaded tree/msa/treeMetadata, generally loaded by
|
|
286
|
-
* autorun
|
|
287
|
-
*/
|
|
288
|
-
data: types.optional(DataModelF(), { tree: '', msa: '' }),
|
|
289
|
-
}),
|
|
290
|
-
)
|
|
291
|
-
.volatile(() => ({
|
|
292
|
-
/**
|
|
293
|
-
* #volatile
|
|
294
|
-
* resize handle width between tree and msa area, px
|
|
295
|
-
*/
|
|
296
|
-
resizeHandleWidth: 5,
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* #volatile
|
|
300
|
-
* size of blocks of content to be drawn, px
|
|
301
|
-
*/
|
|
302
|
-
blockSize: 1000,
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* #volatile
|
|
306
|
-
* the currently mouse-hovered row
|
|
307
|
-
*/
|
|
308
|
-
mouseRow: undefined as number | undefined,
|
|
309
|
-
|
|
310
|
-
/**
|
|
311
|
-
* #volatile
|
|
312
|
-
* the currently mouse-hovered column
|
|
313
|
-
*/
|
|
314
|
-
mouseCol: undefined as number | undefined,
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* #volatile
|
|
318
|
-
* the currently mouse-click row
|
|
319
|
-
*/
|
|
320
|
-
mouseClickRow: undefined as number | undefined,
|
|
321
|
-
|
|
322
|
-
/**
|
|
323
|
-
* #volatile
|
|
324
|
-
* the currently mouse-click column
|
|
325
|
-
*/
|
|
326
|
-
mouseClickCol: undefined as number | undefined,
|
|
327
|
-
|
|
328
|
-
/**
|
|
329
|
-
* #volatile
|
|
330
|
-
* a dummy variable that is incremented when ref changes so autorun for
|
|
331
|
-
* drawing canvas commands will run
|
|
332
|
-
*/
|
|
333
|
-
nref: 0,
|
|
334
|
-
|
|
335
|
-
/**
|
|
336
|
-
* #volatile
|
|
337
|
-
*/
|
|
338
|
-
minimapHeight: 56,
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* #volatile
|
|
342
|
-
*/
|
|
343
|
-
marginLeft: 20,
|
|
344
|
-
|
|
345
|
-
/**
|
|
346
|
-
* #volatile
|
|
347
|
-
*/
|
|
348
|
-
error: undefined as unknown,
|
|
349
|
-
|
|
350
|
-
/**
|
|
351
|
-
* #volatile
|
|
352
|
-
*/
|
|
353
|
-
annotPos: undefined as { left: number; right: number } | undefined,
|
|
354
|
-
}))
|
|
355
|
-
.actions(self => ({
|
|
356
|
-
/**
|
|
357
|
-
* #action
|
|
358
|
-
* set the height of the view in px
|
|
359
|
-
*/
|
|
360
|
-
setHeight(height: number) {
|
|
361
|
-
self.height = height
|
|
362
|
-
},
|
|
363
|
-
|
|
364
|
-
/**
|
|
365
|
-
* #action
|
|
366
|
-
* set error state
|
|
367
|
-
*/
|
|
368
|
-
setError(error?: unknown) {
|
|
369
|
-
self.error = error
|
|
370
|
-
},
|
|
371
|
-
|
|
372
|
-
/**
|
|
373
|
-
* #action
|
|
374
|
-
* set mouse position (row, column) in the MSA
|
|
375
|
-
*/
|
|
376
|
-
setMousePos(col?: number, row?: number) {
|
|
377
|
-
self.mouseCol = col
|
|
378
|
-
self.mouseRow = row
|
|
379
|
-
},
|
|
380
|
-
|
|
381
|
-
/**
|
|
382
|
-
* #action
|
|
383
|
-
* set mouse click position (row, column) in the MSA
|
|
384
|
-
*/
|
|
385
|
-
setMouseClickPos(col?: number, row?: number) {
|
|
386
|
-
self.mouseClickCol = col
|
|
387
|
-
self.mouseClickRow = row
|
|
388
|
-
},
|
|
389
|
-
|
|
390
|
-
/**
|
|
391
|
-
* #action
|
|
392
|
-
* set row height (px)
|
|
393
|
-
*/
|
|
394
|
-
setRowHeight(n: number) {
|
|
395
|
-
self.rowHeight = n
|
|
396
|
-
},
|
|
397
|
-
|
|
398
|
-
/**
|
|
399
|
-
* #action
|
|
400
|
-
* set col width (px)
|
|
401
|
-
*/
|
|
402
|
-
setColWidth(n: number) {
|
|
403
|
-
self.colWidth = n
|
|
404
|
-
},
|
|
405
|
-
|
|
406
|
-
/**
|
|
407
|
-
* #action
|
|
408
|
-
* set color scheme name
|
|
409
|
-
*/
|
|
410
|
-
setColorSchemeName(name: string) {
|
|
411
|
-
self.colorSchemeName = name
|
|
412
|
-
},
|
|
413
|
-
|
|
414
|
-
/**
|
|
415
|
-
* #action
|
|
416
|
-
* synchronize the treewidth and treeareawidth
|
|
417
|
-
*/
|
|
418
|
-
setTreeWidthMatchesArea(arg: boolean) {
|
|
419
|
-
self.treeWidthMatchesArea = arg
|
|
420
|
-
},
|
|
421
|
-
|
|
422
|
-
/**
|
|
423
|
-
* #action
|
|
424
|
-
* set scroll Y-offset (px)
|
|
425
|
-
*/
|
|
426
|
-
setScrollY(n: number) {
|
|
427
|
-
self.scrollY = n
|
|
428
|
-
},
|
|
429
|
-
|
|
430
|
-
/**
|
|
431
|
-
* #action
|
|
432
|
-
* set tree area width (px)
|
|
433
|
-
*/
|
|
434
|
-
setTreeAreaWidth(n: number) {
|
|
435
|
-
self.treeAreaWidth = n
|
|
436
|
-
},
|
|
437
|
-
/**
|
|
438
|
-
* #action
|
|
439
|
-
* set tree width (px)
|
|
440
|
-
*/
|
|
441
|
-
setTreeWidth(n: number) {
|
|
442
|
-
self.treeWidth = n
|
|
443
|
-
},
|
|
444
|
-
|
|
445
|
-
/**
|
|
446
|
-
* #action
|
|
447
|
-
*
|
|
448
|
-
*/
|
|
449
|
-
setCurrentAlignment(n: number) {
|
|
450
|
-
self.currentAlignment = n
|
|
451
|
-
},
|
|
452
|
-
|
|
453
|
-
/**
|
|
454
|
-
* #action
|
|
455
|
-
*/
|
|
456
|
-
setLabelsAlignRight(arg: boolean) {
|
|
457
|
-
self.labelsAlignRight = arg
|
|
458
|
-
},
|
|
459
|
-
/**
|
|
460
|
-
* #action
|
|
461
|
-
*/
|
|
462
|
-
setDrawTree(arg: boolean) {
|
|
463
|
-
self.drawTree = arg
|
|
464
|
-
},
|
|
465
|
-
|
|
466
|
-
/**
|
|
467
|
-
* #action
|
|
468
|
-
*/
|
|
469
|
-
toggleCollapsed(node: string) {
|
|
470
|
-
if (self.collapsed.includes(node)) {
|
|
471
|
-
self.collapsed.remove(node)
|
|
472
|
-
} else {
|
|
473
|
-
self.collapsed.push(node)
|
|
474
|
-
}
|
|
475
|
-
},
|
|
476
|
-
|
|
477
|
-
/**
|
|
478
|
-
* #action
|
|
479
|
-
*/
|
|
480
|
-
toggleCollapsed2(node: string) {
|
|
481
|
-
if (self.collapsed2.includes(node)) {
|
|
482
|
-
self.collapsed2.remove(node)
|
|
483
|
-
} else {
|
|
484
|
-
self.collapsed2.push(node)
|
|
485
|
-
}
|
|
486
|
-
},
|
|
487
|
-
/**
|
|
488
|
-
* #action
|
|
489
|
-
*/
|
|
490
|
-
setShowOnly(node?: string) {
|
|
491
|
-
self.showOnly = node
|
|
492
|
-
},
|
|
493
|
-
|
|
494
|
-
/**
|
|
495
|
-
* #action
|
|
496
|
-
*/
|
|
497
|
-
setShowBranchLen(arg: boolean) {
|
|
498
|
-
self.showBranchLen = arg
|
|
499
|
-
},
|
|
500
|
-
|
|
501
|
-
/**
|
|
502
|
-
* #action
|
|
503
|
-
*/
|
|
504
|
-
setBgColor(arg: boolean) {
|
|
505
|
-
self.bgColor = arg
|
|
506
|
-
},
|
|
507
|
-
|
|
508
|
-
/**
|
|
509
|
-
* #action
|
|
510
|
-
*/
|
|
511
|
-
setDrawNodeBubbles(arg: boolean) {
|
|
512
|
-
self.drawNodeBubbles = arg
|
|
513
|
-
},
|
|
514
|
-
|
|
515
|
-
/**
|
|
516
|
-
* #action
|
|
517
|
-
*/
|
|
518
|
-
setData(data: { msa?: string; tree?: string }) {
|
|
519
|
-
self.data = cast(data)
|
|
520
|
-
},
|
|
521
|
-
|
|
522
|
-
/**
|
|
523
|
-
* #action
|
|
524
|
-
*/
|
|
525
|
-
async setMSAFilehandle(msaFilehandle?: FileLocationType) {
|
|
526
|
-
self.msaFilehandle = msaFilehandle
|
|
527
|
-
},
|
|
528
|
-
|
|
529
|
-
/**
|
|
530
|
-
* #action
|
|
531
|
-
*/
|
|
532
|
-
async setTreeFilehandle(treeFilehandle?: FileLocationType) {
|
|
533
|
-
if (treeFilehandle && 'blobId' in treeFilehandle) {
|
|
534
|
-
const r = await openLocation(treeFilehandle).readFile('utf8')
|
|
535
|
-
this.setTree(r)
|
|
536
|
-
} else {
|
|
446
|
+
* #action
|
|
447
|
+
*/
|
|
448
|
+
setTreeFilehandle(treeFilehandle?: FileLocationType) {
|
|
537
449
|
self.treeFilehandle = treeFilehandle
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
}),
|
|
591
|
-
}
|
|
592
|
-
},
|
|
593
|
-
/**
|
|
594
|
-
* #getter
|
|
595
|
-
*/
|
|
596
|
-
get currentAlignmentName() {
|
|
597
|
-
return this.alignmentNames[self.currentAlignment]
|
|
598
|
-
},
|
|
599
|
-
/**
|
|
600
|
-
* #getter
|
|
601
|
-
*/
|
|
602
|
-
get alignmentNames() {
|
|
603
|
-
return this.MSA?.alignmentNames || []
|
|
604
|
-
},
|
|
605
|
-
/**
|
|
606
|
-
* #getter
|
|
607
|
-
*/
|
|
608
|
-
get noTree() {
|
|
609
|
-
return !!this._tree.noTree
|
|
610
|
-
},
|
|
611
|
-
/**
|
|
612
|
-
* #getter
|
|
613
|
-
*/
|
|
614
|
-
get menuItems() {
|
|
615
|
-
return []
|
|
616
|
-
},
|
|
617
|
-
/**
|
|
618
|
-
* #getter
|
|
619
|
-
*/
|
|
620
|
-
get treeMetadata() {
|
|
621
|
-
return self.data.treeMetadata ? JSON.parse(self.data.treeMetadata) : {}
|
|
622
|
-
},
|
|
623
|
-
/**
|
|
624
|
-
* #getter
|
|
625
|
-
*/
|
|
626
|
-
get MSA() {
|
|
627
|
-
const text = self.data.msa
|
|
628
|
-
if (text) {
|
|
629
|
-
if (Stockholm.sniff(text)) {
|
|
630
|
-
return new StockholmMSA(text, self.currentAlignment)
|
|
631
|
-
} else if (text.startsWith('>')) {
|
|
632
|
-
return new FastaMSA(text)
|
|
633
|
-
} else {
|
|
634
|
-
return new ClustalMSA(text)
|
|
450
|
+
},
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* #action
|
|
454
|
+
*/
|
|
455
|
+
setMSA(result: string) {
|
|
456
|
+
self.data.setMSA(result)
|
|
457
|
+
},
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* #action
|
|
461
|
+
*/
|
|
462
|
+
setTree(result: string) {
|
|
463
|
+
self.data.setTree(result)
|
|
464
|
+
},
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* #action
|
|
468
|
+
*/
|
|
469
|
+
setTreeMetadata(result: string) {
|
|
470
|
+
self.data.setTreeMetadata(result)
|
|
471
|
+
},
|
|
472
|
+
}))
|
|
473
|
+
|
|
474
|
+
.views(self => ({
|
|
475
|
+
/**
|
|
476
|
+
* #method
|
|
477
|
+
* unused here, but can be used by derived classes to add extra items
|
|
478
|
+
*/
|
|
479
|
+
extraViewMenuItems() {
|
|
480
|
+
return []
|
|
481
|
+
},
|
|
482
|
+
/**
|
|
483
|
+
* #getter
|
|
484
|
+
*/
|
|
485
|
+
get colorScheme() {
|
|
486
|
+
return colorSchemes[self.colorSchemeName]
|
|
487
|
+
},
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* #getter
|
|
491
|
+
*/
|
|
492
|
+
get header() {
|
|
493
|
+
return this.MSA?.getHeader() || {}
|
|
494
|
+
},
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* #method
|
|
498
|
+
*/
|
|
499
|
+
getRowData(name: string) {
|
|
500
|
+
return {
|
|
501
|
+
data: this.MSA?.getRowData(name),
|
|
635
502
|
}
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
503
|
+
},
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* #getter
|
|
507
|
+
*/
|
|
508
|
+
get alignmentNames() {
|
|
509
|
+
return this.MSA?.alignmentNames || []
|
|
510
|
+
},
|
|
511
|
+
/**
|
|
512
|
+
* #getter
|
|
513
|
+
*/
|
|
514
|
+
get noTree() {
|
|
515
|
+
return !!this._tree.noTree
|
|
516
|
+
},
|
|
517
|
+
/**
|
|
518
|
+
* #getter
|
|
519
|
+
*/
|
|
520
|
+
get noAnnotations() {
|
|
521
|
+
return !self.loadedInterProAnnotations
|
|
522
|
+
},
|
|
523
|
+
/**
|
|
524
|
+
* #getter
|
|
525
|
+
*/
|
|
526
|
+
get menuItems() {
|
|
527
|
+
return []
|
|
528
|
+
},
|
|
529
|
+
/**
|
|
530
|
+
* #getter
|
|
531
|
+
*/
|
|
532
|
+
get treeMetadata() {
|
|
533
|
+
return self.data.treeMetadata ? JSON.parse(self.data.treeMetadata) : {}
|
|
534
|
+
},
|
|
535
|
+
/**
|
|
536
|
+
* #getter
|
|
537
|
+
*/
|
|
538
|
+
get MSA() {
|
|
539
|
+
const text = self.data.msa
|
|
540
|
+
if (text) {
|
|
541
|
+
if (Stockholm.sniff(text)) {
|
|
542
|
+
return new StockholmMSA(text, self.currentAlignment)
|
|
543
|
+
} else if (text.startsWith('>')) {
|
|
544
|
+
return new FastaMSA(text)
|
|
545
|
+
} else {
|
|
546
|
+
return new ClustalMSA(text)
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
return null
|
|
550
|
+
},
|
|
551
|
+
/**
|
|
552
|
+
* #getter
|
|
553
|
+
*/
|
|
554
|
+
get numColumns() {
|
|
555
|
+
return (this.MSA?.getWidth() || 0) - this.blanks.length
|
|
556
|
+
},
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* #getter
|
|
560
|
+
*/
|
|
561
|
+
get _tree(): NodeWithIds {
|
|
562
|
+
const ret = self.data.tree
|
|
563
|
+
? generateNodeIds(parseNewick(self.data.tree))
|
|
564
|
+
: this.MSA?.getTree() || {
|
|
565
|
+
noTree: true,
|
|
566
|
+
branchset: [],
|
|
567
|
+
id: 'empty',
|
|
568
|
+
name: 'empty',
|
|
569
|
+
}
|
|
570
|
+
return reparseTree(ret)
|
|
571
|
+
},
|
|
572
|
+
/**
|
|
573
|
+
* #getter
|
|
574
|
+
*/
|
|
575
|
+
get rowNames(): string[] {
|
|
576
|
+
return this.hierarchy.leaves().map(n => n.data.name)
|
|
577
|
+
},
|
|
578
|
+
/**
|
|
579
|
+
* #getter
|
|
580
|
+
*/
|
|
581
|
+
get mouseOverRowName() {
|
|
582
|
+
const { mouseRow } = self
|
|
583
|
+
return mouseRow !== undefined ? this.rowNames[mouseRow] : undefined
|
|
584
|
+
},
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* #getter
|
|
588
|
+
*/
|
|
589
|
+
get root() {
|
|
590
|
+
let hier = hierarchy(this._tree, d => d.branchset)
|
|
591
|
+
.sum(d => (d.branchset ? 0 : 1))
|
|
592
|
+
.sort((a, b) => ascending(a.data.length || 1, b.data.length || 1))
|
|
593
|
+
|
|
594
|
+
if (self.showOnly) {
|
|
595
|
+
const res = hier.find(n => n.data.id === self.showOnly)
|
|
596
|
+
if (res) {
|
|
597
|
+
hier = res
|
|
656
598
|
}
|
|
657
|
-
return reparseTree(ret)
|
|
658
|
-
},
|
|
659
|
-
/**
|
|
660
|
-
* #getter
|
|
661
|
-
*/
|
|
662
|
-
get rowNames(): string[] {
|
|
663
|
-
return this.hierarchy.leaves().map(node => node.data.name)
|
|
664
|
-
},
|
|
665
|
-
/**
|
|
666
|
-
* #getter
|
|
667
|
-
*/
|
|
668
|
-
get mouseOverRowName() {
|
|
669
|
-
return self.mouseRow !== undefined
|
|
670
|
-
? this.rowNames[self.mouseRow]
|
|
671
|
-
: undefined
|
|
672
|
-
},
|
|
673
|
-
|
|
674
|
-
/**
|
|
675
|
-
* #method
|
|
676
|
-
*/
|
|
677
|
-
getMouseOverResidue(rowName: string) {
|
|
678
|
-
return this.columns[rowName]
|
|
679
|
-
},
|
|
680
|
-
|
|
681
|
-
/**
|
|
682
|
-
* #getter
|
|
683
|
-
*/
|
|
684
|
-
get root() {
|
|
685
|
-
let hier = hierarchy(this._tree, d => d.branchset)
|
|
686
|
-
.sum(d => (d.branchset ? 0 : 1))
|
|
687
|
-
.sort((a, b) => ascending(a.data.length || 1, b.data.length || 1))
|
|
688
|
-
|
|
689
|
-
if (self.showOnly) {
|
|
690
|
-
const res = hier.find(node => node.data.id === self.showOnly)
|
|
691
|
-
if (res) {
|
|
692
|
-
hier = res
|
|
693
599
|
}
|
|
694
|
-
}
|
|
695
600
|
|
|
696
|
-
if (self.collapsed.length || self.collapsed2.length) {
|
|
697
601
|
;[...self.collapsed, ...self.collapsed2]
|
|
698
602
|
.map(collapsedId => hier.find(node => node.data.id === collapsedId))
|
|
699
603
|
.filter(notEmpty)
|
|
700
604
|
.map(node => collapse(node))
|
|
701
|
-
}
|
|
702
605
|
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
* #getter
|
|
737
|
-
*/
|
|
738
|
-
get blanks() {
|
|
739
|
-
const blanks = []
|
|
740
|
-
const strs = this.hierarchy
|
|
741
|
-
.leaves()
|
|
742
|
-
.map(leaf => this.MSA?.getRow(leaf.data.name))
|
|
743
|
-
.filter((item): item is string => !!item)
|
|
744
|
-
|
|
745
|
-
for (let i = 0; i < strs[0]?.length; i++) {
|
|
746
|
-
let counter = 0
|
|
747
|
-
for (const str of strs) {
|
|
748
|
-
if (str[i] === '-') {
|
|
749
|
-
counter++
|
|
606
|
+
return hier
|
|
607
|
+
},
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* #getter
|
|
611
|
+
* widget width minus the tree area gives the space for the MSA
|
|
612
|
+
*/
|
|
613
|
+
get msaAreaWidth() {
|
|
614
|
+
return self.width - self.treeAreaWidth
|
|
615
|
+
},
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* #getter
|
|
619
|
+
*/
|
|
620
|
+
get treeAreaWidthMinusMargin() {
|
|
621
|
+
return self.treeAreaWidth - self.marginLeft
|
|
622
|
+
},
|
|
623
|
+
/**
|
|
624
|
+
* #getter
|
|
625
|
+
*/
|
|
626
|
+
get blanks() {
|
|
627
|
+
const blanks = []
|
|
628
|
+
const strs = this.hierarchy
|
|
629
|
+
.leaves()
|
|
630
|
+
.map(leaf => this.MSA?.getRow(leaf.data.name))
|
|
631
|
+
.filter((item): item is string => !!item)
|
|
632
|
+
|
|
633
|
+
for (let i = 0; i < strs[0]?.length; i++) {
|
|
634
|
+
let counter = 0
|
|
635
|
+
for (const str of strs) {
|
|
636
|
+
if (str[i] === '-') {
|
|
637
|
+
counter++
|
|
638
|
+
}
|
|
750
639
|
}
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
blanks.push(i)
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
return blanks
|
|
757
|
-
},
|
|
758
|
-
/**
|
|
759
|
-
* #getter
|
|
760
|
-
*/
|
|
761
|
-
get rows() {
|
|
762
|
-
const MSA = this.MSA
|
|
763
|
-
return this.hierarchy
|
|
764
|
-
.leaves()
|
|
765
|
-
.map(leaf => [leaf.data.name, MSA?.getRow(leaf.data.name)] as const)
|
|
766
|
-
.filter((f): f is [string, string] => !!f[1])
|
|
767
|
-
},
|
|
768
|
-
/**
|
|
769
|
-
* #getter
|
|
770
|
-
*/
|
|
771
|
-
get columns() {
|
|
772
|
-
return Object.fromEntries(
|
|
773
|
-
this.rows.map((row, index) => [row[0], this.columns2d[index]] as const),
|
|
774
|
-
)
|
|
775
|
-
},
|
|
776
|
-
/**
|
|
777
|
-
* #getter
|
|
778
|
-
*/
|
|
779
|
-
get columns2d() {
|
|
780
|
-
return this.rows.map(r => r[1]).map(str => skipBlanks(this.blanks, str))
|
|
781
|
-
},
|
|
782
|
-
/**
|
|
783
|
-
* #getter
|
|
784
|
-
*/
|
|
785
|
-
get fontSize() {
|
|
786
|
-
return Math.max(8, self.rowHeight - 8)
|
|
787
|
-
},
|
|
788
|
-
/**
|
|
789
|
-
* #getter
|
|
790
|
-
*/
|
|
791
|
-
get colStats() {
|
|
792
|
-
const r = [] as Record<string, number>[]
|
|
793
|
-
const columns = this.columns2d
|
|
794
|
-
for (const column of columns) {
|
|
795
|
-
for (let j = 0; j < column.length; j++) {
|
|
796
|
-
const l = r[j] || {}
|
|
797
|
-
if (!l[column[j]]) {
|
|
798
|
-
l[column[j]] = 0
|
|
640
|
+
if (counter === strs.length) {
|
|
641
|
+
blanks.push(i)
|
|
799
642
|
}
|
|
800
|
-
l[column[j]]++
|
|
801
|
-
r[j] = l
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
return r
|
|
805
|
-
},
|
|
806
|
-
/**
|
|
807
|
-
* #getter
|
|
808
|
-
* generates a new tree that is clustered with x,y positions
|
|
809
|
-
*/
|
|
810
|
-
get hierarchy(): HierarchyNode<NodeWithIdsAndLength> {
|
|
811
|
-
const r = this.root
|
|
812
|
-
const clust = cluster<NodeWithIds>()
|
|
813
|
-
.size([this.totalHeight, self.treeWidth])
|
|
814
|
-
.separation(() => 1)
|
|
815
|
-
clust(r)
|
|
816
|
-
setBrLength(r, (r.data.length = 0), self.treeWidth / maxLength(r))
|
|
817
|
-
return r as HierarchyNode<NodeWithIdsAndLength>
|
|
818
|
-
},
|
|
819
|
-
|
|
820
|
-
/**
|
|
821
|
-
* #getter
|
|
822
|
-
*/
|
|
823
|
-
get totalHeight() {
|
|
824
|
-
return this.root.leaves().length * self.rowHeight
|
|
825
|
-
},
|
|
826
|
-
}))
|
|
827
|
-
.views(self => ({
|
|
828
|
-
/**
|
|
829
|
-
* #getter
|
|
830
|
-
*/
|
|
831
|
-
get totalWidth() {
|
|
832
|
-
return self.numColumns * self.colWidth
|
|
833
|
-
},
|
|
834
|
-
}))
|
|
835
|
-
|
|
836
|
-
.views(self => ({
|
|
837
|
-
/**
|
|
838
|
-
* #getter
|
|
839
|
-
*/
|
|
840
|
-
get initialized() {
|
|
841
|
-
return (
|
|
842
|
-
(self.data.msa ||
|
|
843
|
-
self.data.tree ||
|
|
844
|
-
self.msaFilehandle ||
|
|
845
|
-
self.treeFilehandle) &&
|
|
846
|
-
!self.error
|
|
847
|
-
)
|
|
848
|
-
},
|
|
849
|
-
/**
|
|
850
|
-
* #getter
|
|
851
|
-
*/
|
|
852
|
-
get blocksX() {
|
|
853
|
-
return blocksX({
|
|
854
|
-
viewportWidth: self.msaAreaWidth,
|
|
855
|
-
viewportX: -self.scrollX,
|
|
856
|
-
blockSize: self.blockSize,
|
|
857
|
-
mapWidth: self.totalWidth,
|
|
858
|
-
})
|
|
859
|
-
},
|
|
860
|
-
/**
|
|
861
|
-
* #getter
|
|
862
|
-
*/
|
|
863
|
-
get blocksY() {
|
|
864
|
-
return blocksY({
|
|
865
|
-
viewportHeight: self.height,
|
|
866
|
-
viewportY: -self.scrollY,
|
|
867
|
-
blockSize: self.blockSize,
|
|
868
|
-
mapHeight: self.totalHeight,
|
|
869
|
-
})
|
|
870
|
-
},
|
|
871
|
-
}))
|
|
872
|
-
.views(self => ({
|
|
873
|
-
/**
|
|
874
|
-
* #getter
|
|
875
|
-
*/
|
|
876
|
-
get blocks2d() {
|
|
877
|
-
const ret = []
|
|
878
|
-
for (const by of self.blocksY) {
|
|
879
|
-
for (const bx of self.blocksX) {
|
|
880
|
-
ret.push([bx, by])
|
|
881
643
|
}
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
644
|
+
return blanks
|
|
645
|
+
},
|
|
646
|
+
/**
|
|
647
|
+
* #getter
|
|
648
|
+
*/
|
|
649
|
+
get rows() {
|
|
650
|
+
const MSA = this.MSA
|
|
651
|
+
return this.hierarchy
|
|
652
|
+
.leaves()
|
|
653
|
+
.map(leaf => [leaf.data.name, MSA?.getRow(leaf.data.name)] as const)
|
|
654
|
+
.filter((f): f is [string, string] => !!f[1])
|
|
655
|
+
},
|
|
656
|
+
/**
|
|
657
|
+
* #getter
|
|
658
|
+
*/
|
|
659
|
+
get columns() {
|
|
660
|
+
return Object.fromEntries(
|
|
661
|
+
this.rows.map(
|
|
662
|
+
(row, index) => [row[0], this.columns2d[index]] as const,
|
|
663
|
+
),
|
|
664
|
+
)
|
|
665
|
+
},
|
|
666
|
+
/**
|
|
667
|
+
* #getter
|
|
668
|
+
*/
|
|
669
|
+
get columns2d() {
|
|
670
|
+
return this.rows.map(r => r[1]).map(str => skipBlanks(this.blanks, str))
|
|
671
|
+
},
|
|
672
|
+
/**
|
|
673
|
+
* #getter
|
|
674
|
+
*/
|
|
675
|
+
get fontSize() {
|
|
676
|
+
return Math.min(Math.max(6, self.rowHeight - 8), 18)
|
|
677
|
+
},
|
|
678
|
+
/**
|
|
679
|
+
* #getter
|
|
680
|
+
*/
|
|
681
|
+
get colStats() {
|
|
682
|
+
const r = [] as Record<string, number>[]
|
|
683
|
+
const columns = this.columns2d
|
|
684
|
+
for (const column of columns) {
|
|
685
|
+
for (let j = 0; j < column.length; j++) {
|
|
686
|
+
const l = r[j] || {}
|
|
687
|
+
if (!l[column[j]]) {
|
|
688
|
+
l[column[j]] = 0
|
|
689
|
+
}
|
|
690
|
+
l[column[j]]++
|
|
691
|
+
r[j] = l
|
|
692
|
+
}
|
|
907
693
|
}
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
694
|
+
return r
|
|
695
|
+
},
|
|
696
|
+
/**
|
|
697
|
+
* #getter
|
|
698
|
+
* generates a new tree that is clustered with x,y positions
|
|
699
|
+
*/
|
|
700
|
+
get hierarchy(): HierarchyNode<NodeWithIdsAndLength> {
|
|
701
|
+
const r = this.root
|
|
702
|
+
const clust = cluster<NodeWithIds>()
|
|
703
|
+
.size([this.totalHeight, self.treeWidth])
|
|
704
|
+
.separation(() => 1)
|
|
705
|
+
clust(r)
|
|
706
|
+
setBrLength(r, (r.data.length = 0), self.treeWidth / maxLength(r))
|
|
707
|
+
return r as HierarchyNode<NodeWithIdsAndLength>
|
|
708
|
+
},
|
|
709
|
+
|
|
710
|
+
/**
|
|
711
|
+
* #getter
|
|
712
|
+
*/
|
|
713
|
+
get totalHeight() {
|
|
714
|
+
return this.root.leaves().length * self.rowHeight
|
|
715
|
+
},
|
|
716
|
+
}))
|
|
717
|
+
.views(self => ({
|
|
718
|
+
/**
|
|
719
|
+
* #getter
|
|
720
|
+
*/
|
|
721
|
+
get totalWidth() {
|
|
722
|
+
return self.numColumns * self.colWidth
|
|
723
|
+
},
|
|
724
|
+
}))
|
|
725
|
+
|
|
726
|
+
.views(self => ({
|
|
727
|
+
/**
|
|
728
|
+
* #getter
|
|
729
|
+
*/
|
|
730
|
+
get initialized() {
|
|
731
|
+
return (self.data.msa || self.data.tree) && !self.error
|
|
732
|
+
},
|
|
733
|
+
/**
|
|
734
|
+
* #getter
|
|
735
|
+
*/
|
|
736
|
+
get blocksX() {
|
|
737
|
+
return blocksX({
|
|
738
|
+
viewportWidth: self.msaAreaWidth,
|
|
739
|
+
viewportX: -self.scrollX,
|
|
740
|
+
blockSize: self.blockSize,
|
|
741
|
+
mapWidth: self.totalWidth,
|
|
913
742
|
})
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
},
|
|
937
|
-
|
|
938
|
-
/**
|
|
939
|
-
* #action
|
|
940
|
-
*/
|
|
941
|
-
setMouseoveredColumn(n: number, chain: string, file: string) {
|
|
942
|
-
let j = 0
|
|
943
|
-
let i = 0
|
|
944
|
-
const { id } = self.inverseStructures[file.slice(0, -4)] || {}
|
|
945
|
-
const row = self.MSA?.getRow(id)
|
|
946
|
-
|
|
947
|
-
if (row) {
|
|
948
|
-
for (i = 0; i < row.length && j < n; i++) {
|
|
949
|
-
if (row[i] !== '-') {
|
|
950
|
-
j++
|
|
743
|
+
},
|
|
744
|
+
/**
|
|
745
|
+
* #getter
|
|
746
|
+
*/
|
|
747
|
+
get blocksY() {
|
|
748
|
+
return blocksY({
|
|
749
|
+
viewportHeight: self.height,
|
|
750
|
+
viewportY: -self.scrollY,
|
|
751
|
+
blockSize: self.blockSize,
|
|
752
|
+
mapHeight: self.totalHeight,
|
|
753
|
+
})
|
|
754
|
+
},
|
|
755
|
+
}))
|
|
756
|
+
.views(self => ({
|
|
757
|
+
/**
|
|
758
|
+
* #getter
|
|
759
|
+
*/
|
|
760
|
+
get blocks2d() {
|
|
761
|
+
const ret = []
|
|
762
|
+
for (const by of self.blocksY) {
|
|
763
|
+
for (const bx of self.blocksX) {
|
|
764
|
+
ret.push([bx, by])
|
|
951
765
|
}
|
|
952
766
|
}
|
|
953
|
-
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
767
|
+
return ret
|
|
768
|
+
},
|
|
769
|
+
|
|
770
|
+
/**
|
|
771
|
+
* #getter
|
|
772
|
+
*/
|
|
773
|
+
get isLoading() {
|
|
774
|
+
return self.loadingMSA || self.loadingTree
|
|
775
|
+
},
|
|
776
|
+
/**
|
|
777
|
+
* #getter
|
|
778
|
+
*/
|
|
779
|
+
get maxScrollX() {
|
|
780
|
+
return -self.totalWidth + (self.msaAreaWidth - 100)
|
|
781
|
+
},
|
|
782
|
+
}))
|
|
783
|
+
.actions(self => ({
|
|
784
|
+
/**
|
|
785
|
+
* #action
|
|
786
|
+
*/
|
|
787
|
+
zoomIn() {
|
|
788
|
+
self.colWidth = Math.ceil(self.colWidth * 1.5)
|
|
789
|
+
self.rowHeight = Math.ceil(self.rowHeight * 1.5)
|
|
790
|
+
self.scrollX = clamp(self.maxScrollX, self.scrollX, 0)
|
|
791
|
+
},
|
|
792
|
+
/**
|
|
793
|
+
* #action
|
|
794
|
+
*/
|
|
795
|
+
zoomOut() {
|
|
796
|
+
self.colWidth = Math.max(1, Math.floor(self.colWidth * 0.75))
|
|
797
|
+
self.rowHeight = Math.max(1.5, Math.floor(self.rowHeight * 0.75))
|
|
798
|
+
self.scrollX = clamp(self.maxScrollX, self.scrollX, 0)
|
|
799
|
+
},
|
|
800
|
+
/**
|
|
801
|
+
* #action
|
|
802
|
+
*/
|
|
803
|
+
setLoadedInterProAnnotations(data: Record<string, InterProScanResults>) {
|
|
804
|
+
self.loadedInterProAnnotations = data
|
|
805
|
+
},
|
|
806
|
+
|
|
807
|
+
/**
|
|
808
|
+
* #action
|
|
809
|
+
*/
|
|
810
|
+
doScrollY(deltaY: number) {
|
|
811
|
+
self.scrollY = clamp(-self.totalHeight + 10, self.scrollY + deltaY, 0)
|
|
812
|
+
},
|
|
813
|
+
|
|
814
|
+
/**
|
|
815
|
+
* #action
|
|
816
|
+
*/
|
|
817
|
+
doScrollX(deltaX: number) {
|
|
818
|
+
self.scrollX = clamp(self.maxScrollX, self.scrollX + deltaX, 0)
|
|
819
|
+
},
|
|
820
|
+
|
|
821
|
+
/**
|
|
822
|
+
* #action
|
|
823
|
+
*/
|
|
824
|
+
setScrollX(n: number) {
|
|
825
|
+
self.scrollX = clamp(self.maxScrollX, n, 0)
|
|
826
|
+
},
|
|
827
|
+
|
|
828
|
+
/**
|
|
829
|
+
* #action
|
|
830
|
+
*/
|
|
831
|
+
toggleTrack(id: string) {
|
|
832
|
+
if (self.turnedOffTracks.has(id)) {
|
|
833
|
+
self.turnedOffTracks.delete(id)
|
|
834
|
+
} else {
|
|
835
|
+
self.turnedOffTracks.set(id, true)
|
|
985
836
|
}
|
|
986
|
-
}
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
}
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
for (const
|
|
1012
|
-
|
|
837
|
+
},
|
|
838
|
+
/**
|
|
839
|
+
* #action
|
|
840
|
+
*/
|
|
841
|
+
addInterProScanJobId(arg: string) {
|
|
842
|
+
self.interProScanJobIds = [
|
|
843
|
+
...self.interProScanJobIds,
|
|
844
|
+
{ jobId: arg, date: +Date.now() },
|
|
845
|
+
]
|
|
846
|
+
},
|
|
847
|
+
/**
|
|
848
|
+
* #action
|
|
849
|
+
*/
|
|
850
|
+
setStatus(status?: { msg: string; url?: string }) {
|
|
851
|
+
self.status = status
|
|
852
|
+
},
|
|
853
|
+
}))
|
|
854
|
+
.views(self => ({
|
|
855
|
+
/**
|
|
856
|
+
* #getter
|
|
857
|
+
*/
|
|
858
|
+
get labelsWidth() {
|
|
859
|
+
let x = 0
|
|
860
|
+
const { rowHeight, hierarchy, treeMetadata, fontSize } = self
|
|
861
|
+
if (rowHeight > 5) {
|
|
862
|
+
for (const node of hierarchy.leaves()) {
|
|
863
|
+
x = Math.max(
|
|
864
|
+
measureTextCanvas(
|
|
865
|
+
treeMetadata[node.data.name]?.genome || node.data.name,
|
|
866
|
+
fontSize,
|
|
867
|
+
),
|
|
868
|
+
x,
|
|
869
|
+
)
|
|
1013
870
|
}
|
|
1014
871
|
}
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
},
|
|
1018
|
-
|
|
1019
|
-
/**
|
|
1020
|
-
* #getter
|
|
1021
|
-
*/
|
|
1022
|
-
get adapterTrackModels(): BasicTrack[] {
|
|
1023
|
-
return (
|
|
1024
|
-
self.MSA?.tracks.map(t => ({
|
|
1025
|
-
model: {
|
|
1026
|
-
...t,
|
|
1027
|
-
data: t.data ? skipBlanks(self.blanks, t.data) : undefined,
|
|
1028
|
-
height: self.rowHeight,
|
|
1029
|
-
} as TextTrackModel,
|
|
1030
|
-
ReactComponent: TextTrack,
|
|
1031
|
-
})) || []
|
|
1032
|
-
)
|
|
1033
|
-
},
|
|
1034
|
-
|
|
1035
|
-
/**
|
|
1036
|
-
* #getter
|
|
1037
|
-
*/
|
|
1038
|
-
get boxTrackModels(): BasicTrack[] {
|
|
1039
|
-
return self.boxTracks
|
|
1040
|
-
.filter(track => self.rows.some(row => row[0] === track.name))
|
|
1041
|
-
.map(track => ({
|
|
1042
|
-
model: track as BoxTrackModel,
|
|
1043
|
-
ReactComponent: BoxTrack,
|
|
1044
|
-
}))
|
|
1045
|
-
},
|
|
1046
|
-
|
|
1047
|
-
/**
|
|
1048
|
-
* #getter
|
|
1049
|
-
*/
|
|
1050
|
-
get tracks(): BasicTrack[] {
|
|
1051
|
-
return [...this.adapterTrackModels, ...this.boxTrackModels]
|
|
1052
|
-
},
|
|
1053
|
-
/**
|
|
1054
|
-
* #getter
|
|
1055
|
-
*/
|
|
1056
|
-
get turnedOnTracks() {
|
|
1057
|
-
return this.tracks.filter(f => !self.turnedOffTracks.has(f.model.id))
|
|
1058
|
-
},
|
|
1059
|
-
|
|
1060
|
-
/**
|
|
1061
|
-
* #method
|
|
1062
|
-
* returns coordinate in the current relative coordinate scheme
|
|
1063
|
-
*/
|
|
1064
|
-
pxToBp(coord: number) {
|
|
1065
|
-
return Math.floor((coord - self.scrollX) / self.colWidth)
|
|
1066
|
-
},
|
|
1067
|
-
|
|
1068
|
-
/**
|
|
1069
|
-
* #method
|
|
1070
|
-
*/
|
|
1071
|
-
rowSpecificBpToPx(rowName: string, position: number) {
|
|
1072
|
-
const { rowNames, rows } = self
|
|
1073
|
-
const index = rowNames.indexOf(rowName)
|
|
1074
|
-
const row = rows[index][1]
|
|
1075
|
-
const details = self.getRowData(rowName)
|
|
1076
|
-
const offset = details.range?.start || 0
|
|
1077
|
-
const current = position - offset
|
|
1078
|
-
const s = new Set(self.blanks)
|
|
1079
|
-
|
|
1080
|
-
if (current < 0) {
|
|
1081
|
-
return 0
|
|
1082
|
-
}
|
|
872
|
+
return x
|
|
873
|
+
},
|
|
1083
874
|
|
|
1084
|
-
|
|
1085
|
-
|
|
875
|
+
/**
|
|
876
|
+
* #getter
|
|
877
|
+
*/
|
|
878
|
+
get secondaryStructureConsensus() {
|
|
879
|
+
return self.MSA?.secondaryStructureConsensus
|
|
880
|
+
},
|
|
1086
881
|
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
882
|
+
/**
|
|
883
|
+
* #getter
|
|
884
|
+
*/
|
|
885
|
+
get seqConsensus() {
|
|
886
|
+
return self.MSA?.seqConsensus
|
|
887
|
+
},
|
|
1092
888
|
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
889
|
+
/**
|
|
890
|
+
* #getter
|
|
891
|
+
*/
|
|
892
|
+
get conservation() {
|
|
893
|
+
if (self.columns2d.length) {
|
|
894
|
+
for (let i = 0; i < self.columns2d[0].length; i++) {
|
|
895
|
+
const col = []
|
|
896
|
+
for (const column of self.columns2d) {
|
|
897
|
+
col.push(column[i])
|
|
898
|
+
}
|
|
899
|
+
}
|
|
1097
900
|
}
|
|
1098
|
-
|
|
901
|
+
return ['a']
|
|
902
|
+
},
|
|
1099
903
|
|
|
1100
|
-
|
|
1101
|
-
|
|
904
|
+
/**
|
|
905
|
+
* #getter
|
|
906
|
+
*/
|
|
907
|
+
get adapterTrackModels(): BasicTrack[] {
|
|
908
|
+
return (
|
|
909
|
+
self.MSA?.tracks.map(t => ({
|
|
910
|
+
model: {
|
|
911
|
+
...t,
|
|
912
|
+
data: t.data ? skipBlanks(self.blanks, t.data) : undefined,
|
|
913
|
+
height: self.rowHeight,
|
|
914
|
+
} as TextTrackModel,
|
|
915
|
+
ReactComponent: TextTrack,
|
|
916
|
+
})) || []
|
|
917
|
+
)
|
|
918
|
+
},
|
|
1102
919
|
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
920
|
+
/**
|
|
921
|
+
* #getter
|
|
922
|
+
*/
|
|
923
|
+
get tracks(): BasicTrack[] {
|
|
924
|
+
return this.adapterTrackModels
|
|
925
|
+
},
|
|
1109
926
|
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
927
|
+
/**
|
|
928
|
+
* #getter
|
|
929
|
+
*/
|
|
930
|
+
get turnedOnTracks() {
|
|
931
|
+
return this.tracks.filter(f => !self.turnedOffTracks.has(f.model.id))
|
|
932
|
+
},
|
|
1115
933
|
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
k
|
|
1132
|
-
|
|
1133
|
-
|
|
934
|
+
/**
|
|
935
|
+
* #method
|
|
936
|
+
* return a row-specific sequence coordinate, skipping gaps, given a global
|
|
937
|
+
* coordinate
|
|
938
|
+
*/
|
|
939
|
+
globalCoordToRowSpecificSeqCoord(rowName: string, position: number) {
|
|
940
|
+
const { rowNames, rows } = self
|
|
941
|
+
const index = rowNames.indexOf(rowName)
|
|
942
|
+
if (index !== -1 && rows[index]) {
|
|
943
|
+
const row = rows[index][1]
|
|
944
|
+
|
|
945
|
+
let k = 0
|
|
946
|
+
for (let i = 0; i < position; i++) {
|
|
947
|
+
if (row[i] !== '-') {
|
|
948
|
+
k++
|
|
949
|
+
} else if (k >= position) {
|
|
950
|
+
break
|
|
951
|
+
}
|
|
1134
952
|
}
|
|
953
|
+
return k
|
|
1135
954
|
}
|
|
1136
|
-
return
|
|
1137
|
-
}
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
955
|
+
return 0
|
|
956
|
+
},
|
|
957
|
+
|
|
958
|
+
/**
|
|
959
|
+
* #method
|
|
960
|
+
* return a global coordinate given a row-specific sequence coordinate
|
|
961
|
+
* which does not not include gaps
|
|
962
|
+
*/
|
|
963
|
+
seqCoordToRowSpecificGlobalCoord(rowName: string, position: number) {
|
|
964
|
+
const { rowNames, rows } = self
|
|
965
|
+
const index = rowNames.indexOf(rowName)
|
|
966
|
+
if (index !== -1 && rows[index]) {
|
|
967
|
+
const row = rows[index][1]
|
|
968
|
+
|
|
969
|
+
let k = 0
|
|
970
|
+
let i = 0
|
|
971
|
+
for (; k < position; i++) {
|
|
972
|
+
if (row[i] !== '-') {
|
|
973
|
+
k++
|
|
974
|
+
} else if (k >= position) {
|
|
975
|
+
break
|
|
976
|
+
}
|
|
1157
977
|
}
|
|
978
|
+
return i
|
|
1158
979
|
}
|
|
1159
|
-
return
|
|
1160
|
-
}
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
980
|
+
return 0
|
|
981
|
+
},
|
|
982
|
+
}))
|
|
983
|
+
|
|
984
|
+
.views(self => ({
|
|
985
|
+
/**
|
|
986
|
+
* #getter
|
|
987
|
+
* total height of track area (px)
|
|
988
|
+
*/
|
|
989
|
+
get totalTrackAreaHeight() {
|
|
990
|
+
return sum(self.turnedOnTracks.map(r => r.model.height))
|
|
991
|
+
},
|
|
992
|
+
/**
|
|
993
|
+
* #getter
|
|
994
|
+
*/
|
|
995
|
+
get tidyTypes() {
|
|
996
|
+
const types = new Map<string, Accession>()
|
|
997
|
+
if (this.tidyAnnotations) {
|
|
998
|
+
for (const { name, accession, description } of this.tidyAnnotations) {
|
|
999
|
+
types.set(accession, { name, accession, description })
|
|
1000
|
+
}
|
|
1172
1001
|
}
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
}) {
|
|
1196
|
-
const { renderToSvg } = await import('./renderToSvg')
|
|
1197
|
-
const html = await renderToSvg(self as MsaViewModel, opts)
|
|
1198
|
-
const blob = new Blob([html], { type: 'image/svg+xml' })
|
|
1199
|
-
saveAs(blob, 'image.svg')
|
|
1200
|
-
},
|
|
1201
|
-
/**
|
|
1202
|
-
* #action
|
|
1203
|
-
* internal, used for drawing to canvas
|
|
1204
|
-
*/
|
|
1205
|
-
incrementRef() {
|
|
1206
|
-
self.nref++
|
|
1207
|
-
},
|
|
1208
|
-
afterCreate() {
|
|
1209
|
-
// autorun opens treeFilehandle
|
|
1210
|
-
addDisposer(
|
|
1211
|
-
self,
|
|
1212
|
-
autorun(async () => {
|
|
1213
|
-
const { treeFilehandle } = self
|
|
1214
|
-
if (treeFilehandle) {
|
|
1215
|
-
try {
|
|
1216
|
-
self.setTree(await openLocation(treeFilehandle).readFile('utf8'))
|
|
1217
|
-
} catch (e) {
|
|
1218
|
-
console.error(e)
|
|
1219
|
-
self.setError(e)
|
|
1002
|
+
return types
|
|
1003
|
+
},
|
|
1004
|
+
get tidyAnnotations() {
|
|
1005
|
+
const ret = []
|
|
1006
|
+
const { loadedInterProAnnotations } = self
|
|
1007
|
+
if (loadedInterProAnnotations) {
|
|
1008
|
+
for (const [id, val] of Object.entries(loadedInterProAnnotations)) {
|
|
1009
|
+
for (const { signature, locations } of val.matches) {
|
|
1010
|
+
const { entry } = signature
|
|
1011
|
+
if (entry) {
|
|
1012
|
+
const { name, accession, description } = entry
|
|
1013
|
+
for (const { start, end } of locations) {
|
|
1014
|
+
ret.push({
|
|
1015
|
+
id,
|
|
1016
|
+
name,
|
|
1017
|
+
accession,
|
|
1018
|
+
description,
|
|
1019
|
+
start,
|
|
1020
|
+
end,
|
|
1021
|
+
})
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1220
1024
|
}
|
|
1221
1025
|
}
|
|
1222
|
-
}
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1026
|
+
}
|
|
1027
|
+
return ret.sort((a, b) => len(b) - len(a))
|
|
1028
|
+
},
|
|
1029
|
+
/**
|
|
1030
|
+
* #getter
|
|
1031
|
+
*/
|
|
1032
|
+
get tidyFilteredAnnotations() {
|
|
1033
|
+
return this.tidyAnnotations.filter(r =>
|
|
1034
|
+
self.featureFilters.get(r.accession),
|
|
1035
|
+
)
|
|
1036
|
+
},
|
|
1037
|
+
/**
|
|
1038
|
+
* #getter
|
|
1039
|
+
*/
|
|
1040
|
+
get tidyFilteredGatheredAnnotations() {
|
|
1041
|
+
return groupBy(this.tidyFilteredAnnotations, r => r.id)
|
|
1042
|
+
},
|
|
1043
|
+
}))
|
|
1044
|
+
.views(self => ({
|
|
1045
|
+
get fillPalette() {
|
|
1046
|
+
const arr = [...self.tidyTypes.keys()]
|
|
1047
|
+
let i = 0
|
|
1048
|
+
const map = {} as Record<string, string>
|
|
1049
|
+
for (const key of arr) {
|
|
1050
|
+
const k = Math.min(arr.length - 1, palettes.length - 1)
|
|
1051
|
+
map[key] = palettes[k][i]
|
|
1052
|
+
i++
|
|
1053
|
+
}
|
|
1054
|
+
return map
|
|
1055
|
+
},
|
|
1056
|
+
get strokePalette() {
|
|
1057
|
+
return Object.fromEntries(
|
|
1058
|
+
Object.entries(this.fillPalette).map(([key, val]) => [
|
|
1059
|
+
key,
|
|
1060
|
+
colord(val).darken(0.1).toHex(),
|
|
1061
|
+
]),
|
|
1062
|
+
)
|
|
1063
|
+
},
|
|
1064
|
+
}))
|
|
1065
|
+
.actions(self => ({
|
|
1066
|
+
/**
|
|
1067
|
+
* #action
|
|
1068
|
+
*/
|
|
1069
|
+
async loadInterProScanResults(jobId: string) {
|
|
1070
|
+
self.setStatus({ msg: 'Loading ' + jobId })
|
|
1071
|
+
const ret = await loadInterProScanResults(jobId)
|
|
1072
|
+
self.setStatus()
|
|
1073
|
+
self.setLoadedInterProAnnotations(
|
|
1074
|
+
Object.fromEntries(ret.results.map(r => [r.xref[0].id, r])),
|
|
1075
|
+
)
|
|
1076
|
+
},
|
|
1077
|
+
/**
|
|
1078
|
+
* #action
|
|
1079
|
+
*/
|
|
1080
|
+
async queryInterProScan(programs: string[]) {
|
|
1081
|
+
const { rows } = self
|
|
1082
|
+
if (rows.length > 140) {
|
|
1083
|
+
throw new Error('Too many sequences, please run InterProScan offline')
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
const ret = await launchInterProScan({
|
|
1087
|
+
algorithm: 'interproscan',
|
|
1088
|
+
programs: programs,
|
|
1089
|
+
seq: rows
|
|
1090
|
+
.map(row => `>${row[0]}\n${row[1].replaceAll('-', '')}`)
|
|
1091
|
+
.join('\n'),
|
|
1092
|
+
onProgress: arg => self.setStatus(arg),
|
|
1093
|
+
onJobId: jobId => self.addInterProScanJobId(jobId),
|
|
1094
|
+
})
|
|
1095
|
+
|
|
1096
|
+
self.setLoadedInterProAnnotations(
|
|
1097
|
+
Object.fromEntries(ret.results.map(r => [r.xref[0].id, r])),
|
|
1098
|
+
)
|
|
1099
|
+
},
|
|
1100
|
+
/**
|
|
1101
|
+
* #action
|
|
1102
|
+
*/
|
|
1103
|
+
reset() {
|
|
1104
|
+
transaction(() => {
|
|
1105
|
+
self.setData({ tree: '', msa: '' })
|
|
1106
|
+
self.setScrollY(0)
|
|
1107
|
+
self.setScrollX(0)
|
|
1108
|
+
self.setCurrentAlignment(0)
|
|
1109
|
+
self.setTreeFilehandle(undefined)
|
|
1110
|
+
self.setMSAFilehandle(undefined)
|
|
1111
|
+
})
|
|
1112
|
+
},
|
|
1113
|
+
/**
|
|
1114
|
+
* #action
|
|
1115
|
+
*/
|
|
1116
|
+
async exportSVG(opts: {
|
|
1117
|
+
theme: Theme
|
|
1118
|
+
includeMinimap?: boolean
|
|
1119
|
+
exportType: string
|
|
1120
|
+
}) {
|
|
1121
|
+
const { renderToSvg } = await import('./renderToSvg')
|
|
1122
|
+
const html = await renderToSvg(self as MsaViewModel, opts)
|
|
1123
|
+
const blob = new Blob([html], { type: 'image/svg+xml' })
|
|
1124
|
+
saveAs(blob, 'image.svg')
|
|
1125
|
+
},
|
|
1126
|
+
/**
|
|
1127
|
+
* #action
|
|
1128
|
+
* internal, used for drawing to canvas
|
|
1129
|
+
*/
|
|
1130
|
+
incrementRef() {
|
|
1131
|
+
self.nref++
|
|
1132
|
+
},
|
|
1133
|
+
|
|
1134
|
+
/**
|
|
1135
|
+
* #action
|
|
1136
|
+
*/
|
|
1137
|
+
initFilter(arg: string) {
|
|
1138
|
+
const ret = self.featureFilters.get(arg)
|
|
1139
|
+
if (ret === undefined) {
|
|
1140
|
+
self.featureFilters.set(arg, true)
|
|
1141
|
+
}
|
|
1142
|
+
},
|
|
1143
|
+
/**
|
|
1144
|
+
* #action
|
|
1145
|
+
*/
|
|
1146
|
+
setFilter(arg: string, flag: boolean) {
|
|
1147
|
+
self.featureFilters.set(arg, flag)
|
|
1148
|
+
},
|
|
1149
|
+
|
|
1150
|
+
afterCreate() {
|
|
1151
|
+
addDisposer(
|
|
1152
|
+
self,
|
|
1153
|
+
autorun(() => {
|
|
1154
|
+
for (const key of self.tidyTypes.keys()) {
|
|
1155
|
+
this.initFilter(key)
|
|
1237
1156
|
}
|
|
1238
|
-
}
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1157
|
+
}),
|
|
1158
|
+
)
|
|
1159
|
+
addDisposer(
|
|
1160
|
+
self,
|
|
1161
|
+
autorun(async () => {
|
|
1162
|
+
// const res = Object.fromEntries(
|
|
1163
|
+
// await Promise.all(
|
|
1164
|
+
// self.annotationTracks.map(async f => {
|
|
1165
|
+
// const d = await jsonfetch(
|
|
1166
|
+
// `https://jbrowse.org/demos/interproscan/json/${encodeURIComponent(f)}`,
|
|
1167
|
+
// )
|
|
1168
|
+
// return [
|
|
1169
|
+
// decodeURIComponent(f).replace('.json', ''),
|
|
1170
|
+
// d.result.results[0],
|
|
1171
|
+
// ] as const
|
|
1172
|
+
// }),
|
|
1173
|
+
// ),
|
|
1174
|
+
// )
|
|
1175
|
+
// self.setLoadedInterProAnnotations(res)
|
|
1176
|
+
}),
|
|
1177
|
+
)
|
|
1178
|
+
// autorun opens treeFilehandle
|
|
1179
|
+
addDisposer(
|
|
1180
|
+
self,
|
|
1181
|
+
autorun(async () => {
|
|
1182
|
+
const { treeFilehandle } = self
|
|
1183
|
+
if (treeFilehandle) {
|
|
1184
|
+
try {
|
|
1185
|
+
self.setLoadingTree(true)
|
|
1186
|
+
self.setTree(
|
|
1187
|
+
await openLocation(treeFilehandle).readFile('utf8'),
|
|
1188
|
+
)
|
|
1189
|
+
if (treeFilehandle.locationType === 'BlobLocation') {
|
|
1190
|
+
// clear filehandle after loading if from a local file
|
|
1191
|
+
self.setTreeFilehandle(undefined)
|
|
1192
|
+
}
|
|
1193
|
+
} catch (e) {
|
|
1194
|
+
console.error(e)
|
|
1195
|
+
self.setError(e)
|
|
1196
|
+
} finally {
|
|
1197
|
+
self.setLoadingTree(false)
|
|
1198
|
+
}
|
|
1253
1199
|
}
|
|
1254
|
-
}
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1200
|
+
}),
|
|
1201
|
+
)
|
|
1202
|
+
// autorun opens treeMetadataFilehandle
|
|
1203
|
+
addDisposer(
|
|
1204
|
+
self,
|
|
1205
|
+
autorun(async () => {
|
|
1206
|
+
const { treeMetadataFilehandle } = self
|
|
1207
|
+
if (treeMetadataFilehandle) {
|
|
1208
|
+
try {
|
|
1209
|
+
self.setTreeMetadata(
|
|
1210
|
+
await openLocation(treeMetadataFilehandle).readFile('utf8'),
|
|
1211
|
+
)
|
|
1212
|
+
} catch (e) {
|
|
1213
|
+
console.error(e)
|
|
1214
|
+
self.setError(e)
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
}),
|
|
1218
|
+
)
|
|
1219
|
+
|
|
1220
|
+
// autorun opens msaFilehandle
|
|
1221
|
+
addDisposer(
|
|
1222
|
+
self,
|
|
1223
|
+
autorun(async () => {
|
|
1224
|
+
const { msaFilehandle } = self
|
|
1225
|
+
if (msaFilehandle) {
|
|
1226
|
+
try {
|
|
1227
|
+
self.setLoadingMSA(true)
|
|
1228
|
+
const res = await openLocation(msaFilehandle).readFile('utf8')
|
|
1229
|
+
transaction(() => {
|
|
1230
|
+
self.setMSA(res)
|
|
1231
|
+
if (msaFilehandle.locationType === 'BlobLocation') {
|
|
1232
|
+
// clear filehandle after loading if from a local file
|
|
1233
|
+
self.setMSAFilehandle(undefined)
|
|
1234
|
+
}
|
|
1235
|
+
})
|
|
1236
|
+
} catch (e) {
|
|
1237
|
+
console.error(e)
|
|
1238
|
+
self.setError(e)
|
|
1239
|
+
} finally {
|
|
1240
|
+
self.setLoadingMSA(false)
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
}),
|
|
1244
|
+
)
|
|
1245
|
+
|
|
1246
|
+
// autorun synchronizes treeWidth with treeAreaWidth
|
|
1247
|
+
addDisposer(
|
|
1248
|
+
self,
|
|
1249
|
+
autorun(async () => {
|
|
1250
|
+
if (self.treeWidthMatchesArea) {
|
|
1251
|
+
self.setTreeWidth(
|
|
1252
|
+
Math.max(
|
|
1253
|
+
50,
|
|
1254
|
+
self.treeAreaWidth - self.labelsWidth - 10 - self.marginLeft,
|
|
1255
|
+
),
|
|
1256
|
+
)
|
|
1257
|
+
}
|
|
1258
|
+
}),
|
|
1259
|
+
)
|
|
1260
|
+
|
|
1261
|
+
addDisposer(
|
|
1262
|
+
self,
|
|
1263
|
+
autorun(() => {
|
|
1264
|
+
localStorageSetItem(
|
|
1265
|
+
'msaview-interproscanqueries',
|
|
1266
|
+
JSON.stringify(self.interProScanJobIds),
|
|
1268
1267
|
)
|
|
1269
|
-
}
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
}
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
...
|
|
1289
|
-
}
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
export type MsaViewStateModel = typeof model
|
|
1268
|
+
}),
|
|
1269
|
+
)
|
|
1270
|
+
},
|
|
1271
|
+
}))
|
|
1272
|
+
.postProcessSnapshot(result => {
|
|
1273
|
+
const snap = result as Omit<typeof result, symbol>
|
|
1274
|
+
const {
|
|
1275
|
+
data: { tree, msa, treeMetadata },
|
|
1276
|
+
...rest
|
|
1277
|
+
} = snap
|
|
1278
|
+
|
|
1279
|
+
// remove the MSA/tree data from the tree if the filehandle available in
|
|
1280
|
+
// which case it can be reloaded on refresh
|
|
1281
|
+
return {
|
|
1282
|
+
data: {
|
|
1283
|
+
...(result.treeFilehandle ? {} : { tree }),
|
|
1284
|
+
...(result.msaFilehandle ? {} : { msa }),
|
|
1285
|
+
...(result.treeMetadataFilehandle ? {} : { treeMetadata }),
|
|
1286
|
+
},
|
|
1287
|
+
...rest,
|
|
1288
|
+
}
|
|
1289
|
+
})
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
export default stateModelFactory
|
|
1293
|
+
|
|
1294
|
+
export type MsaViewStateModel = ReturnType<typeof stateModelFactory>
|
|
1297
1295
|
export type MsaViewModel = Instance<MsaViewStateModel>
|