react-msaview 5.2.0 → 5.3.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/README.md +41 -8
- package/bundle/index.js +17 -17
- package/bundle/index.js.LICENSE.txt +1 -1
- package/bundle/index.js.map +1 -1
- package/dist/components/ConservationTrack.js +2 -13
- package/dist/components/ConservationTrack.js.map +1 -1
- package/dist/components/MSAView.js +8 -1
- package/dist/components/MSAView.js.map +1 -1
- package/dist/components/ResizeHandles.d.ts +3 -0
- package/dist/components/ResizeHandles.js +60 -105
- package/dist/components/ResizeHandles.js.map +1 -1
- package/dist/components/TextTrack.js +2 -1
- package/dist/components/TextTrack.js.map +1 -1
- package/dist/components/Track.js +1 -1
- package/dist/components/Track.js.map +1 -1
- package/dist/components/VerticalScrollbar.js +2 -2
- package/dist/components/VerticalScrollbar.js.map +1 -1
- package/dist/components/dialogs/UserProvidedDomainsDialog.js +1 -2
- package/dist/components/dialogs/UserProvidedDomainsDialog.js.map +1 -1
- package/dist/components/header/ColorSchemeMenu.js +1 -1
- package/dist/components/header/ColorSchemeMenu.js.map +1 -1
- package/dist/components/header/FileMenu.js +1 -1
- package/dist/components/header/FileMenu.js.map +1 -1
- package/dist/components/header/HeaderInfoArea.js +28 -8
- package/dist/components/header/HeaderInfoArea.js.map +1 -1
- package/dist/components/header/MSASettingsMenu.js +1 -1
- package/dist/components/header/MSASettingsMenu.js.map +1 -1
- package/dist/components/header/TreeSettingsMenu.js +1 -1
- package/dist/components/header/TreeSettingsMenu.js.map +1 -1
- package/dist/components/header/ZoomControls.js +7 -2
- package/dist/components/header/ZoomControls.js.map +1 -1
- package/dist/components/header/ZoomMenu.js +9 -0
- package/dist/components/header/ZoomMenu.js.map +1 -1
- package/dist/components/minimap/Minimap.js +6 -12
- package/dist/components/minimap/Minimap.js.map +1 -1
- package/dist/components/minimap/MinimapSVG.js +7 -13
- package/dist/components/minimap/MinimapSVG.js.map +1 -1
- package/dist/components/minimap/minimapLayout.d.ts +9 -0
- package/dist/components/minimap/minimapLayout.js +15 -0
- package/dist/components/minimap/minimapLayout.js.map +1 -0
- package/dist/components/msa/MSACanvas.js +6 -1
- package/dist/components/msa/MSACanvas.js.map +1 -1
- package/dist/components/msa/MSACanvasBlock.js +5 -1
- package/dist/components/msa/MSACanvasBlock.js.map +1 -1
- package/dist/components/msa/MSAMouseoverCanvas.js +2 -2
- package/dist/components/msa/MSAMouseoverCanvas.js.map +1 -1
- package/dist/components/msa/renderMSABlock.js +5 -7
- package/dist/components/msa/renderMSABlock.js.map +1 -1
- package/dist/components/msa/renderMSAMouseover.js +3 -2
- package/dist/components/msa/renderMSAMouseover.js.map +1 -1
- package/dist/components/tracks/renderTracksSvg.js +2 -1
- package/dist/components/tracks/renderTracksSvg.js.map +1 -1
- package/dist/components/tree/renderTreeCanvas.js +2 -2
- package/dist/components/tree/renderTreeCanvas.js.map +1 -1
- package/dist/constants.d.ts +4 -0
- package/dist/constants.js +7 -0
- package/dist/constants.js.map +1 -1
- package/dist/flatToTree.js +6 -0
- package/dist/flatToTree.js.map +1 -1
- package/dist/hierarchy.d.ts +1 -1
- package/dist/hierarchy.js +136 -102
- package/dist/hierarchy.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/launchInterProScan.js +22 -9
- package/dist/launchInterProScan.js.map +1 -1
- package/dist/measureTextCanvas.js +2 -1
- package/dist/measureTextCanvas.js.map +1 -1
- package/dist/model.d.ts +212 -150
- package/dist/model.js +239 -205
- package/dist/model.js.map +1 -1
- package/dist/parseAsn1.js +8 -2
- package/dist/parseAsn1.js.map +1 -1
- package/dist/renderToSvg.js +27 -24
- package/dist/renderToSvg.js.map +1 -1
- package/dist/setFontSize.d.ts +3 -0
- package/dist/setFontSize.js +14 -0
- package/dist/setFontSize.js.map +1 -0
- package/dist/useDevicePixelRatio.d.ts +1 -0
- package/dist/useDevicePixelRatio.js +27 -0
- package/dist/useDevicePixelRatio.js.map +1 -0
- package/dist/useWheelScroll.d.ts +3 -1
- package/dist/useWheelScroll.js +64 -22
- package/dist/useWheelScroll.js.map +1 -1
- package/dist/util.d.ts +4 -0
- package/dist/util.js +36 -0
- package/dist/util.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/webpack.js +0 -1
- package/dist/webpack.js.map +1 -1
- package/dist/wheelZoom.d.ts +4 -0
- package/dist/wheelZoom.js +43 -0
- package/dist/wheelZoom.js.map +1 -0
- package/package.json +5 -5
- package/src/components/ConservationTrack.tsx +2 -18
- package/src/components/MSAView.tsx +10 -1
- package/src/components/ResizeHandles.tsx +101 -113
- package/src/components/TextTrack.tsx +2 -1
- package/src/components/Track.tsx +1 -1
- package/src/components/VerticalScrollbar.tsx +2 -2
- package/src/components/dialogs/UserProvidedDomainsDialog.tsx +1 -2
- package/src/components/header/ColorSchemeMenu.tsx +1 -0
- package/src/components/header/FileMenu.tsx +1 -0
- package/src/components/header/HeaderInfoArea.tsx +32 -7
- package/src/components/header/MSASettingsMenu.tsx +1 -0
- package/src/components/header/TreeSettingsMenu.tsx +1 -0
- package/src/components/header/ZoomControls.tsx +14 -1
- package/src/components/header/ZoomMenu.tsx +10 -0
- package/src/components/minimap/Minimap.tsx +6 -15
- package/src/components/minimap/MinimapSVG.tsx +9 -22
- package/src/components/minimap/minimapLayout.ts +17 -0
- package/src/components/msa/MSACanvas.tsx +9 -0
- package/src/components/msa/MSACanvasBlock.tsx +7 -1
- package/src/components/msa/MSAMouseoverCanvas.tsx +4 -3
- package/src/components/msa/renderMSABlock.ts +6 -9
- package/src/components/msa/renderMSAMouseover.ts +3 -1
- package/src/components/tracks/renderTracksSvg.ts +2 -1
- package/src/components/tree/renderTreeCanvas.ts +2 -2
- package/src/constants.ts +8 -0
- package/src/flatToTree.test.ts +30 -0
- package/src/flatToTree.ts +10 -1
- package/src/hierarchy.test.ts +53 -1
- package/src/hierarchy.ts +139 -113
- package/src/index.ts +1 -0
- package/src/launchInterProScan.ts +33 -11
- package/src/measureTextCanvas.ts +3 -1
- package/src/model.ts +312 -242
- package/src/parseAsn1.test.ts +6 -0
- package/src/parseAsn1.ts +11 -3
- package/src/renderToSvg.tsx +79 -67
- package/src/setFontSize.test.ts +33 -0
- package/src/setFontSize.ts +20 -0
- package/src/useDevicePixelRatio.ts +29 -0
- package/src/useWheelScroll.ts +79 -21
- package/src/util.ts +35 -0
- package/src/version.ts +1 -1
- package/src/webpack.ts +0 -1
- package/src/wheelZoom.test.ts +46 -0
- package/src/wheelZoom.ts +47 -0
package/src/hierarchy.ts
CHANGED
|
@@ -19,45 +19,72 @@ export interface HierarchyLink<T = NodeWithIds> {
|
|
|
19
19
|
target: HierarchyNode<T>
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
// All traversals below are iterative (explicit stack / reversed-preorder) rather
|
|
23
|
+
// than recursive: phylogenetic trees can be deeply unbalanced (a caterpillar tree
|
|
24
|
+
// has depth ~= leaf count), which overflows the JS call stack on recursion.
|
|
25
|
+
|
|
26
|
+
// Pre-order: every parent precedes all of its descendants. Iterating the result
|
|
27
|
+
// in reverse therefore yields a valid post-order (children before parents), which
|
|
28
|
+
// the accumulation helpers rely on.
|
|
29
|
+
export function descendants<T>(node: HierarchyNode<T>): HierarchyNode<T>[] {
|
|
30
|
+
const result: HierarchyNode<T>[] = []
|
|
31
|
+
const stack = [node]
|
|
32
|
+
while (stack.length > 0) {
|
|
33
|
+
const n = stack.pop()!
|
|
34
|
+
result.push(n)
|
|
35
|
+
if (n.children) {
|
|
36
|
+
for (let i = n.children.length - 1; i >= 0; i--) {
|
|
37
|
+
stack.push(n.children[i]!)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return result
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function computeHeight<T>(node: HierarchyNode<T>) {
|
|
45
|
+
const nodes = descendants(node)
|
|
46
|
+
for (let i = nodes.length - 1; i >= 0; i--) {
|
|
47
|
+
const n = nodes[i]!
|
|
48
|
+
let h = 0
|
|
49
|
+
if (n.children) {
|
|
50
|
+
for (const child of n.children) {
|
|
51
|
+
if (child.height + 1 > h) {
|
|
52
|
+
h = child.height + 1
|
|
53
|
+
}
|
|
29
54
|
}
|
|
30
55
|
}
|
|
56
|
+
n.height = h
|
|
31
57
|
}
|
|
32
|
-
node.height = h
|
|
33
|
-
return h
|
|
34
58
|
}
|
|
35
59
|
|
|
36
|
-
function
|
|
60
|
+
export function hierarchy<T>(
|
|
37
61
|
data: T,
|
|
38
62
|
childrenAccessor: (d: T) => T[] | undefined,
|
|
39
|
-
parent: HierarchyNode<T> | null,
|
|
40
|
-
depth: number,
|
|
41
63
|
): HierarchyNode<T> {
|
|
42
|
-
const
|
|
43
|
-
const node: HierarchyNode<T> = {
|
|
64
|
+
const root: HierarchyNode<T> = {
|
|
44
65
|
data,
|
|
45
66
|
children: null,
|
|
46
|
-
parent,
|
|
47
|
-
depth,
|
|
67
|
+
parent: null,
|
|
68
|
+
depth: 0,
|
|
48
69
|
height: 0,
|
|
49
70
|
}
|
|
50
|
-
|
|
51
|
-
|
|
71
|
+
const stack = [root]
|
|
72
|
+
while (stack.length > 0) {
|
|
73
|
+
const node = stack.pop()!
|
|
74
|
+
const kids = childrenAccessor(node.data)
|
|
75
|
+
if (kids?.length) {
|
|
76
|
+
node.children = kids.map(d => ({
|
|
77
|
+
data: d,
|
|
78
|
+
children: null,
|
|
79
|
+
parent: node,
|
|
80
|
+
depth: node.depth + 1,
|
|
81
|
+
height: 0,
|
|
82
|
+
}))
|
|
83
|
+
for (const child of node.children) {
|
|
84
|
+
stack.push(child)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
52
87
|
}
|
|
53
|
-
return node
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function hierarchy<T>(
|
|
57
|
-
data: T,
|
|
58
|
-
childrenAccessor: (d: T) => T[] | undefined,
|
|
59
|
-
): HierarchyNode<T> {
|
|
60
|
-
const root = wrap(data, childrenAccessor, null, 0)
|
|
61
88
|
computeHeight(root)
|
|
62
89
|
return root
|
|
63
90
|
}
|
|
@@ -66,17 +93,17 @@ export function sum<T>(
|
|
|
66
93
|
node: HierarchyNode<T>,
|
|
67
94
|
valueFn: (d: T) => number,
|
|
68
95
|
): HierarchyNode<T> {
|
|
69
|
-
|
|
96
|
+
const nodes = descendants(node)
|
|
97
|
+
for (let i = nodes.length - 1; i >= 0; i--) {
|
|
98
|
+
const n = nodes[i]!
|
|
70
99
|
let s = valueFn(n.data)
|
|
71
100
|
if (n.children) {
|
|
72
101
|
for (const child of n.children) {
|
|
73
|
-
s +=
|
|
102
|
+
s += child.value!
|
|
74
103
|
}
|
|
75
104
|
}
|
|
76
105
|
n.value = s
|
|
77
|
-
return s
|
|
78
106
|
}
|
|
79
|
-
visit(node)
|
|
80
107
|
return node
|
|
81
108
|
}
|
|
82
109
|
|
|
@@ -84,15 +111,16 @@ export function sort<T>(
|
|
|
84
111
|
node: HierarchyNode<T>,
|
|
85
112
|
compareFn: (a: HierarchyNode<T>, b: HierarchyNode<T>) => number,
|
|
86
113
|
): HierarchyNode<T> {
|
|
87
|
-
|
|
114
|
+
const stack = [node]
|
|
115
|
+
while (stack.length > 0) {
|
|
116
|
+
const n = stack.pop()!
|
|
88
117
|
if (n.children) {
|
|
89
118
|
n.children.sort(compareFn)
|
|
90
119
|
for (const child of n.children) {
|
|
91
|
-
|
|
120
|
+
stack.push(child)
|
|
92
121
|
}
|
|
93
122
|
}
|
|
94
123
|
}
|
|
95
|
-
visit(node)
|
|
96
124
|
return node
|
|
97
125
|
}
|
|
98
126
|
|
|
@@ -100,60 +128,42 @@ export function find<T>(
|
|
|
100
128
|
node: HierarchyNode<T>,
|
|
101
129
|
predicate: (n: HierarchyNode<T>) => boolean,
|
|
102
130
|
): HierarchyNode<T> | undefined {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
131
|
+
const stack = [node]
|
|
132
|
+
let found: HierarchyNode<T> | undefined
|
|
133
|
+
while (stack.length > 0 && found === undefined) {
|
|
134
|
+
const n = stack.pop()!
|
|
135
|
+
if (predicate(n)) {
|
|
136
|
+
found = n
|
|
137
|
+
} else if (n.children) {
|
|
138
|
+
for (let i = n.children.length - 1; i >= 0; i--) {
|
|
139
|
+
stack.push(n.children[i]!)
|
|
111
140
|
}
|
|
112
141
|
}
|
|
113
142
|
}
|
|
114
|
-
return
|
|
143
|
+
return found
|
|
115
144
|
}
|
|
116
145
|
|
|
117
146
|
export function leaves<T>(node: HierarchyNode<T>): HierarchyNode<T>[] {
|
|
118
147
|
const result: HierarchyNode<T>[] = []
|
|
119
|
-
|
|
148
|
+
const stack = [node]
|
|
149
|
+
while (stack.length > 0) {
|
|
150
|
+
const n = stack.pop()!
|
|
120
151
|
if (n.children) {
|
|
121
|
-
for (
|
|
122
|
-
|
|
152
|
+
for (let i = n.children.length - 1; i >= 0; i--) {
|
|
153
|
+
stack.push(n.children[i]!)
|
|
123
154
|
}
|
|
124
155
|
} else {
|
|
125
156
|
result.push(n)
|
|
126
157
|
}
|
|
127
158
|
}
|
|
128
|
-
visit(node)
|
|
129
|
-
return result
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
export function descendants<T>(node: HierarchyNode<T>): HierarchyNode<T>[] {
|
|
133
|
-
const result: HierarchyNode<T>[] = []
|
|
134
|
-
function visit(n: HierarchyNode<T>) {
|
|
135
|
-
result.push(n)
|
|
136
|
-
if (n.children) {
|
|
137
|
-
for (const child of n.children) {
|
|
138
|
-
visit(child)
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
visit(node)
|
|
143
159
|
return result
|
|
144
160
|
}
|
|
145
161
|
|
|
146
162
|
export function links<T>(node: HierarchyNode<T>): HierarchyLink<T>[] {
|
|
147
163
|
const result: HierarchyLink<T>[] = []
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
result.push({ source: n, target: child })
|
|
152
|
-
visit(child)
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
visit(node)
|
|
164
|
+
forEachLink(node, (source, target) => {
|
|
165
|
+
result.push({ source, target })
|
|
166
|
+
})
|
|
157
167
|
return result
|
|
158
168
|
}
|
|
159
169
|
|
|
@@ -161,26 +171,25 @@ export function forEachLink<T>(
|
|
|
161
171
|
node: HierarchyNode<T>,
|
|
162
172
|
cb: (source: HierarchyNode<T>, target: HierarchyNode<T>) => void,
|
|
163
173
|
) {
|
|
164
|
-
|
|
174
|
+
const stack = [node]
|
|
175
|
+
while (stack.length > 0) {
|
|
176
|
+
const n = stack.pop()!
|
|
165
177
|
if (n.children) {
|
|
166
|
-
for (
|
|
178
|
+
for (let i = n.children.length - 1; i >= 0; i--) {
|
|
179
|
+
const child = n.children[i]!
|
|
167
180
|
cb(n, child)
|
|
168
|
-
|
|
181
|
+
stack.push(child)
|
|
169
182
|
}
|
|
170
183
|
}
|
|
171
184
|
}
|
|
172
|
-
visit(node)
|
|
173
185
|
}
|
|
174
186
|
|
|
175
187
|
export function forEachDescendant<T>(
|
|
176
188
|
node: HierarchyNode<T>,
|
|
177
189
|
cb: (n: HierarchyNode<T>) => void,
|
|
178
190
|
) {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
for (const child of node.children) {
|
|
182
|
-
forEachDescendant(child, cb)
|
|
183
|
-
}
|
|
191
|
+
for (const n of descendants(node)) {
|
|
192
|
+
cb(n)
|
|
184
193
|
}
|
|
185
194
|
}
|
|
186
195
|
|
|
@@ -197,31 +206,31 @@ export function clusterLayout<T>(
|
|
|
197
206
|
leafNodes[i]!.x = (i + 0.5) * step
|
|
198
207
|
}
|
|
199
208
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
209
|
+
// x of an internal node is the mean of its children's x, so process in
|
|
210
|
+
// post-order (children before parents)
|
|
211
|
+
const nodes = descendants(root)
|
|
212
|
+
for (let i = nodes.length - 1; i >= 0; i--) {
|
|
213
|
+
const node = nodes[i]!
|
|
214
|
+
if (node.children) {
|
|
215
|
+
let sum = 0
|
|
216
|
+
for (const child of node.children) {
|
|
217
|
+
sum += child.x!
|
|
218
|
+
}
|
|
219
|
+
node.x = sum / node.children.length
|
|
210
220
|
}
|
|
211
|
-
node.x = sum / node.children.length
|
|
212
221
|
}
|
|
213
|
-
assignX(root)
|
|
214
222
|
|
|
215
223
|
const rootHeight = root.height
|
|
216
|
-
|
|
224
|
+
const stack = [{ node: root, depth: 0 }]
|
|
225
|
+
while (stack.length > 0) {
|
|
226
|
+
const { node, depth } = stack.pop()!
|
|
217
227
|
node.y = rootHeight === 0 ? sizeY : (depth / rootHeight) * sizeY
|
|
218
228
|
if (node.children) {
|
|
219
229
|
for (const child of node.children) {
|
|
220
|
-
|
|
230
|
+
stack.push({ node: child, depth: depth + 1 })
|
|
221
231
|
}
|
|
222
232
|
}
|
|
223
233
|
}
|
|
224
|
-
assignY(root, 0)
|
|
225
234
|
}
|
|
226
235
|
|
|
227
236
|
export function collapse<T>(node: HierarchyNode<T>) {
|
|
@@ -236,40 +245,57 @@ export function collapse<T>(node: HierarchyNode<T>) {
|
|
|
236
245
|
// x. Memoizes onto node.depthToLeaf since the layout walks the tree repeatedly.
|
|
237
246
|
// See https://github.com/emmanuelparadis/ape/blob/master/R/plot.phylo.R
|
|
238
247
|
export function calcDepthToLeaf(node: HierarchyNode): number {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
248
|
+
const nodes = descendants(node)
|
|
249
|
+
for (let i = nodes.length - 1; i >= 0; i--) {
|
|
250
|
+
const n = nodes[i]!
|
|
251
|
+
if (n.depthToLeaf === undefined) {
|
|
252
|
+
let maxDepth = 0
|
|
253
|
+
if (n.children) {
|
|
254
|
+
for (const child of n.children) {
|
|
255
|
+
maxDepth = Math.max(maxDepth, 1 + child.depthToLeaf!)
|
|
256
|
+
}
|
|
244
257
|
}
|
|
258
|
+
n.depthToLeaf = maxDepth
|
|
245
259
|
}
|
|
246
|
-
node.depthToLeaf = maxDepth
|
|
247
260
|
}
|
|
248
|
-
return node.depthToLeaf
|
|
261
|
+
return node.depthToLeaf!
|
|
249
262
|
}
|
|
250
263
|
|
|
251
264
|
export function findMaxBranchLen(node: HierarchyNode): number {
|
|
252
|
-
let maxLen =
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
maxLen = Math.max(maxLen, findMaxBranchLen(child))
|
|
256
|
-
}
|
|
265
|
+
let maxLen = 0
|
|
266
|
+
for (const n of descendants(node)) {
|
|
267
|
+
maxLen = Math.max(maxLen, n.len || 0)
|
|
257
268
|
}
|
|
258
269
|
return maxLen
|
|
259
270
|
}
|
|
260
271
|
|
|
272
|
+
// Max root-to-leaf sum of branch lengths within the subtree at d
|
|
261
273
|
export function maxLength(d: HierarchyNode): number {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
274
|
+
const nodes = descendants(d)
|
|
275
|
+
const pathLen = new Map<HierarchyNode, number>()
|
|
276
|
+
for (let i = nodes.length - 1; i >= 0; i--) {
|
|
277
|
+
const n = nodes[i]!
|
|
278
|
+
let childMax = 0
|
|
279
|
+
if (n.children) {
|
|
280
|
+
for (const child of n.children) {
|
|
281
|
+
childMax = Math.max(childMax, pathLen.get(child)!)
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
pathLen.set(n, (n.data.length || 0) + childMax)
|
|
285
|
+
}
|
|
286
|
+
return pathLen.get(d)!
|
|
266
287
|
}
|
|
267
288
|
|
|
268
289
|
export function setBrLength(d: HierarchyNode, y0: number, k: number) {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
290
|
+
const stack = [{ node: d, y0 }]
|
|
291
|
+
while (stack.length > 0) {
|
|
292
|
+
const { node, y0: acc } = stack.pop()!
|
|
293
|
+
const next = acc + Math.max(node.data.length || 0, 0)
|
|
294
|
+
node.len = next * k
|
|
295
|
+
if (node.children) {
|
|
296
|
+
for (const child of node.children) {
|
|
297
|
+
stack.push({ node: child, y0: next })
|
|
298
|
+
}
|
|
273
299
|
}
|
|
274
300
|
}
|
|
275
301
|
}
|
package/src/index.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
// Those plugins also reach into the MsaViewModel instance at runtime; the model
|
|
6
6
|
// members they rely on (e.g. mouseCol/setMousePos, setHighlightedColumns,
|
|
7
7
|
// seqPosToVisibleCol/visibleColToSeqPos) are flagged inline in model.ts.
|
|
8
|
+
export { renderToSvg } from './renderToSvg.tsx'
|
|
8
9
|
export { default as MSAView } from './components/Loading.tsx'
|
|
9
10
|
export { default as MSAViewer } from './components/MSAViewer.tsx'
|
|
10
11
|
export { type MsaViewModel, default as MSAModelF } from './model.ts'
|
|
@@ -73,21 +73,36 @@ async function wait({
|
|
|
73
73
|
signal?: AbortSignal
|
|
74
74
|
}) {
|
|
75
75
|
const url = `${base}/iprscan5/status/${jobId}`
|
|
76
|
+
// EBI jobs run for minutes; cap polling so an unexpected/stuck status can't
|
|
77
|
+
// spin forever (the abort signal is the normal early exit). ~10s per check.
|
|
78
|
+
const maxChecks = 360
|
|
76
79
|
try {
|
|
77
|
-
|
|
78
|
-
|
|
80
|
+
let finished = false
|
|
81
|
+
for (let check = 0; check < maxChecks && !finished; check++) {
|
|
79
82
|
for (let i = 0; i < 10; i++) {
|
|
80
83
|
await timeout(1000, signal)
|
|
81
84
|
onProgress({ msg: `Checking status ${10 - i}`, url })
|
|
82
85
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
86
|
+
// status endpoint returns a bare status token; match exactly rather than
|
|
87
|
+
// substring so an error page mentioning "FINISHED" can't be a false hit
|
|
88
|
+
const status = (await textfetch(url, { signal })).trim()
|
|
89
|
+
if (status === 'FINISHED') {
|
|
90
|
+
finished = true
|
|
91
|
+
} else if (
|
|
92
|
+
status === 'FAILURE' ||
|
|
93
|
+
status === 'ERROR' ||
|
|
94
|
+
status === 'NOT_FOUND'
|
|
95
|
+
) {
|
|
96
|
+
throw new Error(
|
|
97
|
+
`InterProScan job ${jobId} ended with status: ${status}`,
|
|
98
|
+
)
|
|
89
99
|
}
|
|
90
100
|
}
|
|
101
|
+
if (!finished) {
|
|
102
|
+
throw new Error(
|
|
103
|
+
`InterProScan job ${jobId} did not finish after ${maxChecks} status checks`,
|
|
104
|
+
)
|
|
105
|
+
}
|
|
91
106
|
} finally {
|
|
92
107
|
onProgress()
|
|
93
108
|
}
|
|
@@ -152,10 +167,17 @@ async function loadInterProScanResultsWithStatus({
|
|
|
152
167
|
url: `https://www.ebi.ac.uk/Tools/services/rest/iprscan5/result/${jobId}/json`,
|
|
153
168
|
})
|
|
154
169
|
const ret = await loadInterProScanResults(jobId, signal)
|
|
155
|
-
|
|
156
|
-
|
|
170
|
+
// key each result by its xref id; skip any result lacking an xref entry
|
|
171
|
+
// rather than crashing the whole load on one malformed result
|
|
172
|
+
model.setDomains(
|
|
173
|
+
Object.fromEntries(
|
|
174
|
+
ret.results
|
|
175
|
+
.map(r => [r.xref[0]?.id, r] as const)
|
|
176
|
+
.filter(
|
|
177
|
+
(e): e is [string, InterProScanResults] => e[0] !== undefined,
|
|
178
|
+
),
|
|
179
|
+
),
|
|
157
180
|
)
|
|
158
|
-
model.setShowDomains(true)
|
|
159
181
|
getSession(model).notify(`Loaded interproscan ${jobId} results`, 'success')
|
|
160
182
|
} catch (e) {
|
|
161
183
|
if (isAbortError(e)) {
|
package/src/measureTextCanvas.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { setFontSize } from './setFontSize.ts'
|
|
2
|
+
|
|
1
3
|
let canvasHandle: HTMLCanvasElement | undefined
|
|
2
4
|
|
|
3
5
|
export function measureTextCanvas(text: string, fontSize: number) {
|
|
@@ -9,6 +11,6 @@ export function measureTextCanvas(text: string, fontSize: number) {
|
|
|
9
11
|
if (!ctx) {
|
|
10
12
|
throw new Error('no canvas context')
|
|
11
13
|
}
|
|
12
|
-
ctx
|
|
14
|
+
setFontSize(ctx, fontSize)
|
|
13
15
|
return ctx.measureText(text).width
|
|
14
16
|
}
|