@jbrowse/plugin-linear-genome-view 2.0.0 → 2.0.1

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 (38) hide show
  1. package/dist/LinearGenomeView/components/{VerticalGuides.d.ts → Gridlines.d.ts} +0 -0
  2. package/dist/LinearGenomeView/components/{VerticalGuides.js → Gridlines.js} +1 -1
  3. package/dist/LinearGenomeView/components/Gridlines.js.map +1 -0
  4. package/dist/LinearGenomeView/components/OverviewRubberBand.js +13 -10
  5. package/dist/LinearGenomeView/components/OverviewRubberBand.js.map +1 -1
  6. package/dist/LinearGenomeView/components/RubberBand.js +0 -1
  7. package/dist/LinearGenomeView/components/RubberBand.js.map +1 -1
  8. package/dist/LinearGenomeView/components/SequenceDialog.js +1 -1
  9. package/dist/LinearGenomeView/components/SequenceDialog.js.map +1 -1
  10. package/dist/LinearGenomeView/components/TracksContainer.js +2 -2
  11. package/dist/LinearGenomeView/components/TracksContainer.js.map +1 -1
  12. package/dist/LinearGenomeView/index.d.ts +63 -76
  13. package/dist/LinearGenomeView/index.js +239 -365
  14. package/dist/LinearGenomeView/index.js.map +1 -1
  15. package/esm/LinearGenomeView/components/{VerticalGuides.d.ts → Gridlines.d.ts} +0 -0
  16. package/esm/LinearGenomeView/components/{VerticalGuides.js → Gridlines.js} +1 -1
  17. package/esm/LinearGenomeView/components/Gridlines.js.map +1 -0
  18. package/esm/LinearGenomeView/components/OverviewRubberBand.js +13 -10
  19. package/esm/LinearGenomeView/components/OverviewRubberBand.js.map +1 -1
  20. package/esm/LinearGenomeView/components/RubberBand.js +0 -1
  21. package/esm/LinearGenomeView/components/RubberBand.js.map +1 -1
  22. package/esm/LinearGenomeView/components/SequenceDialog.js +1 -1
  23. package/esm/LinearGenomeView/components/SequenceDialog.js.map +1 -1
  24. package/esm/LinearGenomeView/components/TracksContainer.js +2 -2
  25. package/esm/LinearGenomeView/components/TracksContainer.js.map +1 -1
  26. package/esm/LinearGenomeView/index.d.ts +63 -76
  27. package/esm/LinearGenomeView/index.js +243 -389
  28. package/esm/LinearGenomeView/index.js.map +1 -1
  29. package/package.json +3 -3
  30. package/src/LinearGenomeView/components/{VerticalGuides.tsx → Gridlines.tsx} +0 -0
  31. package/src/LinearGenomeView/components/OverviewRubberBand.tsx +14 -19
  32. package/src/LinearGenomeView/components/RubberBand.tsx +0 -1
  33. package/src/LinearGenomeView/components/SequenceDialog.tsx +1 -1
  34. package/src/LinearGenomeView/components/TracksContainer.tsx +2 -2
  35. package/src/LinearGenomeView/index.test.ts +13 -36
  36. package/src/LinearGenomeView/index.tsx +360 -519
  37. package/dist/LinearGenomeView/components/VerticalGuides.js.map +0 -1
  38. package/esm/LinearGenomeView/components/VerticalGuides.js.map +0 -1
@@ -98,8 +98,9 @@ var tracks_1 = require("@jbrowse/core/util/tracks");
98
98
  var mobx_1 = require("mobx");
99
99
  var mobx_state_tree_1 = require("mobx-state-tree");
100
100
  var Base1DViewModel_1 = __importDefault(require("@jbrowse/core/util/Base1DViewModel"));
101
- var clone_1 = __importDefault(require("clone"));
101
+ var Base1DUtils_1 = require("@jbrowse/core/util/Base1DUtils");
102
102
  var file_saver_1 = require("file-saver");
103
+ var clone_1 = __importDefault(require("clone"));
103
104
  // icons
104
105
  var Icons_1 = require("@jbrowse/core/ui/Icons");
105
106
  var SyncAlt_1 = __importDefault(require("@mui/icons-material/SyncAlt"));
@@ -163,6 +164,7 @@ function stateModelFactory(pluginManager) {
163
164
  var setting = localStorageGetItem('lgv-showCytobands');
164
165
  return setting !== undefined && setting !== null ? !!+setting : true;
165
166
  }),
167
+ showGridlines: true,
166
168
  }))
167
169
  .volatile(function () { return ({
168
170
  volatileWidth: undefined,
@@ -280,71 +282,6 @@ function stateModelFactory(pluginManager) {
280
282
  tracks: self.tracks,
281
283
  };
282
284
  },
283
- bpToPx: function (_a) {
284
- var refName = _a.refName, coord = _a.coord, regionNumber = _a.regionNumber;
285
- return (0, util_1.viewBpToPx)({ refName: refName, coord: coord, regionNumber: regionNumber, self: self });
286
- },
287
- /**
288
- *
289
- * @param px - px in the view area, return value is the displayed regions
290
- * @returns BpOffset of the displayed region that it lands in
291
- */
292
- pxToBp: function (px) {
293
- var bpSoFar = 0;
294
- var bp = (self.offsetPx + px) * self.bpPerPx;
295
- var n = self.displayedRegions.length;
296
- if (bp < 0) {
297
- var region = self.displayedRegions[0];
298
- var offset = bp;
299
- var snap = (0, mobx_state_tree_1.getSnapshot)(region);
300
- return __assign(__assign({}, snap), { oob: true, coord: region.reversed
301
- ? Math.floor(region.end - offset) + 1
302
- : Math.floor(region.start + offset) + 1, offset: offset, index: 0 });
303
- }
304
- var interRegionPaddingBp = self.interRegionPaddingWidth * self.bpPerPx;
305
- var minimumBlockBp = self.minimumBlockWidth * self.bpPerPx;
306
- for (var index = 0; index < self.displayedRegions.length; index += 1) {
307
- var region = self.displayedRegions[index];
308
- var len = region.end - region.start;
309
- var offset = bp - bpSoFar;
310
- if (len + bpSoFar > bp && bpSoFar <= bp) {
311
- var snap = (0, mobx_state_tree_1.getSnapshot)(region);
312
- return __assign(__assign({}, snap), { oob: false, offset: offset, coord: region.reversed
313
- ? Math.floor(region.end - offset) + 1
314
- : Math.floor(region.start + offset) + 1, index: index });
315
- }
316
- // add the interRegionPaddingWidth if the boundary is in the screen
317
- // e.g. offset>0 && offset<width
318
- if (region.end - region.start > minimumBlockBp &&
319
- offset / self.bpPerPx > 0 &&
320
- offset / self.bpPerPx < self.width) {
321
- bpSoFar += len + interRegionPaddingBp;
322
- }
323
- else {
324
- bpSoFar += len;
325
- }
326
- }
327
- if (bp >= bpSoFar) {
328
- var region = self.displayedRegions[n - 1];
329
- var len = region.end - region.start;
330
- var offset = bp - bpSoFar + len;
331
- var snap = (0, mobx_state_tree_1.getSnapshot)(region);
332
- return __assign(__assign({}, snap), { oob: true, offset: offset, coord: region.reversed
333
- ? Math.floor(region.end - offset) + 1
334
- : Math.floor(region.start + offset) + 1, index: n - 1 });
335
- }
336
- return {
337
- coord: 0,
338
- index: 0,
339
- refName: '',
340
- oob: true,
341
- assemblyName: '',
342
- offset: 0,
343
- start: 0,
344
- end: 0,
345
- reversed: false,
346
- };
347
- },
348
285
  getTrack: function (id) {
349
286
  return self.tracks.find(function (t) { return t.configuration.trackId === id; });
350
287
  },
@@ -395,11 +332,6 @@ function stateModelFactory(pluginManager) {
395
332
  });
396
333
  return allActions;
397
334
  },
398
- get centerLineInfo() {
399
- return self.displayedRegions.length
400
- ? this.pxToBp(self.width / 2)
401
- : undefined;
402
- },
403
335
  }); })
404
336
  .actions(function (self) { return ({
405
337
  setShowCytobands: function (flag) {
@@ -421,6 +353,9 @@ function stateModelFactory(pluginManager) {
421
353
  toggleNoTracksActive: function () {
422
354
  self.hideNoTracksActive = !self.hideNoTracksActive;
423
355
  },
356
+ toggleShowGridlines: function () {
357
+ self.showGridlines = !self.showGridlines;
358
+ },
424
359
  scrollTo: function (offsetPx) {
425
360
  var newOffsetPx = (0, util_1.clamp)(offsetPx, self.minOffset, self.maxOffset);
426
361
  self.offsetPx = newOffsetPx;
@@ -562,230 +497,6 @@ function stateModelFactory(pluginManager) {
562
497
  }
563
498
  throw new Error("invalid track selector type ".concat(self.trackSelectorType));
564
499
  },
565
- navToLocString: function (locString, optAssemblyName) {
566
- var assemblyNames = self.assemblyNames;
567
- var assemblyManager = (0, util_1.getSession)(self).assemblyManager;
568
- var isValidRefName = assemblyManager.isValidRefName;
569
- var assemblyName = optAssemblyName || assemblyNames[0];
570
- var parsedLocStrings;
571
- var inputs = locString
572
- .split(/(\s+)/)
573
- .map(function (f) { return f.trim(); })
574
- .filter(function (f) { return !!f; });
575
- // first try interpreting as a whitespace-separated sequence of
576
- // multiple locstrings
577
- try {
578
- parsedLocStrings = inputs.map(function (l) {
579
- return (0, util_1.parseLocString)(l, function (ref) { return isValidRefName(ref, assemblyName); });
580
- });
581
- }
582
- catch (e) {
583
- // if this fails, try interpreting as a whitespace-separated refname,
584
- // start, end if start and end are integer inputs
585
- var _a = __read(inputs, 3), refName = _a[0], start = _a[1], end = _a[2];
586
- if ("".concat(e).match(/Unknown reference sequence/) &&
587
- Number.isInteger(+start) &&
588
- Number.isInteger(+end)) {
589
- parsedLocStrings = [
590
- (0, util_1.parseLocString)(refName + ':' + start + '..' + end, function (ref) {
591
- return isValidRefName(ref, assemblyName);
592
- }),
593
- ];
594
- }
595
- else {
596
- throw e;
597
- }
598
- }
599
- var locations = parsedLocStrings === null || parsedLocStrings === void 0 ? void 0 : parsedLocStrings.map(function (region) {
600
- var asmName = region.assemblyName || assemblyName;
601
- var asm = assemblyManager.get(asmName);
602
- var refName = region.refName;
603
- if (!asm) {
604
- throw new Error("assembly ".concat(asmName, " not found"));
605
- }
606
- var regions = asm.regions;
607
- if (!regions) {
608
- throw new Error("regions not loaded yet for ".concat(asmName));
609
- }
610
- var canonicalRefName = asm.getCanonicalRefName(region.refName);
611
- if (!canonicalRefName) {
612
- throw new Error("Could not find refName ".concat(refName, " in ").concat(asm.name));
613
- }
614
- var parentRegion = regions.find(function (region) { return region.refName === canonicalRefName; });
615
- if (!parentRegion) {
616
- throw new Error("Could not find refName ".concat(refName, " in ").concat(asmName));
617
- }
618
- return __assign(__assign({}, region), { assemblyName: asmName, parentRegion: parentRegion });
619
- });
620
- if (locations.length === 1) {
621
- var loc = locations[0];
622
- this.setDisplayedRegions([
623
- __assign({ reversed: loc.reversed }, loc.parentRegion),
624
- ]);
625
- var start = loc.start, end = loc.end, parentRegion = loc.parentRegion;
626
- this.navTo(__assign(__assign({}, loc), { start: (0, util_1.clamp)(start !== null && start !== void 0 ? start : 0, 0, parentRegion.end), end: (0, util_1.clamp)(end !== null && end !== void 0 ? end : parentRegion.end, 0, parentRegion.end) }));
627
- }
628
- else {
629
- this.setDisplayedRegions(
630
- // @ts-ignore
631
- locations.map(function (r) { return (r.start === undefined ? r.parentRegion : r); }));
632
- this.showAllRegions();
633
- }
634
- },
635
- /**
636
- * Navigate to a location based on its refName and optionally start, end,
637
- * and assemblyName. Can handle if there are multiple displayedRegions
638
- * from same refName. Only navigates to a location if it is entirely
639
- * within a displayedRegion. Navigates to the first matching location
640
- * encountered.
641
- *
642
- * Throws an error if navigation was unsuccessful
643
- *
644
- * @param location - a proposed location to navigate to
645
- */
646
- navTo: function (query) {
647
- this.navToMultiple([query]);
648
- },
649
- navToMultiple: function (locations) {
650
- var firstLocation = locations[0];
651
- var refName = firstLocation.refName;
652
- var start = firstLocation.start, end = firstLocation.end, _a = firstLocation.assemblyName, assemblyName = _a === void 0 ? self.assemblyNames[0] : _a;
653
- if (start !== undefined && end !== undefined && start > end) {
654
- throw new Error("start \"".concat(start + 1, "\" is greater than end \"").concat(end, "\""));
655
- }
656
- var session = (0, util_1.getSession)(self);
657
- var assemblyManager = session.assemblyManager;
658
- var assembly = assemblyManager.get(assemblyName);
659
- if (assembly) {
660
- var canonicalRefName = assembly.getCanonicalRefName(refName);
661
- if (canonicalRefName) {
662
- refName = canonicalRefName;
663
- }
664
- }
665
- var s = start;
666
- var e = end;
667
- var refNameMatched = false;
668
- var predicate = function (r) {
669
- if (refName === r.refName) {
670
- refNameMatched = true;
671
- if (s === undefined) {
672
- s = r.start;
673
- }
674
- if (e === undefined) {
675
- e = r.end;
676
- }
677
- if (s >= r.start && s <= r.end && e <= r.end && e >= r.start) {
678
- return true;
679
- }
680
- s = start;
681
- e = end;
682
- }
683
- return false;
684
- };
685
- var lastIndex = (0, util_1.findLastIndex)(self.displayedRegions, predicate);
686
- var index;
687
- while (index !== lastIndex) {
688
- try {
689
- var previousIndex = index;
690
- index = self.displayedRegions
691
- .slice(previousIndex === undefined ? 0 : previousIndex + 1)
692
- .findIndex(predicate);
693
- if (previousIndex !== undefined) {
694
- index += previousIndex + 1;
695
- }
696
- if (!refNameMatched) {
697
- throw new Error("could not find a region with refName \"".concat(refName, "\""));
698
- }
699
- if (s === undefined) {
700
- throw new Error("could not find a region with refName \"".concat(refName, "\" that contained an end position ").concat(e));
701
- }
702
- if (e === undefined) {
703
- throw new Error("could not find a region with refName \"".concat(refName, "\" that contained a start position ").concat(s + 1));
704
- }
705
- if (index === -1) {
706
- throw new Error("could not find a region that completely contained \"".concat((0, util_1.assembleLocString)(firstLocation), "\""));
707
- }
708
- if (locations.length === 1) {
709
- var f = self.displayedRegions[index];
710
- this.moveTo({ index: index, offset: f.reversed ? f.end - e : s - f.start }, { index: index, offset: f.reversed ? f.end - s : e - f.start });
711
- return;
712
- }
713
- var locationIndex = 0;
714
- var locationStart = 0;
715
- var locationEnd = 0;
716
- for (locationIndex; locationIndex < locations.length; locationIndex++) {
717
- var location_1 = locations[locationIndex];
718
- var region = self.displayedRegions[index + locationIndex];
719
- locationStart = location_1.start || region.start;
720
- locationEnd = location_1.end || region.end;
721
- if (location_1.refName !== region.refName) {
722
- throw new Error("Entered location ".concat((0, util_1.assembleLocString)(location_1), " does not match with displayed regions"));
723
- }
724
- }
725
- locationIndex -= 1;
726
- var startDisplayedRegion = self.displayedRegions[index];
727
- var endDisplayedRegion = self.displayedRegions[index + locationIndex];
728
- this.moveTo({
729
- index: index,
730
- offset: startDisplayedRegion.reversed
731
- ? startDisplayedRegion.end - e
732
- : s - startDisplayedRegion.start,
733
- }, {
734
- index: index + locationIndex,
735
- offset: endDisplayedRegion.reversed
736
- ? endDisplayedRegion.end - locationStart
737
- : locationEnd - endDisplayedRegion.start,
738
- });
739
- return;
740
- }
741
- catch (error) {
742
- if (index === lastIndex) {
743
- throw error;
744
- }
745
- }
746
- }
747
- },
748
- /**
749
- * Navigate to a location based on user clicking and dragging on the
750
- * overview scale bar to select a region to zoom into.
751
- * Can handle if there are multiple displayedRegions from same refName.
752
- * Only navigates to a location if it is entirely within a displayedRegion.
753
- *
754
- * @param leftPx- `object as {start, end, index, offset}`, offset = start of user drag
755
- * @param rightPx- `object as {start, end, index, offset}`, offset = end of user drag
756
- */
757
- zoomToDisplayedRegions: function (leftPx, rightPx) {
758
- var _a;
759
- if (leftPx === undefined || rightPx === undefined) {
760
- return;
761
- }
762
- var singleRefSeq = leftPx.refName === rightPx.refName && leftPx.index === rightPx.index;
763
- // zooming into one displayed Region
764
- if ((singleRefSeq && rightPx.offset < leftPx.offset) ||
765
- leftPx.index > rightPx.index) {
766
- ;
767
- _a = __read([rightPx, leftPx], 2), leftPx = _a[0], rightPx = _a[1];
768
- }
769
- var startOffset = {
770
- start: leftPx.start,
771
- end: leftPx.end,
772
- index: leftPx.index,
773
- offset: leftPx.offset,
774
- };
775
- var endOffset = {
776
- start: rightPx.start,
777
- end: rightPx.end,
778
- index: rightPx.index,
779
- offset: rightPx.offset,
780
- };
781
- if (startOffset && endOffset) {
782
- this.moveTo(startOffset, endOffset);
783
- }
784
- else {
785
- var session = (0, util_1.getSession)(self);
786
- session.notify('No regions found to navigate to', 'warning');
787
- }
788
- },
789
500
  /**
790
501
  * Helper method for the fetchSequence.
791
502
  * Retrieves the corresponding regions that were selected by the rubberband
@@ -798,87 +509,23 @@ function stateModelFactory(pluginManager) {
798
509
  var snap = (0, mobx_state_tree_1.getSnapshot)(self);
799
510
  var simView = Base1DViewModel_1.default.create(__assign(__assign({}, snap), { interRegionPaddingWidth: self.interRegionPaddingWidth }));
800
511
  simView.setVolatileWidth(self.width);
801
- simView.zoomToDisplayedRegions(leftOffset, rightOffset);
512
+ simView.moveTo(leftOffset, rightOffset);
802
513
  return simView.dynamicBlocks.contentBlocks.map(function (region) { return (__assign(__assign({}, region), { start: Math.floor(region.start), end: Math.ceil(region.end) })); });
803
514
  },
804
515
  // schedule something to be run after the next time displayedRegions is set
805
516
  afterDisplayedRegionsSet: function (cb) {
806
517
  self.afterDisplayedRegionsSetCallbacks.push(cb);
807
518
  },
808
- /**
809
- * offset is the base-pair-offset in the displayed region, index is the index of the
810
- * displayed region in the linear genome view
811
- *
812
- * @param start - object as `{start, end, offset, index}`
813
- * @param end - object as `{start, end, offset, index}`
814
- */
815
- moveTo: function (start, end) {
816
- // find locations in the modellist
817
- var bpSoFar = 0;
818
- if (start.index === end.index) {
819
- bpSoFar += end.offset - start.offset;
820
- }
821
- else {
822
- var s = self.displayedRegions[start.index];
823
- bpSoFar += s.end - s.start - start.offset;
824
- if (end.index - start.index >= 2) {
825
- for (var i = start.index + 1; i < end.index; i += 1) {
826
- bpSoFar +=
827
- self.displayedRegions[i].end - self.displayedRegions[i].start;
828
- }
829
- }
830
- bpSoFar += end.offset;
831
- }
832
- var targetBpPerPx = bpSoFar /
833
- (self.width -
834
- self.interRegionPaddingWidth * (end.index - start.index));
835
- var newBpPerPx = self.zoomTo(targetBpPerPx);
836
- // If our target bpPerPx was smaller than the allowed minBpPerPx, adjust
837
- // the scroll so the requested range is in the middle of the screen
838
- var extraBp = 0;
839
- if (targetBpPerPx < newBpPerPx) {
840
- extraBp = ((newBpPerPx - targetBpPerPx) * self.width) / 2;
841
- }
842
- var bpToStart = -extraBp;
843
- for (var i = 0; i < self.displayedRegions.length; i += 1) {
844
- var region = self.displayedRegions[i];
845
- if (start.index === i) {
846
- bpToStart += start.offset;
847
- break;
848
- }
849
- else {
850
- bpToStart += region.end - region.start;
851
- }
852
- }
853
- self.scrollTo(Math.round(bpToStart / self.bpPerPx) +
854
- self.interRegionPaddingWidth * start.index);
855
- },
856
519
  horizontalScroll: function (distance) {
857
520
  var oldOffsetPx = self.offsetPx;
858
521
  // newOffsetPx is the actual offset after the scroll is clamped
859
522
  var newOffsetPx = self.scrollTo(self.offsetPx + distance);
860
523
  return newOffsetPx - oldOffsetPx;
861
524
  },
862
- /**
863
- * scrolls the view to center on the given bp. if that is not in any
864
- * of the displayed regions, does nothing
865
- * @param bp - basepair at which you want to center the view
866
- * @param refName - refName of the displayedRegion you are centering at
867
- * @param regionIndex - index of the displayedRegion
868
- */
869
- centerAt: function (bp, refName, regionIndex) {
870
- var centerPx = self.bpToPx({
871
- refName: refName,
872
- coord: bp,
873
- regionNumber: regionIndex,
874
- });
875
- if (centerPx) {
876
- self.scrollTo(Math.round(centerPx.offsetPx - self.width / 2));
877
- }
878
- },
879
525
  center: function () {
880
526
  var centerBp = self.totalBp / 2;
881
- self.scrollTo(Math.round(centerBp / self.bpPerPx - self.width / 2));
527
+ var centerPx = centerBp / self.bpPerPx;
528
+ self.scrollTo(Math.round(centerPx - self.width / 2));
882
529
  },
883
530
  showAllRegions: function () {
884
531
  self.zoomTo(self.maxBpPerPx);
@@ -1034,6 +681,13 @@ function stateModelFactory(pluginManager) {
1034
681
  checked: !self.hideNoTracksActive,
1035
682
  onClick: self.toggleNoTracksActive,
1036
683
  },
684
+ {
685
+ label: 'Show gridlines',
686
+ icon: Visibility_1.default,
687
+ type: 'checkbox',
688
+ checked: self.showGridlines,
689
+ onClick: self.toggleShowGridlines,
690
+ },
1037
691
  {
1038
692
  label: 'Track labels',
1039
693
  icon: Label_1.default,
@@ -1163,6 +817,199 @@ function stateModelFactory(pluginManager) {
1163
817
  });
1164
818
  });
1165
819
  },
820
+ /**
821
+ * offset is the base-pair-offset in the displayed region, index is the index of the
822
+ * displayed region in the linear genome view
823
+ *
824
+ * @param start - object as `{start, end, offset, index}`
825
+ * @param end - object as `{start, end, offset, index}`
826
+ */
827
+ moveTo: function (start, end) {
828
+ (0, Base1DUtils_1.moveTo)(self, start, end);
829
+ },
830
+ navToLocString: function (locString, optAssemblyName) {
831
+ var assemblyNames = self.assemblyNames;
832
+ var assemblyManager = (0, util_1.getSession)(self).assemblyManager;
833
+ var isValidRefName = assemblyManager.isValidRefName;
834
+ var assemblyName = optAssemblyName || assemblyNames[0];
835
+ var parsedLocStrings;
836
+ var inputs = locString
837
+ .split(/(\s+)/)
838
+ .map(function (f) { return f.trim(); })
839
+ .filter(function (f) { return !!f; });
840
+ // first try interpreting as a whitespace-separated sequence of
841
+ // multiple locstrings
842
+ try {
843
+ parsedLocStrings = inputs.map(function (l) {
844
+ return (0, util_1.parseLocString)(l, function (ref) { return isValidRefName(ref, assemblyName); });
845
+ });
846
+ }
847
+ catch (e) {
848
+ // if this fails, try interpreting as a whitespace-separated refname,
849
+ // start, end if start and end are integer inputs
850
+ var _a = __read(inputs, 3), refName = _a[0], start = _a[1], end = _a[2];
851
+ if ("".concat(e).match(/Unknown reference sequence/) &&
852
+ Number.isInteger(+start) &&
853
+ Number.isInteger(+end)) {
854
+ parsedLocStrings = [
855
+ (0, util_1.parseLocString)(refName + ':' + start + '..' + end, function (ref) {
856
+ return isValidRefName(ref, assemblyName);
857
+ }),
858
+ ];
859
+ }
860
+ else {
861
+ throw e;
862
+ }
863
+ }
864
+ var locations = parsedLocStrings === null || parsedLocStrings === void 0 ? void 0 : parsedLocStrings.map(function (region) {
865
+ var asmName = region.assemblyName || assemblyName;
866
+ var asm = assemblyManager.get(asmName);
867
+ var refName = region.refName;
868
+ if (!asm) {
869
+ throw new Error("assembly ".concat(asmName, " not found"));
870
+ }
871
+ var regions = asm.regions;
872
+ if (!regions) {
873
+ throw new Error("regions not loaded yet for ".concat(asmName));
874
+ }
875
+ var canonicalRefName = asm.getCanonicalRefName(region.refName);
876
+ if (!canonicalRefName) {
877
+ throw new Error("Could not find refName ".concat(refName, " in ").concat(asm.name));
878
+ }
879
+ var parentRegion = regions.find(function (region) { return region.refName === canonicalRefName; });
880
+ if (!parentRegion) {
881
+ throw new Error("Could not find refName ".concat(refName, " in ").concat(asmName));
882
+ }
883
+ return __assign(__assign({}, region), { assemblyName: asmName, parentRegion: parentRegion });
884
+ });
885
+ if (locations.length === 1) {
886
+ var loc = locations[0];
887
+ self.setDisplayedRegions([
888
+ __assign({ reversed: loc.reversed }, loc.parentRegion),
889
+ ]);
890
+ var start = loc.start, end = loc.end, parentRegion = loc.parentRegion;
891
+ this.navTo(__assign(__assign({}, loc), { start: (0, util_1.clamp)(start !== null && start !== void 0 ? start : 0, 0, parentRegion.end), end: (0, util_1.clamp)(end !== null && end !== void 0 ? end : parentRegion.end, 0, parentRegion.end) }));
892
+ }
893
+ else {
894
+ self.setDisplayedRegions(
895
+ // @ts-ignore
896
+ locations.map(function (r) { return (r.start === undefined ? r.parentRegion : r); }));
897
+ self.showAllRegions();
898
+ }
899
+ },
900
+ /**
901
+ * Navigate to a location based on its refName and optionally start, end,
902
+ * and assemblyName. Can handle if there are multiple displayedRegions
903
+ * from same refName. Only navigates to a location if it is entirely
904
+ * within a displayedRegion. Navigates to the first matching location
905
+ * encountered.
906
+ *
907
+ * Throws an error if navigation was unsuccessful
908
+ *
909
+ * @param location - a proposed location to navigate to
910
+ */
911
+ navTo: function (query) {
912
+ this.navToMultiple([query]);
913
+ },
914
+ navToMultiple: function (locations) {
915
+ var firstLocation = locations[0];
916
+ var refName = firstLocation.refName;
917
+ var start = firstLocation.start, end = firstLocation.end, _a = firstLocation.assemblyName, assemblyName = _a === void 0 ? self.assemblyNames[0] : _a;
918
+ if (start !== undefined && end !== undefined && start > end) {
919
+ throw new Error("start \"".concat(start + 1, "\" is greater than end \"").concat(end, "\""));
920
+ }
921
+ var session = (0, util_1.getSession)(self);
922
+ var assemblyManager = session.assemblyManager;
923
+ var assembly = assemblyManager.get(assemblyName);
924
+ if (assembly) {
925
+ var canonicalRefName = assembly.getCanonicalRefName(refName);
926
+ if (canonicalRefName) {
927
+ refName = canonicalRefName;
928
+ }
929
+ }
930
+ var s = start;
931
+ var e = end;
932
+ var refNameMatched = false;
933
+ var predicate = function (r) {
934
+ if (refName === r.refName) {
935
+ refNameMatched = true;
936
+ if (s === undefined) {
937
+ s = r.start;
938
+ }
939
+ if (e === undefined) {
940
+ e = r.end;
941
+ }
942
+ if (s >= r.start && s <= r.end && e <= r.end && e >= r.start) {
943
+ return true;
944
+ }
945
+ s = start;
946
+ e = end;
947
+ }
948
+ return false;
949
+ };
950
+ var lastIndex = (0, util_1.findLastIndex)(self.displayedRegions, predicate);
951
+ var index;
952
+ while (index !== lastIndex) {
953
+ try {
954
+ var previousIndex = index;
955
+ index = self.displayedRegions
956
+ .slice(previousIndex === undefined ? 0 : previousIndex + 1)
957
+ .findIndex(predicate);
958
+ if (previousIndex !== undefined) {
959
+ index += previousIndex + 1;
960
+ }
961
+ if (!refNameMatched) {
962
+ throw new Error("could not find a region with refName \"".concat(refName, "\""));
963
+ }
964
+ if (s === undefined) {
965
+ throw new Error("could not find a region with refName \"".concat(refName, "\" that contained an end position ").concat(e));
966
+ }
967
+ if (e === undefined) {
968
+ throw new Error("could not find a region with refName \"".concat(refName, "\" that contained a start position ").concat(s + 1));
969
+ }
970
+ if (index === -1) {
971
+ throw new Error("could not find a region that completely contained \"".concat((0, util_1.assembleLocString)(firstLocation), "\""));
972
+ }
973
+ if (locations.length === 1) {
974
+ var f = self.displayedRegions[index];
975
+ this.moveTo({ index: index, offset: f.reversed ? f.end - e : s - f.start }, { index: index, offset: f.reversed ? f.end - s : e - f.start });
976
+ return;
977
+ }
978
+ var locationIndex = 0;
979
+ var locationStart = 0;
980
+ var locationEnd = 0;
981
+ for (locationIndex; locationIndex < locations.length; locationIndex++) {
982
+ var location_1 = locations[locationIndex];
983
+ var region = self.displayedRegions[index + locationIndex];
984
+ locationStart = location_1.start || region.start;
985
+ locationEnd = location_1.end || region.end;
986
+ if (location_1.refName !== region.refName) {
987
+ throw new Error("Entered location ".concat((0, util_1.assembleLocString)(location_1), " does not match with displayed regions"));
988
+ }
989
+ }
990
+ locationIndex -= 1;
991
+ var startDisplayedRegion = self.displayedRegions[index];
992
+ var endDisplayedRegion = self.displayedRegions[index + locationIndex];
993
+ this.moveTo({
994
+ index: index,
995
+ offset: startDisplayedRegion.reversed
996
+ ? startDisplayedRegion.end - e
997
+ : s - startDisplayedRegion.start,
998
+ }, {
999
+ index: index + locationIndex,
1000
+ offset: endDisplayedRegion.reversed
1001
+ ? endDisplayedRegion.end - locationStart
1002
+ : locationEnd - endDisplayedRegion.start,
1003
+ });
1004
+ return;
1005
+ }
1006
+ catch (error) {
1007
+ if (index === lastIndex) {
1008
+ throw error;
1009
+ }
1010
+ }
1011
+ }
1012
+ },
1166
1013
  }); })
1167
1014
  .views(function (self) { return ({
1168
1015
  rubberBandMenuItems: function () {
@@ -1172,9 +1019,7 @@ function stateModelFactory(pluginManager) {
1172
1019
  icon: ZoomIn_1.default,
1173
1020
  onClick: function () {
1174
1021
  var leftOffset = self.leftOffset, rightOffset = self.rightOffset;
1175
- if (leftOffset && rightOffset) {
1176
- self.moveTo(leftOffset, rightOffset);
1177
- }
1022
+ self.moveTo(leftOffset, rightOffset);
1178
1023
  },
1179
1024
  },
1180
1025
  {
@@ -1186,6 +1031,35 @@ function stateModelFactory(pluginManager) {
1186
1031
  },
1187
1032
  ];
1188
1033
  },
1034
+ bpToPx: function (_a) {
1035
+ var refName = _a.refName, coord = _a.coord, regionNumber = _a.regionNumber;
1036
+ return (0, Base1DUtils_1.bpToPx)({ refName: refName, coord: coord, regionNumber: regionNumber, self: self });
1037
+ },
1038
+ /**
1039
+ * scrolls the view to center on the given bp. if that is not in any
1040
+ * of the displayed regions, does nothing
1041
+ * @param coord - basepair at which you want to center the view
1042
+ * @param refName - refName of the displayedRegion you are centering at
1043
+ * @param regionNumber - index of the displayedRegion
1044
+ */
1045
+ centerAt: function (coord, refName, regionNumber) {
1046
+ var centerPx = this.bpToPx({
1047
+ refName: refName,
1048
+ coord: coord,
1049
+ regionNumber: regionNumber,
1050
+ });
1051
+ if (centerPx) {
1052
+ self.scrollTo(Math.round(centerPx.offsetPx - self.width / 2));
1053
+ }
1054
+ },
1055
+ pxToBp: function (px) {
1056
+ return (0, Base1DUtils_1.pxToBp)(self, px);
1057
+ },
1058
+ get centerLineInfo() {
1059
+ return self.displayedRegions.length
1060
+ ? this.pxToBp(self.width / 2)
1061
+ : undefined;
1062
+ },
1189
1063
  }); });
1190
1064
  }
1191
1065
  exports.stateModelFactory = stateModelFactory;