react-msaview 3.1.11 → 3.2.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.
Files changed (191) hide show
  1. package/bundle/index.js +32 -31
  2. package/dist/colorSchemes.d.ts +2 -2
  3. package/dist/colorSchemes.js +3 -4
  4. package/dist/colorSchemes.js.map +1 -1
  5. package/dist/components/Loading.d.ts +1 -1
  6. package/dist/components/Loading.js +4 -4
  7. package/dist/components/Loading.js.map +1 -1
  8. package/dist/components/MSAView.d.ts +1 -1
  9. package/dist/components/MSAView.js +13 -9
  10. package/dist/components/MSAView.js.map +1 -1
  11. package/dist/components/ResizeHandles.d.ts +1 -1
  12. package/dist/components/ResizeHandles.js +2 -2
  13. package/dist/components/SequenceTextArea.js +4 -0
  14. package/dist/components/SequenceTextArea.js.map +1 -1
  15. package/dist/components/TextTrack.d.ts +1 -1
  16. package/dist/components/Track.d.ts +1 -1
  17. package/dist/components/VerticalScrollbar.d.ts +6 -0
  18. package/dist/components/VerticalScrollbar.js +65 -0
  19. package/dist/components/VerticalScrollbar.js.map +1 -0
  20. package/dist/components/dialogs/AddTrackDialog.d.ts +1 -1
  21. package/dist/components/dialogs/DomainDialog.d.ts +6 -0
  22. package/dist/components/dialogs/DomainDialog.js +19 -0
  23. package/dist/components/dialogs/DomainDialog.js.map +1 -0
  24. package/dist/components/dialogs/ExportSVGDialog.d.ts +1 -1
  25. package/dist/components/dialogs/FeatureDialog.d.ts +1 -1
  26. package/dist/components/dialogs/FeatureDialog.js.map +1 -1
  27. package/dist/components/dialogs/InterProScanDialog.d.ts +4 -4
  28. package/dist/components/dialogs/InterProScanDialog.js +37 -8
  29. package/dist/components/dialogs/InterProScanDialog.js.map +1 -1
  30. package/dist/components/dialogs/MetadataDialog.d.ts +1 -1
  31. package/dist/components/dialogs/SettingsDialog.d.ts +1 -1
  32. package/dist/components/dialogs/SettingsDialog.js +10 -1
  33. package/dist/components/dialogs/SettingsDialog.js.map +1 -1
  34. package/dist/components/dialogs/TabPanel.d.ts +6 -0
  35. package/dist/components/dialogs/TabPanel.js +6 -0
  36. package/dist/components/dialogs/TabPanel.js.map +1 -0
  37. package/dist/components/dialogs/TracklistDialog.d.ts +1 -1
  38. package/dist/components/dialogs/UserProvidedDomainsDialog.d.ts +7 -0
  39. package/dist/components/dialogs/UserProvidedDomainsDialog.js +58 -0
  40. package/dist/components/dialogs/UserProvidedDomainsDialog.js.map +1 -0
  41. package/dist/components/header/Header.d.ts +1 -1
  42. package/dist/components/header/Header.js +8 -5
  43. package/dist/components/header/Header.js.map +1 -1
  44. package/dist/components/header/HeaderInfoArea.d.ts +1 -1
  45. package/dist/components/header/HeaderMenu.d.ts +1 -1
  46. package/dist/components/header/HeaderMenuExtra.d.ts +1 -1
  47. package/dist/components/header/HeaderMenuExtra.js +54 -41
  48. package/dist/components/header/HeaderMenuExtra.js.map +1 -1
  49. package/dist/components/header/HeaderStatusArea.d.ts +1 -1
  50. package/dist/components/header/HeaderStatusArea.js +2 -1
  51. package/dist/components/header/HeaderStatusArea.js.map +1 -1
  52. package/dist/components/header/MultiAlignmentSelector.d.ts +1 -1
  53. package/dist/components/header/ZoomControls.js +31 -1
  54. package/dist/components/header/ZoomControls.js.map +1 -1
  55. package/dist/components/import/ImportForm.d.ts +1 -1
  56. package/dist/components/import/ImportForm.js +1 -1
  57. package/dist/components/import/ImportForm.js.map +1 -1
  58. package/dist/components/import/ImportFormExamples.d.ts +1 -1
  59. package/dist/components/import/ImportFormExamples.js +10 -8
  60. package/dist/components/import/ImportFormExamples.js.map +1 -1
  61. package/dist/components/import/util.d.ts +2 -2
  62. package/dist/components/minimap/Minimap.d.ts +1 -1
  63. package/dist/components/minimap/Minimap.js +14 -15
  64. package/dist/components/minimap/Minimap.js.map +1 -1
  65. package/dist/components/minimap/MinimapSVG.d.ts +1 -1
  66. package/dist/components/minimap/MinimapSVG.js +1 -1
  67. package/dist/components/minimap/MinimapSVG.js.map +1 -1
  68. package/dist/components/msa/MSACanvas.d.ts +1 -1
  69. package/dist/components/msa/MSACanvas.js +3 -3
  70. package/dist/components/msa/MSACanvas.js.map +1 -1
  71. package/dist/components/msa/MSACanvasBlock.d.ts +3 -3
  72. package/dist/components/msa/MSACanvasBlock.js +4 -3
  73. package/dist/components/msa/MSACanvasBlock.js.map +1 -1
  74. package/dist/components/msa/MSAMouseoverCanvas.d.ts +2 -2
  75. package/dist/components/msa/MSAMouseoverCanvas.js +1 -1
  76. package/dist/components/msa/MSAMouseoverCanvas.js.map +1 -1
  77. package/dist/components/msa/MSAPanel.d.ts +1 -1
  78. package/dist/components/msa/renderBoxFeatureCanvasBlock.d.ts +1 -1
  79. package/dist/components/msa/renderBoxFeatureCanvasBlock.js +2 -3
  80. package/dist/components/msa/renderBoxFeatureCanvasBlock.js.map +1 -1
  81. package/dist/components/msa/renderMSABlock.d.ts +2 -2
  82. package/dist/components/msa/renderMSABlock.js +12 -12
  83. package/dist/components/msa/renderMSABlock.js.map +1 -1
  84. package/dist/components/msa/renderMSAMouseover.d.ts +1 -1
  85. package/dist/components/tree/TreeBranchMenu.d.ts +1 -1
  86. package/dist/components/tree/TreeCanvas.d.ts +1 -1
  87. package/dist/components/tree/TreeCanvas.js +13 -12
  88. package/dist/components/tree/TreeCanvas.js.map +1 -1
  89. package/dist/components/tree/TreeCanvasBlock.d.ts +1 -1
  90. package/dist/components/tree/TreeCanvasBlock.js +2 -1
  91. package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
  92. package/dist/components/tree/TreeNodeMenu.d.ts +1 -1
  93. package/dist/components/tree/TreeNodeMenu.js +2 -2
  94. package/dist/components/tree/TreeNodeMenu.js.map +1 -1
  95. package/dist/components/tree/TreePanel.d.ts +1 -1
  96. package/dist/components/tree/TreeRuler.d.ts +1 -1
  97. package/dist/components/tree/dialogs/TreeNodeInfoDialog.d.ts +1 -1
  98. package/dist/components/tree/renderTreeCanvas.d.ts +3 -3
  99. package/dist/components/tree/renderTreeCanvas.js +25 -9
  100. package/dist/components/tree/renderTreeCanvas.js.map +1 -1
  101. package/dist/components/util.js +1 -1
  102. package/dist/components/util.js.map +1 -1
  103. package/dist/fetchUtils.d.ts +1 -1
  104. package/dist/fetchUtils.js.map +1 -1
  105. package/dist/launchInterProScan.d.ts +9 -3
  106. package/dist/launchInterProScan.js +57 -22
  107. package/dist/launchInterProScan.js.map +1 -1
  108. package/dist/model/DataModel.d.ts +5 -1
  109. package/dist/model/DataModel.js +10 -1
  110. package/dist/model/DataModel.js.map +1 -1
  111. package/dist/model/DialogQueue.d.ts +1 -1
  112. package/dist/model.d.ts +138 -43
  113. package/dist/model.js +235 -110
  114. package/dist/model.js.map +1 -1
  115. package/dist/parseNewick.js +1 -1
  116. package/dist/parseNewick.js.map +1 -1
  117. package/dist/parsers/ClustalMSA.d.ts +1 -1
  118. package/dist/parsers/FastaMSA.d.ts +1 -1
  119. package/dist/parsers/StockholmMSA.d.ts +1 -1
  120. package/dist/parsers/StockholmMSA.js.map +1 -1
  121. package/dist/renderToSvg.d.ts +2 -2
  122. package/dist/renderToSvg.js +3 -28
  123. package/dist/renderToSvg.js.map +1 -1
  124. package/dist/reparseTree.d.ts +1 -1
  125. package/dist/util.d.ts +2 -2
  126. package/dist/util.js +0 -2
  127. package/dist/util.js.map +1 -1
  128. package/dist/version.d.ts +1 -1
  129. package/dist/version.js +1 -1
  130. package/dist/version.js.map +1 -1
  131. package/package.json +5 -2
  132. package/src/colorSchemes.ts +3 -2
  133. package/src/components/Checkbox2.tsx +1 -1
  134. package/src/components/Loading.tsx +11 -5
  135. package/src/components/MSAView.tsx +27 -18
  136. package/src/components/ResizeHandles.tsx +3 -3
  137. package/src/components/SequenceTextArea.tsx +8 -0
  138. package/src/components/TextTrack.tsx +1 -1
  139. package/src/components/Track.tsx +1 -1
  140. package/src/components/VerticalScrollbar.tsx +85 -0
  141. package/src/components/dialogs/AddTrackDialog.tsx +2 -2
  142. package/src/components/dialogs/DomainDialog.tsx +38 -0
  143. package/src/components/dialogs/ExportSVGDialog.tsx +1 -1
  144. package/src/components/dialogs/FeatureDialog.tsx +3 -3
  145. package/src/components/dialogs/InterProScanDialog.tsx +49 -11
  146. package/src/components/dialogs/MetadataDialog.tsx +1 -1
  147. package/src/components/dialogs/SettingsDialog.tsx +38 -3
  148. package/src/components/dialogs/TabPanel.tsx +19 -0
  149. package/src/components/dialogs/TracklistDialog.tsx +1 -1
  150. package/src/components/dialogs/UserProvidedDomainsDialog.tsx +133 -0
  151. package/src/components/header/Header.tsx +9 -6
  152. package/src/components/header/HeaderInfoArea.tsx +1 -1
  153. package/src/components/header/HeaderMenu.tsx +1 -1
  154. package/src/components/header/HeaderMenuExtra.tsx +65 -48
  155. package/src/components/header/HeaderStatusArea.tsx +3 -2
  156. package/src/components/header/MultiAlignmentSelector.tsx +1 -1
  157. package/src/components/header/ZoomControls.tsx +34 -0
  158. package/src/components/import/ImportForm.tsx +3 -3
  159. package/src/components/import/ImportFormExamples.tsx +19 -17
  160. package/src/components/import/util.ts +2 -2
  161. package/src/components/minimap/Minimap.tsx +15 -22
  162. package/src/components/minimap/MinimapSVG.tsx +2 -2
  163. package/src/components/msa/MSACanvas.tsx +11 -4
  164. package/src/components/msa/MSACanvasBlock.tsx +5 -4
  165. package/src/components/msa/MSAMouseoverCanvas.tsx +2 -6
  166. package/src/components/msa/MSAPanel.tsx +1 -1
  167. package/src/components/msa/renderBoxFeatureCanvasBlock.ts +5 -6
  168. package/src/components/msa/renderMSABlock.ts +37 -17
  169. package/src/components/msa/renderMSAMouseover.ts +1 -1
  170. package/src/components/tree/TreeBranchMenu.tsx +1 -1
  171. package/src/components/tree/TreeCanvas.tsx +15 -16
  172. package/src/components/tree/TreeCanvasBlock.tsx +3 -2
  173. package/src/components/tree/TreeNodeMenu.tsx +3 -3
  174. package/src/components/tree/TreePanel.tsx +1 -1
  175. package/src/components/tree/TreeRuler.tsx +1 -1
  176. package/src/components/tree/dialogs/TreeNodeInfoDialog.tsx +1 -1
  177. package/src/components/tree/renderTreeCanvas.ts +32 -12
  178. package/src/components/util.ts +1 -1
  179. package/src/fetchUtils.ts +2 -2
  180. package/src/launchInterProScan.ts +69 -24
  181. package/src/model/DataModel.ts +10 -0
  182. package/src/model/DialogQueue.ts +1 -1
  183. package/src/model.ts +262 -143
  184. package/src/parseNewick.ts +1 -1
  185. package/src/parsers/ClustalMSA.ts +1 -1
  186. package/src/parsers/FastaMSA.ts +1 -1
  187. package/src/parsers/StockholmMSA.ts +1 -1
  188. package/src/renderToSvg.tsx +6 -30
  189. package/src/reparseTree.ts +1 -1
  190. package/src/util.ts +2 -4
  191. package/src/version.ts +1 -1
package/dist/model.js CHANGED
@@ -4,10 +4,14 @@ import { hierarchy, cluster } from 'd3-hierarchy';
4
4
  import { ascending } from 'd3-array';
5
5
  import Stockholm from 'stockholm-js';
6
6
  import { saveAs } from 'file-saver';
7
+ import { ungzip } from 'pako';
7
8
  // jbrowse
8
9
  import { FileLocation, ElementId } from '@jbrowse/core/util/types/mst';
9
10
  import { openLocation } from '@jbrowse/core/util/io';
10
- import { groupBy, localStorageGetItem, localStorageSetItem, notEmpty, sum, } from '@jbrowse/core/util';
11
+ import { groupBy, notEmpty, sum } from '@jbrowse/core/util';
12
+ export function isGzip(buf) {
13
+ return buf[0] === 31 && buf[1] === 139 && buf[2] === 8;
14
+ }
11
15
  // locals
12
16
  import { clamp, collapse, generateNodeIds, maxLength, setBrLength, skipBlanks, len, } from './util';
13
17
  import { colord } from 'colord';
@@ -28,7 +32,6 @@ import { DataModelF } from './model/DataModel';
28
32
  import { DialogQueueSessionMixin } from './model/DialogQueue';
29
33
  import { TreeF } from './model/treeModel';
30
34
  import { MSAModelF } from './model/msaModel';
31
- import { launchInterProScan, loadInterProScanResults, } from './launchInterProScan';
32
35
  /**
33
36
  * #stateModel MsaView
34
37
  * extends
@@ -47,7 +50,15 @@ function stateModelFactory() {
47
50
  /**
48
51
  * #property
49
52
  */
50
- featureMode: false,
53
+ showDomains: false,
54
+ /**
55
+ * #property
56
+ */
57
+ allowedGappyness: 100,
58
+ /**
59
+ * #property
60
+ */
61
+ contrastLettering: true,
51
62
  /**
52
63
  * #property
53
64
  */
@@ -57,6 +68,18 @@ function stateModelFactory() {
57
68
  * hardcoded view type
58
69
  */
59
70
  type: types.literal('MsaView'),
71
+ /**
72
+ * #property
73
+ */
74
+ drawMsaLetters: true,
75
+ /**
76
+ * #property
77
+ */
78
+ hideGaps: true,
79
+ /**
80
+ * #property
81
+ */
82
+ drawTreeText: true,
60
83
  /**
61
84
  * #property
62
85
  * height of the div containing the view, px
@@ -105,14 +128,16 @@ function stateModelFactory() {
105
128
  currentAlignment: 0,
106
129
  /**
107
130
  * #property
108
- * array of tree parent nodes that are 'collapsed'
131
+ * array of tree parent nodes that are 'collapsed' (all children are
132
+ * hidden)
109
133
  */
110
134
  collapsed: types.array(types.string),
111
135
  /**
112
136
  * #property
113
- * array of tree leaf nodes that are 'collapsed'
137
+ * array of tree leaf nodes that are 'collapsed' (just that leaf node
138
+ * is hidden)
114
139
  */
115
- collapsed2: types.array(types.string),
140
+ collapsedLeaves: types.array(types.string),
116
141
  /**
117
142
  * #property
118
143
  * focus on particular subtree
@@ -135,6 +160,13 @@ function stateModelFactory() {
135
160
  featureFilters: types.map(types.boolean),
136
161
  }))
137
162
  .volatile(() => ({
163
+ /**
164
+ * #volatile
165
+ */
166
+ headerHeight: 0,
167
+ /**
168
+ * #volatile
169
+ */
138
170
  status: undefined,
139
171
  /**
140
172
  * #volatile
@@ -153,7 +185,7 @@ function stateModelFactory() {
153
185
  /**
154
186
  * #volatile
155
187
  */
156
- width: 800,
188
+ volatileWidth: undefined,
157
189
  /**
158
190
  * #volatile
159
191
  * resize handle width between tree and msa area, px
@@ -163,7 +195,7 @@ function stateModelFactory() {
163
195
  * #volatile
164
196
  * size of blocks of content to be drawn, px
165
197
  */
166
- blockSize: 1000,
198
+ blockSize: 500,
167
199
  /**
168
200
  * #volatile
169
201
  * the currently mouse-hovered row
@@ -210,13 +242,27 @@ function stateModelFactory() {
210
242
  * #volatile
211
243
  *
212
244
  */
213
- loadedInterProAnnotations: undefined,
214
- /**
215
- * #volatile
216
- */
217
- interProScanJobIds: JSON.parse(localStorageGetItem('msaview-interproscanqueries') || '[]'),
245
+ interProAnnotations: undefined,
218
246
  }))
219
247
  .actions(self => ({
248
+ /**
249
+ * #action
250
+ */
251
+ setHideGaps(arg) {
252
+ self.hideGaps = arg;
253
+ },
254
+ /**
255
+ * #action
256
+ */
257
+ setAllowedGappyness(arg) {
258
+ self.allowedGappyness = arg;
259
+ },
260
+ /**
261
+ * #action
262
+ */
263
+ setContrastLettering(arg) {
264
+ self.contrastLettering = arg;
265
+ },
220
266
  /**
221
267
  * #action
222
268
  */
@@ -233,7 +279,7 @@ function stateModelFactory() {
233
279
  * #action
234
280
  */
235
281
  setWidth(arg) {
236
- self.width = arg;
282
+ self.volatileWidth = arg;
237
283
  },
238
284
  /**
239
285
  * #action
@@ -260,8 +306,8 @@ function stateModelFactory() {
260
306
  /**
261
307
  * #action
262
308
  */
263
- setFeatureMode(arg) {
264
- self.featureMode = arg;
309
+ setShowDomains(arg) {
310
+ self.showDomains = arg;
265
311
  },
266
312
  /**
267
313
  * #action
@@ -320,11 +366,11 @@ function stateModelFactory() {
320
366
  * #action
321
367
  */
322
368
  toggleCollapsed2(node) {
323
- if (self.collapsed2.includes(node)) {
324
- self.collapsed2.remove(node);
369
+ if (self.collapsedLeaves.includes(node)) {
370
+ self.collapsedLeaves.remove(node);
325
371
  }
326
372
  else {
327
- self.collapsed2.push(node);
373
+ self.collapsedLeaves.push(node);
328
374
  }
329
375
  },
330
376
  /**
@@ -369,6 +415,29 @@ function stateModelFactory() {
369
415
  setTreeMetadata(result) {
370
416
  self.data.setTreeMetadata(result);
371
417
  },
418
+ }))
419
+ .views(self => ({
420
+ /**
421
+ * #getter
422
+ */
423
+ get actuallyShowDomains() {
424
+ return self.showDomains && !!self.interProAnnotations;
425
+ },
426
+ /**
427
+ * #getter
428
+ */
429
+ get viewInitialized() {
430
+ return self.volatileWidth !== undefined;
431
+ },
432
+ /**
433
+ * #getter
434
+ */
435
+ get width() {
436
+ if (self.volatileWidth === undefined) {
437
+ throw new Error('not initialized');
438
+ }
439
+ return self.volatileWidth;
440
+ },
372
441
  }))
373
442
  .views(self => ({
374
443
  /**
@@ -408,13 +477,13 @@ function stateModelFactory() {
408
477
  * #getter
409
478
  */
410
479
  get noTree() {
411
- return !!this._tree.noTree;
480
+ return !!this.tree.noTree;
412
481
  },
413
482
  /**
414
483
  * #getter
415
484
  */
416
- get noAnnotations() {
417
- return !self.loadedInterProAnnotations;
485
+ get noDomains() {
486
+ return !self.interProAnnotations;
418
487
  },
419
488
  /**
420
489
  * #getter
@@ -437,12 +506,10 @@ function stateModelFactory() {
437
506
  if (Stockholm.sniff(text)) {
438
507
  return new StockholmMSA(text, self.currentAlignment);
439
508
  }
440
- else if (text.startsWith('>')) {
509
+ if (text.startsWith('>')) {
441
510
  return new FastaMSA(text);
442
511
  }
443
- else {
444
- return new ClustalMSA(text);
445
- }
512
+ return new ClustalMSA(text);
446
513
  }
447
514
  return null;
448
515
  },
@@ -455,7 +522,7 @@ function stateModelFactory() {
455
522
  /**
456
523
  * #getter
457
524
  */
458
- get _tree() {
525
+ get tree() {
459
526
  const ret = self.data.tree
460
527
  ? generateNodeIds(parseNewick(self.data.tree))
461
528
  : this.MSA?.getTree() || {
@@ -470,7 +537,7 @@ function stateModelFactory() {
470
537
  * #getter
471
538
  */
472
539
  get rowNames() {
473
- return this.hierarchy.leaves().map(n => n.data.name);
540
+ return this.leaves.map(n => n.data.name);
474
541
  },
475
542
  /**
476
543
  * #getter
@@ -483,7 +550,7 @@ function stateModelFactory() {
483
550
  * #getter
484
551
  */
485
552
  get root() {
486
- let hier = hierarchy(this._tree, d => d.branchset)
553
+ let hier = hierarchy(this.tree, d => d.branchset)
487
554
  .sum(d => (d.branchset ? 0 : 1))
488
555
  .sort((a, b) => ascending(a.data.length || 1, b.data.length || 1));
489
556
  if (self.showOnly) {
@@ -493,7 +560,7 @@ function stateModelFactory() {
493
560
  }
494
561
  }
495
562
  ;
496
- [...self.collapsed, ...self.collapsed2]
563
+ [...self.collapsed, ...self.collapsedLeaves]
497
564
  .map(collapsedId => hier.find(node => node.data.id === collapsedId))
498
565
  .filter(notEmpty)
499
566
  .map(node => collapse(node));
@@ -516,9 +583,9 @@ function stateModelFactory() {
516
583
  * #getter
517
584
  */
518
585
  get blanks() {
586
+ const { allowedGappyness } = self;
519
587
  const blanks = [];
520
- const strs = this.hierarchy
521
- .leaves()
588
+ const strs = this.leaves
522
589
  .map(leaf => this.MSA?.getRow(leaf.data.name))
523
590
  .filter((item) => !!item);
524
591
  for (let i = 0; i < strs[0]?.length; i++) {
@@ -528,7 +595,7 @@ function stateModelFactory() {
528
595
  counter++;
529
596
  }
530
597
  }
531
- if (counter === strs.length) {
598
+ if (counter / strs.length >= allowedGappyness / 100) {
532
599
  blanks.push(i);
533
600
  }
534
601
  }
@@ -539,8 +606,7 @@ function stateModelFactory() {
539
606
  */
540
607
  get rows() {
541
608
  const MSA = this.MSA;
542
- return this.hierarchy
543
- .leaves()
609
+ return this.leaves
544
610
  .map(leaf => [leaf.data.name, MSA?.getRow(leaf.data.name)])
545
611
  .filter((f) => !!f[1]);
546
612
  },
@@ -580,6 +646,14 @@ function stateModelFactory() {
580
646
  }
581
647
  return r;
582
648
  },
649
+ /**
650
+ * #getter
651
+ */
652
+ get colStatsSums() {
653
+ return Object.fromEntries(Object.entries(this.colStats).map(([key, val]) => {
654
+ return [key, sum(Object.values(val))];
655
+ }));
656
+ },
583
657
  /**
584
658
  * #getter
585
659
  * generates a new tree that is clustered with x,y positions
@@ -599,6 +673,12 @@ function stateModelFactory() {
599
673
  get totalHeight() {
600
674
  return this.root.leaves().length * self.rowHeight;
601
675
  },
676
+ /**
677
+ * #getter
678
+ */
679
+ get leaves() {
680
+ return this.hierarchy.leaves();
681
+ },
602
682
  }))
603
683
  .views(self => ({
604
684
  /**
@@ -612,7 +692,7 @@ function stateModelFactory() {
612
692
  /**
613
693
  * #getter
614
694
  */
615
- get initialized() {
695
+ get dataInitialized() {
616
696
  return (self.data.msa || self.data.tree) && !self.error;
617
697
  },
618
698
  /**
@@ -663,29 +743,77 @@ function stateModelFactory() {
663
743
  get maxScrollX() {
664
744
  return -self.totalWidth + (self.msaAreaWidth - 100);
665
745
  },
746
+ /**
747
+ * #getter
748
+ */
749
+ get showMsaLetters() {
750
+ return self.drawMsaLetters && self.rowHeight >= 5;
751
+ },
752
+ /**
753
+ * #getter
754
+ */
755
+ get showTreeText() {
756
+ return self.drawLabels && self.rowHeight >= 5;
757
+ },
666
758
  }))
667
759
  .actions(self => ({
668
760
  /**
669
761
  * #action
670
762
  */
671
- zoomIn() {
763
+ setDrawMsaLetters(arg) {
764
+ self.drawMsaLetters = arg;
765
+ },
766
+ /**
767
+ * #action
768
+ */
769
+ zoomOutHorizontal() {
770
+ self.colWidth = Math.max(1, Math.floor(self.colWidth * 0.75));
771
+ self.scrollX = clamp(self.maxScrollX, self.scrollX, 0);
772
+ },
773
+ /**
774
+ * #action
775
+ */
776
+ zoomInHorizontal() {
672
777
  self.colWidth = Math.ceil(self.colWidth * 1.5);
673
- self.rowHeight = Math.ceil(self.rowHeight * 1.5);
674
778
  self.scrollX = clamp(self.maxScrollX, self.scrollX, 0);
675
779
  },
676
780
  /**
677
781
  * #action
678
782
  */
679
- zoomOut() {
680
- self.colWidth = Math.max(1, Math.floor(self.colWidth * 0.75));
783
+ zoomInVertical() {
784
+ self.rowHeight = Math.ceil(self.rowHeight * 1.5);
785
+ },
786
+ /**
787
+ * #action
788
+ */
789
+ zoomOutVertical() {
681
790
  self.rowHeight = Math.max(1.5, Math.floor(self.rowHeight * 0.75));
682
- self.scrollX = clamp(self.maxScrollX, self.scrollX, 0);
683
791
  },
684
792
  /**
685
793
  * #action
686
794
  */
687
- setLoadedInterProAnnotations(data) {
688
- self.loadedInterProAnnotations = data;
795
+ zoomIn() {
796
+ transaction(() => {
797
+ self.colWidth = Math.ceil(self.colWidth * 1.5);
798
+ self.rowHeight = Math.ceil(self.rowHeight * 1.5);
799
+ self.scrollX = clamp(self.maxScrollX, self.scrollX, 0);
800
+ });
801
+ },
802
+ /**
803
+ * #action
804
+ */
805
+ zoomOut() {
806
+ transaction(() => {
807
+ self.colWidth = Math.max(1, Math.floor(self.colWidth * 0.75));
808
+ self.rowHeight = Math.max(1.5, Math.floor(self.rowHeight * 0.75));
809
+ self.scrollX = clamp(self.maxScrollX, self.scrollX, 0);
810
+ });
811
+ },
812
+ /**
813
+ * #action
814
+ */
815
+ setInterProAnnotations(data) {
816
+ self.interProAnnotations = data;
689
817
  },
690
818
  /**
691
819
  * #action
@@ -716,15 +844,6 @@ function stateModelFactory() {
716
844
  self.turnedOffTracks.set(id, true);
717
845
  }
718
846
  },
719
- /**
720
- * #action
721
- */
722
- addInterProScanJobId(arg) {
723
- self.interProScanJobIds = [
724
- ...self.interProScanJobIds,
725
- { jobId: arg, date: +Date.now() },
726
- ];
727
- },
728
847
  /**
729
848
  * #action
730
849
  */
@@ -738,9 +857,9 @@ function stateModelFactory() {
738
857
  */
739
858
  get labelsWidth() {
740
859
  let x = 0;
741
- const { rowHeight, hierarchy, treeMetadata, fontSize } = self;
860
+ const { rowHeight, leaves, treeMetadata, fontSize } = self;
742
861
  if (rowHeight > 5) {
743
- for (const node of hierarchy.leaves()) {
862
+ for (const node of leaves) {
744
863
  x = Math.max(measureTextCanvas(treeMetadata[node.data.name]?.genome || node.data.name, fontSize), x);
745
864
  }
746
865
  }
@@ -797,6 +916,12 @@ function stateModelFactory() {
797
916
  get turnedOnTracks() {
798
917
  return this.tracks.filter(f => !self.turnedOffTracks.has(f.model.id));
799
918
  },
919
+ /**
920
+ * #getter
921
+ */
922
+ get showHorizontalScrollbar() {
923
+ return self.msaAreaWidth < self.totalWidth;
924
+ },
800
925
  /**
801
926
  * #method
802
927
  * return a row-specific sequence coordinate, skipping gaps, given a global
@@ -846,6 +971,15 @@ function stateModelFactory() {
846
971
  },
847
972
  }))
848
973
  .views(self => ({
974
+ /**
975
+ * #getter
976
+ * widget width minus the tree area gives the space for the MSA
977
+ */
978
+ get msaAreaHeight() {
979
+ return (self.height -
980
+ (self.showHorizontalScrollbar ? self.minimapHeight : 0) -
981
+ self.headerHeight);
982
+ },
849
983
  /**
850
984
  * #getter
851
985
  * total height of track area (px)
@@ -865,11 +999,14 @@ function stateModelFactory() {
865
999
  }
866
1000
  return types;
867
1001
  },
1002
+ /**
1003
+ * #getter
1004
+ */
868
1005
  get tidyAnnotations() {
869
1006
  const ret = [];
870
- const { loadedInterProAnnotations } = self;
871
- if (loadedInterProAnnotations) {
872
- for (const [id, val] of Object.entries(loadedInterProAnnotations)) {
1007
+ const { interProAnnotations } = self;
1008
+ if (interProAnnotations) {
1009
+ for (const [id, val] of Object.entries(interProAnnotations)) {
873
1010
  for (const { signature, locations } of val.matches) {
874
1011
  const { entry } = signature;
875
1012
  if (entry) {
@@ -904,6 +1041,23 @@ function stateModelFactory() {
904
1041
  },
905
1042
  }))
906
1043
  .views(self => ({
1044
+ /**
1045
+ * #getter
1046
+ */
1047
+ get showVerticalScrollbar() {
1048
+ return self.msaAreaHeight < self.totalHeight;
1049
+ },
1050
+ }))
1051
+ .views(self => ({
1052
+ /**
1053
+ * #getter
1054
+ */
1055
+ get verticalScrollbarWidth() {
1056
+ return self.showVerticalScrollbar ? 20 : 0;
1057
+ },
1058
+ /**
1059
+ * #getter
1060
+ */
907
1061
  get fillPalette() {
908
1062
  const arr = [...self.tidyTypes.keys()];
909
1063
  let i = 0;
@@ -915,6 +1069,9 @@ function stateModelFactory() {
915
1069
  }
916
1070
  return map;
917
1071
  },
1072
+ /**
1073
+ * #getter
1074
+ */
918
1075
  get strokePalette() {
919
1076
  return Object.fromEntries(Object.entries(this.fillPalette).map(([key, val]) => [
920
1077
  key,
@@ -926,43 +1083,20 @@ function stateModelFactory() {
926
1083
  /**
927
1084
  * #action
928
1085
  */
929
- async loadInterProScanResults(jobId) {
930
- self.setStatus({ msg: 'Loading ' + jobId });
931
- const ret = await loadInterProScanResults(jobId);
932
- self.setStatus();
933
- self.setLoadedInterProAnnotations(Object.fromEntries(ret.results.map(r => [r.xref[0].id, r])));
934
- },
935
- /**
936
- * #action
937
- */
938
- async queryInterProScan(programs) {
939
- const { rows } = self;
940
- if (rows.length > 140) {
941
- throw new Error('Too many sequences, please run InterProScan offline');
942
- }
943
- const ret = await launchInterProScan({
944
- algorithm: 'interproscan',
945
- programs: programs,
946
- seq: rows
947
- .map(row => `>${row[0]}\n${row[1].replaceAll('-', '')}`)
948
- .join('\n'),
949
- onProgress: arg => self.setStatus(arg),
950
- onJobId: jobId => self.addInterProScanJobId(jobId),
951
- });
952
- self.setLoadedInterProAnnotations(Object.fromEntries(ret.results.map(r => [r.xref[0].id, r])));
1086
+ setHeaderHeight(arg) {
1087
+ self.headerHeight = arg;
953
1088
  },
954
1089
  /**
955
1090
  * #action
956
1091
  */
957
1092
  reset() {
958
- transaction(() => {
959
- self.setData({ tree: '', msa: '' });
960
- self.setScrollY(0);
961
- self.setScrollX(0);
962
- self.setCurrentAlignment(0);
963
- self.setTreeFilehandle(undefined);
964
- self.setMSAFilehandle(undefined);
965
- });
1093
+ self.setData({ tree: '', msa: '' });
1094
+ self.setError(undefined);
1095
+ self.setScrollY(0);
1096
+ self.setScrollX(0);
1097
+ self.setCurrentAlignment(0);
1098
+ self.setTreeFilehandle(undefined);
1099
+ self.setMSAFilehandle(undefined);
966
1100
  },
967
1101
  /**
968
1102
  * #action
@@ -1001,22 +1135,6 @@ function stateModelFactory() {
1001
1135
  this.initFilter(key);
1002
1136
  }
1003
1137
  }));
1004
- addDisposer(self, autorun(async () => {
1005
- // const res = Object.fromEntries(
1006
- // await Promise.all(
1007
- // self.annotationTracks.map(async f => {
1008
- // const d = await jsonfetch(
1009
- // `https://jbrowse.org/demos/interproscan/json/${encodeURIComponent(f)}`,
1010
- // )
1011
- // return [
1012
- // decodeURIComponent(f).replace('.json', ''),
1013
- // d.result.results[0],
1014
- // ] as const
1015
- // }),
1016
- // ),
1017
- // )
1018
- // self.setLoadedInterProAnnotations(res)
1019
- }));
1020
1138
  // autorun opens treeFilehandle
1021
1139
  addDisposer(self, autorun(async () => {
1022
1140
  const { treeFilehandle } = self;
@@ -1057,9 +1175,11 @@ function stateModelFactory() {
1057
1175
  if (msaFilehandle) {
1058
1176
  try {
1059
1177
  self.setLoadingMSA(true);
1060
- const res = await openLocation(msaFilehandle).readFile('utf8');
1178
+ const res = await openLocation(msaFilehandle).readFile();
1179
+ const buf = isGzip(res) ? ungzip(res) : res;
1180
+ const txt = new TextDecoder('utf8').decode(buf);
1061
1181
  transaction(() => {
1062
- self.setMSA(res);
1182
+ self.setMSA(txt);
1063
1183
  if (msaFilehandle.locationType === 'BlobLocation') {
1064
1184
  // clear filehandle after loading if from a local file
1065
1185
  self.setMSAFilehandle(undefined);
@@ -1075,15 +1195,20 @@ function stateModelFactory() {
1075
1195
  }
1076
1196
  }
1077
1197
  }));
1198
+ addDisposer(self, autorun(() => {
1199
+ // force colStats not to go stale,
1200
+ // xref solution https://github.com/mobxjs/mobx/issues/266#issuecomment-222007278
1201
+ // xref problem https://github.com/GMOD/react-msaview/issues/75
1202
+ self.colStats;
1203
+ self.colStatsSums;
1204
+ self.columns;
1205
+ }));
1078
1206
  // autorun synchronizes treeWidth with treeAreaWidth
1079
1207
  addDisposer(self, autorun(async () => {
1080
1208
  if (self.treeWidthMatchesArea) {
1081
1209
  self.setTreeWidth(Math.max(50, self.treeAreaWidth - self.labelsWidth - 10 - self.marginLeft));
1082
1210
  }
1083
1211
  }));
1084
- addDisposer(self, autorun(() => {
1085
- localStorageSetItem('msaview-interproscanqueries', JSON.stringify(self.interProScanJobIds));
1086
- }));
1087
1212
  },
1088
1213
  }))
1089
1214
  .postProcessSnapshot(result => {