igv 2.11.1 → 2.12.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.
package/dist/igv.js CHANGED
@@ -19911,7 +19911,7 @@
19911
19911
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19912
19912
  * THE SOFTWARE.
19913
19913
  */
19914
- const knownFileExtensions = new Set(["narrowpeak", "broadpeak", "regionpeak", "peaks", "bedgraph", "wig", "gff3", "gff", "gtf", "fusionjuncspan", "refflat", "seg", "aed", "bed", "vcf", "bb", "bigbed", "biginteract", "bw", "bigwig", "bam", "tdf", "refgene", "genepred", "genepredext", "bedpe", "bp", "snp", "rmsk", "cram", "gwas", "maf", "mut"]);
19914
+ const knownFileExtensions = new Set(["narrowpeak", "broadpeak", "regionpeak", "peaks", "bedgraph", "wig", "gff3", "gff", "gtf", "fusionjuncspan", "refflat", "seg", "aed", "bed", "vcf", "bb", "bigbed", "biginteract", "biggenepred", "bignarrowpeak", "bw", "bigwig", "bam", "tdf", "refgene", "genepred", "genepredext", "bedpe", "bp", "snp", "rmsk", "cram", "gwas", "maf", "mut"]);
19915
19915
  /**
19916
19916
  * Return a custom format object with the given name.
19917
19917
  * @param name
@@ -20032,6 +20032,7 @@
20032
20032
 
20033
20033
  case "bedpe":
20034
20034
  case "bedpe-loop":
20035
+ case "biginteract":
20035
20036
  return "interact";
20036
20037
 
20037
20038
  case "bp":
@@ -20043,7 +20044,8 @@
20043
20044
  case "bed":
20044
20045
  case "bigbed":
20045
20046
  case "bb":
20046
- case "biginteract":
20047
+ case "biggenepred":
20048
+ case "bignarrowpeak":
20047
20049
  return "bedtype";
20048
20050
 
20049
20051
  default:
@@ -20287,6 +20289,30 @@
20287
20289
  return window.location.protocol === "https:" || window.location.hostname === "localhost";
20288
20290
  }
20289
20291
 
20292
+ const pairs = [['A', 'T'], ['G', 'C'], ['Y', 'R'], ['W', 'S'], ['K', 'M'], ['D', 'H'], ['B', 'V']];
20293
+ const complements = new Map();
20294
+
20295
+ for (let p of pairs) {
20296
+ const p1 = p[0];
20297
+ const p2 = p[1];
20298
+ complements.set(p1, p2);
20299
+ complements.set(p2, p1);
20300
+ complements.set(p1.toLowerCase(), p2.toLowerCase());
20301
+ complements.set(p2.toLowerCase(), p1.toLowerCase());
20302
+ }
20303
+
20304
+ function reverseComplementSequence(sequence) {
20305
+ let comp = '';
20306
+ let idx = sequence.length;
20307
+
20308
+ while (idx-- > 0) {
20309
+ const base = sequence[idx];
20310
+ comp += complements.has(base) ? complements.get(base) : base;
20311
+ }
20312
+
20313
+ return comp;
20314
+ }
20315
+
20290
20316
  /*
20291
20317
  * The MIT License (MIT)
20292
20318
  *
@@ -20440,16 +20466,21 @@
20440
20466
  const viewport = clickState.viewport;
20441
20467
 
20442
20468
  if (viewport.referenceFrame.bpPerPixel <= 1) {
20469
+ const pixelWidth = viewport.getWidth();
20470
+ const bpWindow = pixelWidth * viewport.referenceFrame.bpPerPixel;
20471
+ const chr = viewport.referenceFrame.chr;
20472
+ const start = Math.floor(viewport.referenceFrame.start);
20473
+ const end = Math.ceil(start + bpWindow);
20443
20474
  const items = [{
20444
- label: 'View visible sequence...',
20475
+ label: this.reversed ? 'View visible sequence (reversed)...' : 'View visible sequence...',
20445
20476
  click: async () => {
20446
- const pixelWidth = viewport.getWidth();
20447
- const bpWindow = pixelWidth * viewport.referenceFrame.bpPerPixel;
20448
- const chr = viewport.referenceFrame.chr;
20449
- const start = viewport.referenceFrame.start;
20450
- const end = start + bpWindow;
20451
- const sequence = await this.browser.genome.sequence.getSequence(chr, start, end);
20452
- Alert.presentAlert(sequence);
20477
+ let seq = await this.browser.genome.sequence.getSequence(chr, start, end);
20478
+
20479
+ if (this.reversed) {
20480
+ seq = reverseComplementSequence(seq);
20481
+ }
20482
+
20483
+ Alert.presentAlert(seq);
20453
20484
  }
20454
20485
  }];
20455
20486
 
@@ -20457,13 +20488,18 @@
20457
20488
  items.push({
20458
20489
  label: 'Copy visible sequence',
20459
20490
  click: async () => {
20460
- const pixelWidth = viewport.getWidth();
20461
- const bpWindow = pixelWidth * viewport.referenceFrame.bpPerPixel;
20462
- const chr = viewport.referenceFrame.chr;
20463
- const start = viewport.referenceFrame.start;
20464
- const end = start + bpWindow;
20465
- const sequence = await this.browser.genome.sequence.getSequence(chr, start, end);
20466
- navigator.clipboard.writeText(sequence);
20491
+ let seq = await this.browser.genome.sequence.getSequence(chr, start, end);
20492
+
20493
+ if (this.reversed) {
20494
+ seq = reverseComplementSequence(seq);
20495
+ }
20496
+
20497
+ try {
20498
+ await navigator.clipboard.writeText(seq);
20499
+ } catch (e) {
20500
+ console.error(e);
20501
+ Alert.presentAlert(`error copying sequence to clipboard ${e}`);
20502
+ }
20467
20503
  }
20468
20504
  });
20469
20505
  }
@@ -20604,7 +20640,7 @@
20604
20640
  }
20605
20641
  }
20606
20642
 
20607
- supportsWholeGenome() {
20643
+ get supportsWholeGenome() {
20608
20644
  return false;
20609
20645
  }
20610
20646
 
@@ -20672,12 +20708,13 @@
20672
20708
  });
20673
20709
  this.$viewport.append(this.$content);
20674
20710
  this.$content.height(this.$viewport.height());
20675
- this.contentDiv = this.$content.get(0);
20676
- this.$canvas = $$1('<canvas>');
20677
- this.$content.append(this.$canvas);
20678
- this.canvas = this.$canvas.get(0);
20679
- this.ctx = this.canvas.getContext("2d");
20680
- this.setWidth(width);
20711
+ this.contentDiv = this.$content.get(0); // this.$canvas = $('<canvas>')
20712
+ // this.$content.append(this.$canvas)
20713
+ //
20714
+ // this.canvas = this.$canvas.get(0)
20715
+ // this.ctx = this.canvas.getContext("2d")
20716
+
20717
+ this.$viewport.width(width);
20681
20718
  this.initializationHelper();
20682
20719
  }
20683
20720
 
@@ -20759,7 +20796,6 @@
20759
20796
  // Maximum height of a canvas is ~32,000 pixels on Chrome, possibly smaller on other platforms
20760
20797
  contentHeight = Math.min(contentHeight, 32000);
20761
20798
  this.$content.height(contentHeight);
20762
- if (this.canvas._data) this.canvas._data.invalidate = true;
20763
20799
  }
20764
20800
 
20765
20801
  isLoading() {
@@ -20801,7 +20837,6 @@
20801
20837
  this.popover.dispose();
20802
20838
  }
20803
20839
 
20804
- this.removeMouseHandlers();
20805
20840
  this.$viewport.get(0).remove(); // Null out all properties -- this should not be neccessary, but just in case there is a
20806
20841
  // reference to self somewhere we want to free memory.
20807
20842
 
@@ -23058,7 +23093,7 @@
23058
23093
  }
23059
23094
  };
23060
23095
 
23061
- const _version = "2.11.1";
23096
+ const _version = "2.12.1";
23062
23097
 
23063
23098
  function version$1() {
23064
23099
  return _version;
@@ -23201,7 +23236,7 @@
23201
23236
  class Genome {
23202
23237
  constructor(config, sequence, ideograms, aliases) {
23203
23238
  this.config = config;
23204
- this.id = config.id;
23239
+ this.id = config.id || generateGenomeID(config);
23205
23240
  this.sequence = sequence;
23206
23241
  this.chromosomeNames = sequence.chromosomeNames;
23207
23242
  this.chromosomes = sequence.chromosomes; // An object (functions as a dictionary)
@@ -23408,6 +23443,7 @@
23408
23443
  }
23409
23444
 
23410
23445
  async getSequence(chr, start, end) {
23446
+ chr = this.getChromosomeName(chr);
23411
23447
  return this.sequence.getSequence(chr, start, end);
23412
23448
  }
23413
23449
 
@@ -23516,6 +23552,18 @@
23516
23552
  }
23517
23553
  }
23518
23554
 
23555
+ function generateGenomeID(config) {
23556
+ if (config.id !== undefined) {
23557
+ return config.id;
23558
+ } else if (config.fastaURL && isString$3(config.fastaURL)) {
23559
+ return config.fastaURL;
23560
+ } else if (config.fastaURL && config.fastaURL.name) {
23561
+ return config.fastaURL.name;
23562
+ } else {
23563
+ return ("0000" + (Math.random() * Math.pow(36, 4) << 0).toString(36)).slice(-4);
23564
+ }
23565
+ }
23566
+
23519
23567
  /**
23520
23568
  * Created by dat on 9/16/16.
23521
23569
  */
@@ -23534,15 +23582,13 @@
23534
23582
  });
23535
23583
  this.$viewport.append(this.$spinner);
23536
23584
  this.$spinner.append($$1('<div>'));
23537
- const {
23538
- track
23539
- } = this.trackView;
23585
+ const track = this.trackView.track;
23540
23586
 
23541
23587
  if ('sequence' !== track.type) {
23542
23588
  this.$zoomInNotice = this.createZoomInNotice(this.$content);
23543
23589
  }
23544
23590
 
23545
- if (track.name && "sequence" !== track.config.type) {
23591
+ if (track.name && "sequence" !== track.id) {
23546
23592
  this.$trackLabel = $$1('<div class="igv-track-label">');
23547
23593
  this.$viewport.append(this.$trackLabel);
23548
23594
  this.setTrackLabel(track.name);
@@ -23556,6 +23602,11 @@
23556
23602
  this.addMouseHandlers();
23557
23603
  }
23558
23604
 
23605
+ setContentHeight(contentHeight) {
23606
+ super.setContentHeight(contentHeight);
23607
+ if (this.featureCache) this.featureCache.redraw = true;
23608
+ }
23609
+
23559
23610
  setTrackLabel(label) {
23560
23611
  this.$trackLabel.empty();
23561
23612
  this.$trackLabel.html(label);
@@ -23572,10 +23623,18 @@
23572
23623
  this.$spinner.hide();
23573
23624
  }
23574
23625
  }
23626
+ /**
23627
+ * Test to determine if we are zoomed in far enough to see features. Applicable to tracks with visibility windows.
23628
+ *
23629
+ * As a side effect the viewports canvas is removed if zoomed out.
23630
+ *
23631
+ * @returns {boolean} true if we are zoomed in past visibility window, false otherwise
23632
+ */
23633
+
23575
23634
 
23576
23635
  checkZoomIn() {
23577
- const showZoomInNotice = () => {
23578
- if (this.referenceFrame.chr.toLowerCase() === "all" && !this.trackView.track.supportsWholeGenome()) {
23636
+ const zoomedOutOfWindow = () => {
23637
+ if (this.referenceFrame.chr.toLowerCase() === "all" && !this.trackView.track.supportsWholeGenome) {
23579
23638
  return true;
23580
23639
  } else {
23581
23640
  const visibilityWindow = this.trackView.track.visibilityWindow;
@@ -23584,7 +23643,8 @@
23584
23643
  };
23585
23644
 
23586
23645
  if (this.trackView.track && "sequence" === this.trackView.track.type && this.referenceFrame.bpPerPixel > 1) {
23587
- if (this.canvas) ;
23646
+ $$1(this.canvas).remove();
23647
+ this.canvas = undefined; //this.featureCache = undefined
23588
23648
 
23589
23649
  return false;
23590
23650
  }
@@ -23593,30 +23653,30 @@
23593
23653
  return false;
23594
23654
  }
23595
23655
 
23596
- if (this.$zoomInNotice) {
23597
- if (showZoomInNotice()) {
23598
- // Out of visibility window
23599
- if (this.canvas) {
23600
- this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
23601
- this.canvas._data = undefined;
23602
- this.featureCache = undefined;
23603
- }
23656
+ if (zoomedOutOfWindow()) {
23657
+ // Out of visibility window
23658
+ if (this.canvas) {
23659
+ $$1(this.canvas).remove();
23660
+ this.canvas = undefined; //this.featureCache = undefined
23661
+ }
23604
23662
 
23605
- this.$zoomInNotice.show();
23663
+ if (this.trackView.track.autoHeight) {
23664
+ const minHeight = this.trackView.minHeight || 0;
23665
+ this.setContentHeight(minHeight);
23666
+ }
23606
23667
 
23607
- if (this.trackView.track.autoHeight) {
23608
- const minHeight = this.trackView.minHeight || 0;
23609
- this.setContentHeight(minHeight);
23610
- }
23668
+ if (this.$zoomInNotice) {
23669
+ this.$zoomInNotice.show();
23670
+ }
23611
23671
 
23612
- return false;
23613
- } else {
23672
+ return false;
23673
+ } else {
23674
+ if (this.$zoomInNotice) {
23614
23675
  this.$zoomInNotice.hide();
23615
- return true;
23616
23676
  }
23617
- }
23618
23677
 
23619
- return true;
23678
+ return true;
23679
+ }
23620
23680
  }
23621
23681
  /**
23622
23682
  * Adjust the canvas to the current genomic state.
@@ -23689,9 +23749,8 @@
23689
23749
  }
23690
23750
  }
23691
23751
  /**
23692
- * Repaint the canvas for the current genomic state.
23752
+ * Repaint the canvas using the cached features
23693
23753
  *
23694
- * @returns {Promise<void>}
23695
23754
  */
23696
23755
 
23697
23756
 
@@ -23706,24 +23765,15 @@
23706
23765
  } = this.featureCache; //this.tile.bpPerPixel = this.referenceFrame.bpPerPixel
23707
23766
  // const isWGV = GenomeUtils.isWholeGenomeView(this.browser.referenceFrameList[0].chr)
23708
23767
 
23709
- const isWGV = GenomeUtils.isWholeGenomeView(this.referenceFrame.chr);
23710
- let pixelWidth;
23711
- const startBP = this.featureCache.startBP;
23712
- const endBP = this.featureCache.endBP;
23713
- let bpPerPixel = this.referenceFrame.bpPerPixel;
23714
-
23715
- if (isWGV) {
23716
- pixelWidth = this.$viewport.width();
23717
- } else {
23718
- pixelWidth = 3 * this.$viewport.width();
23719
- } // For deep tracks we paint a canvas == 3*viewportHeight centered on the current vertical scroll position
23720
-
23768
+ const isWGV = GenomeUtils.isWholeGenomeView(this.referenceFrame.chr); // Canvas dimensions. There is no left-right panning for WGV so canvas width is viewport width.
23769
+ // For deep tracks we paint a canvas == 3*viewportHeight centered on the current vertical scroll position
23721
23770
 
23771
+ const pixelWidth = isWGV ? this.$viewport.width() : 3 * this.$viewport.width();
23722
23772
  const viewportHeight = this.$viewport.height();
23723
23773
  const contentHeight = this.getContentHeight();
23724
23774
  const minHeight = roiFeatures ? Math.max(contentHeight, viewportHeight) : contentHeight; // Need to fill viewport for ROIs.
23725
23775
 
23726
- let pixelHeight = Math.min(minHeight, 3 * viewportHeight);
23776
+ const pixelHeight = Math.min(minHeight, 3 * viewportHeight);
23727
23777
 
23728
23778
  if (0 === pixelWidth || 0 === pixelHeight) {
23729
23779
  if (this.canvas) {
@@ -23733,26 +23783,22 @@
23733
23783
  return;
23734
23784
  }
23735
23785
 
23736
- const canvasTop = Math.max(0, -this.$content.position().top - viewportHeight); // Always use high DPI if in compressed display mode, otherwise use preference setting;
23737
-
23738
- let devicePixelRatio;
23739
-
23740
- if ("FILL" === this.trackView.track.displayMode) {
23741
- devicePixelRatio = window.devicePixelRatio;
23742
- } else {
23743
- devicePixelRatio = this.trackView.track.supportHiDPI === false ? 1 : window.devicePixelRatio;
23744
- }
23745
-
23746
- const pixelXOffset = Math.round((startBP - this.referenceFrame.start) / this.referenceFrame.bpPerPixel);
23786
+ const canvasTop = Math.max(0, -this.$content.position().top - viewportHeight);
23787
+ const bpPerPixel = this.referenceFrame.bpPerPixel;
23788
+ const startBP = this.referenceFrame.start - (isWGV ? 0 : pixelWidth / 3 * bpPerPixel);
23789
+ const endBP = this.referenceFrame.end + (isWGV ? 0 : pixelWidth / 3 * bpPerPixel);
23790
+ const pixelXOffset = Math.round((startBP - this.referenceFrame.start) / bpPerPixel);
23747
23791
  const newCanvas = $$1('<canvas class="igv-canvas">').get(0);
23748
- const ctx = newCanvas.getContext("2d");
23749
23792
  newCanvas.style.width = pixelWidth + "px";
23750
23793
  newCanvas.style.height = pixelHeight + "px";
23794
+ newCanvas.style.left = pixelXOffset + "px";
23795
+ newCanvas.style.top = canvasTop + "px"; // Always use high DPI if in "FILL" display mode, otherwise use track setting;
23796
+
23797
+ const devicePixelRatio = "FILL" === this.trackView.track.displayMode || this.trackView.track.supportHiDPI !== false ? window.devicePixelRatio : 1;
23751
23798
  newCanvas.width = devicePixelRatio * pixelWidth;
23752
23799
  newCanvas.height = devicePixelRatio * pixelHeight;
23800
+ const ctx = newCanvas.getContext("2d");
23753
23801
  ctx.scale(devicePixelRatio, devicePixelRatio);
23754
- newCanvas.style.left = pixelXOffset + "px";
23755
- newCanvas.style.top = canvasTop + "px";
23756
23802
  ctx.translate(0, -canvasTop);
23757
23803
  const drawConfiguration = {
23758
23804
  context: ctx,
@@ -23768,23 +23814,23 @@
23768
23814
  viewport: this,
23769
23815
  viewportWidth: this.$viewport.width()
23770
23816
  };
23771
- this.draw(drawConfiguration, features, roiFeatures); // Attach metadata to canvas
23772
-
23773
- newCanvas._data = {
23774
- chr: this.referenceFrame.chr,
23775
- startBP,
23776
- endBP,
23777
- bpPerPixel,
23778
- top: canvasTop,
23779
- bottom: canvasTop + pixelHeight
23780
- };
23817
+ this.draw(drawConfiguration, features, roiFeatures);
23818
+ this.featureCache.canvasTop = canvasTop;
23819
+ this.featureCache.height = pixelHeight;
23781
23820
 
23782
23821
  if (this.canvas) {
23783
23822
  $$1(this.canvas).remove();
23784
23823
  }
23785
23824
 
23825
+ newCanvas._data = {
23826
+ chr: this.featureCache.chr,
23827
+ bpPerPixel,
23828
+ startBP,
23829
+ endBP,
23830
+ pixelHeight,
23831
+ pixelTop: canvasTop
23832
+ };
23786
23833
  this.canvas = newCanvas;
23787
- this.ctx = ctx;
23788
23834
  this.$content.append($$1(newCanvas));
23789
23835
  }
23790
23836
  /**
@@ -23824,15 +23870,16 @@
23824
23870
  }
23825
23871
 
23826
23872
  savePNG() {
23827
- if (!this.ctx) return;
23828
- const canvasMetadata = this.canvas._data;
23829
- const canvasTop = canvasMetadata ? canvasMetadata.top : 0;
23873
+ if (!this.canvas) return;
23874
+ const canvasMetadata = this.featureCache;
23875
+ const canvasTop = canvasMetadata ? canvasMetadata.canvasTop : 0;
23830
23876
  const devicePixelRatio = window.devicePixelRatio;
23831
23877
  const w = this.$viewport.width() * devicePixelRatio;
23832
23878
  const h = this.$viewport.height() * devicePixelRatio;
23833
23879
  const x = -$$1(this.canvas).position().left * devicePixelRatio;
23834
23880
  const y = (-this.$content.position().top - canvasTop) * devicePixelRatio;
23835
- const imageData = this.ctx.getImageData(x, y, w, h);
23881
+ const ctx = this.canvas.getContext("2d");
23882
+ const imageData = ctx.getImageData(x, y, w, h);
23836
23883
  const exportCanvas = document.createElement('canvas');
23837
23884
  const exportCtx = exportCanvas.getContext('2d');
23838
23885
  exportCanvas.width = imageData.width;
@@ -23970,6 +24017,22 @@
23970
24017
  }
23971
24018
  }
23972
24019
 
24020
+ needsRepaint() {
24021
+ if (!this.canvas) return true;
24022
+ const data = this.canvas._data;
24023
+ return !data || this.referenceFrame.start < data.startBP || this.referenceFrame.end > data.endBP || this.referenceFrame.chr !== data.chr || this.referenceFrame.bpPerPixel != data.bpPerPixel;
24024
+ }
24025
+
24026
+ needsReload() {
24027
+ if (!this.featureCache) return true;
24028
+ const referenceFrame = this.referenceFrame;
24029
+ const chr = this.referenceFrame.chr;
24030
+ const start = referenceFrame.start;
24031
+ const end = start + referenceFrame.toBP($$1(this.contentDiv).width());
24032
+ const bpPerPixel = referenceFrame.bpPerPixel;
24033
+ return !this.featureCache.containsRange(chr, start, end, bpPerPixel);
24034
+ }
24035
+
23973
24036
  createZoomInNotice($parent) {
23974
24037
  const $container = $$1('<div>', {
23975
24038
  class: 'igv-zoom-in-notice-container'
@@ -23987,36 +24050,42 @@
23987
24050
  }
23988
24051
 
23989
24052
  addMouseHandlers() {
23990
- this.addViewportContextMenuHandler(this.$viewport.get(0));
23991
- this.addViewportMouseDownHandler(this.$viewport.get(0));
23992
- this.addViewportTouchStartHandler(this.$viewport.get(0));
23993
- this.addViewportMouseUpHandler(this.$viewport.get(0));
23994
- this.addViewportTouchEndHandler(this.$viewport.get(0));
23995
- this.addViewportClickHandler(this.$viewport.get(0));
24053
+ const viewport = this.$viewport.get(0);
24054
+ this.addViewportContextMenuHandler(viewport);
23996
24055
 
23997
- if (this.trackView.track.name && "sequence" !== this.trackView.track.config.type) {
23998
- this.addTrackLabelClickHandler(this.$trackLabel.get(0));
23999
- }
24000
- }
24056
+ const md = event => {
24057
+ this.enableClick = true;
24058
+ this.browser.mouseDownOnViewport(event, this);
24059
+ pageCoordinates$1(event);
24060
+ };
24001
24061
 
24002
- removeMouseHandlers() {
24003
- this.removeViewportContextMenuHandler(this.$viewport.get(0));
24004
- this.removeViewportMouseDownHandler(this.$viewport.get(0));
24005
- this.removeViewportTouchStartHandler(this.$viewport.get(0));
24006
- this.removeViewportMouseUpHandler(this.$viewport.get(0));
24007
- this.removeViewportTouchEndHandler(this.$viewport.get(0));
24008
- this.removeViewportClickHandler(this.$viewport.get(0));
24062
+ viewport.addEventListener('mousedown', md);
24063
+ viewport.addEventListener('touchstart', md);
24064
+
24065
+ const mu = event => {
24066
+ // Any mouse up cancels drag and scrolling
24067
+ if (this.browser.dragObject || this.browser.isScrolling) {
24068
+ this.browser.cancelTrackPan(); // event.preventDefault();
24069
+ // event.stopPropagation();
24070
+
24071
+ this.enableClick = false; // Until next mouse down
24072
+ } else {
24073
+ this.browser.cancelTrackPan();
24074
+ this.browser.endTrackDrag();
24075
+ }
24076
+ };
24077
+
24078
+ viewport.addEventListener('mouseup', mu);
24079
+ viewport.addEventListener('touchend', mu);
24080
+ this.addViewportClickHandler(this.$viewport.get(0));
24009
24081
 
24010
24082
  if (this.trackView.track.name && "sequence" !== this.trackView.track.config.type) {
24011
- this.removeTrackLabelClickHandler(this.$trackLabel.get(0));
24083
+ this.addTrackLabelClickHandler(this.$trackLabel.get(0));
24012
24084
  }
24013
24085
  }
24014
24086
 
24015
24087
  addViewportContextMenuHandler(viewport) {
24016
- this.boundContextMenuHandler = contextMenuHandler.bind(this);
24017
- viewport.addEventListener('contextmenu', this.boundContextMenuHandler);
24018
-
24019
- function contextMenuHandler(event) {
24088
+ viewport.addEventListener('contextmenu', event => {
24020
24089
  // Ignore if we are doing a drag. This can happen with touch events.
24021
24090
  if (this.browser.dragObject) {
24022
24091
  return false;
@@ -24056,55 +24125,12 @@
24056
24125
  click: () => this.saveSVG()
24057
24126
  });
24058
24127
  this.browser.menuPopup.presentTrackContextMenu(event, menuItems);
24059
- }
24060
- }
24061
-
24062
- removeViewportContextMenuHandler(viewport) {
24063
- viewport.removeEventListener('contextmenu', this.boundContextMenuHandler);
24064
- }
24065
-
24066
- addViewportMouseDownHandler(viewport) {
24067
- this.boundMouseDownHandler = mouseDownHandler.bind(this);
24068
- viewport.addEventListener('mousedown', this.boundMouseDownHandler);
24069
- }
24070
-
24071
- removeViewportMouseDownHandler(viewport) {
24072
- viewport.removeEventListener('mousedown', this.boundMouseDownHandler);
24073
- }
24074
-
24075
- addViewportTouchStartHandler(viewport) {
24076
- this.boundTouchStartHandler = mouseDownHandler.bind(this);
24077
- viewport.addEventListener('touchstart', this.boundTouchStartHandler);
24078
- }
24079
-
24080
- removeViewportTouchStartHandler(viewport) {
24081
- viewport.removeEventListener('touchstart', this.boundTouchStartHandler);
24082
- }
24083
-
24084
- addViewportMouseUpHandler(viewport) {
24085
- this.boundMouseUpHandler = mouseUpHandler.bind(this);
24086
- viewport.addEventListener('mouseup', this.boundMouseUpHandler);
24087
- }
24088
-
24089
- removeViewportMouseUpHandler(viewport) {
24090
- viewport.removeEventListener('mouseup', this.boundMouseUpHandler);
24091
- }
24092
-
24093
- addViewportTouchEndHandler(viewport) {
24094
- this.boundTouchEndHandler = mouseUpHandler.bind(this);
24095
- viewport.addEventListener('touchend', this.boundTouchEndHandler);
24096
- }
24097
-
24098
- removeViewportTouchEndHandler(viewport) {
24099
- viewport.removeEventListener('touchend', this.boundTouchEndHandler);
24128
+ });
24100
24129
  }
24101
24130
 
24102
24131
  addViewportClickHandler(viewport) {
24103
- this.boundClickHandler = clickHandler.bind(this);
24104
- viewport.addEventListener('click', this.boundClickHandler);
24105
-
24106
- function clickHandler(event) {
24107
- if (this.enableClick) {
24132
+ viewport.addEventListener('click', event => {
24133
+ if (this.enableClick && this.canvas) {
24108
24134
  if (3 === event.which || event.ctrlKey) {
24109
24135
  return;
24110
24136
  } // Close any currently open popups
@@ -24178,18 +24204,11 @@
24178
24204
 
24179
24205
  lastClickTime = time;
24180
24206
  }
24181
- }
24182
- }
24183
-
24184
- removeViewportClickHandler(viewport) {
24185
- viewport.removeEventListener('click', this.boundClickHandler);
24207
+ });
24186
24208
  }
24187
24209
 
24188
24210
  addTrackLabelClickHandler(trackLabel) {
24189
- this.boundTrackLabelClickHandler = clickHandler.bind(this);
24190
- trackLabel.addEventListener('click', this.boundTrackLabelClickHandler);
24191
-
24192
- function clickHandler(event) {
24211
+ trackLabel.addEventListener('click', event => {
24193
24212
  event.stopPropagation();
24194
24213
  const {
24195
24214
  track
@@ -24210,34 +24229,11 @@
24210
24229
  this.popover = new Popover(this.browser.columnContainer, track.name || '');
24211
24230
  this.popover.presentContentWithEvent(event, str);
24212
24231
  }
24213
- }
24214
- }
24215
-
24216
- removeTrackLabelClickHandler(trackLabel) {
24217
- trackLabel.removeEventListener('click', this.boundTrackLabelClickHandler);
24232
+ });
24218
24233
  }
24219
24234
 
24220
24235
  }
24221
24236
 
24222
- function mouseDownHandler(event) {
24223
- this.enableClick = true;
24224
- this.browser.mouseDownOnViewport(event, this);
24225
- pageCoordinates$1(event);
24226
- }
24227
-
24228
- function mouseUpHandler(event) {
24229
- // Any mouse up cancels drag and scrolling
24230
- if (this.browser.dragObject || this.browser.isScrolling) {
24231
- this.browser.cancelTrackPan(); // event.preventDefault();
24232
- // event.stopPropagation();
24233
-
24234
- this.enableClick = false; // Until next mouse down
24235
- } else {
24236
- this.browser.cancelTrackPan();
24237
- this.browser.endTrackDrag();
24238
- }
24239
- }
24240
-
24241
24237
  function createClickState(event, viewport) {
24242
24238
  const referenceFrame = viewport.referenceFrame;
24243
24239
  const viewportCoords = translateMouseCoordinates$1(event, viewport.contentDiv);
@@ -24456,10 +24452,12 @@
24456
24452
  end: bp(this.rulerViewport.referenceFrame, left + width)
24457
24453
  };
24458
24454
  validateLocusExtent(this.rulerViewport.browser.genome.getChromosome(this.rulerViewport.referenceFrame.chr).bpLength, extent, this.rulerViewport.browser.minimumBases());
24459
- this.rulerViewport.referenceFrame.bpPerPixel = (Math.round(extent.end) - Math.round(extent.start)) / this.rulerViewport.contentDiv.clientWidth;
24460
- this.rulerViewport.referenceFrame.start = Math.round(extent.start);
24461
- this.rulerViewport.referenceFrame.end = Math.round(extent.end);
24462
- this.rulerViewport.browser.updateViews(this.rulerViewport.referenceFrame);
24455
+ const newStart = Math.round(extent.start);
24456
+ const newEnd = Math.round(extent.end);
24457
+ this.rulerViewport.referenceFrame.bpPerPixel = (newEnd - newStart) / this.rulerViewport.contentDiv.clientWidth;
24458
+ this.rulerViewport.referenceFrame.start = newStart;
24459
+ this.rulerViewport.referenceFrame.end = newEnd;
24460
+ this.rulerViewport.browser.updateViews();
24463
24461
  }
24464
24462
  }
24465
24463
  }
@@ -25469,7 +25467,7 @@
25469
25467
  return this.firstAlignment.isProperPair();
25470
25468
  }
25471
25469
 
25472
- get tlen() {
25470
+ get fragmentLength() {
25473
25471
  return Math.abs(this.firstAlignment.fragmentLength);
25474
25472
  }
25475
25473
 
@@ -26958,7 +26956,7 @@
26958
26956
  const lseq = readInt(ba, offset + 20);
26959
26957
  const mateChrIdx = readInt(ba, offset + 24);
26960
26958
  const matePos = readInt(ba, offset + 28);
26961
- const tlen = readInt(ba, offset + 32);
26959
+ const fragmentLength = readInt(ba, offset + 32);
26962
26960
  let readName = [];
26963
26961
 
26964
26962
  for (let j = 0; j < nl - 1; ++j) {
@@ -26999,7 +26997,7 @@
26999
26997
  alignment.readName = readName;
27000
26998
  alignment.cigar = cigar;
27001
26999
  alignment.lengthOnRef = lengthOnRef;
27002
- alignment.fragmentLength = tlen;
27000
+ alignment.fragmentLength = fragmentLength;
27003
27001
  alignment.mq = mq;
27004
27002
  BamUtils.bam_tag2cigar(ba, blockEnd, p, lseq, alignment, cigarArray);
27005
27003
  alignment.end = alignment.start + alignment.lengthOnRef;
@@ -45433,6 +45431,10 @@
45433
45431
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
45434
45432
  * THE SOFTWARE.
45435
45433
  */
45434
+
45435
+ const fixColor = colorString => {
45436
+ return colorString.indexOf(",") > 0 && !colorString.startsWith("rgb") ? `rgb(${colorString})` : colorString;
45437
+ };
45436
45438
  /**
45437
45439
  * A collection of properties and methods shared by all (or most) track types.
45438
45440
  *
@@ -45441,6 +45443,7 @@
45441
45443
  * @constructor
45442
45444
  */
45443
45445
 
45446
+
45444
45447
  class TrackBase {
45445
45448
  constructor(config, browser) {
45446
45449
  this.browser = browser;
@@ -45474,8 +45477,8 @@
45474
45477
 
45475
45478
  this.id = this.config.id === undefined ? this.name : this.config.id;
45476
45479
  this.order = config.order;
45477
- this.color = config.color;
45478
- this.altColor = config.altColor;
45480
+ if (config.color) this.color = fixColor(config.color);
45481
+ if (config.altColor) this.altColor = fixColor(config.altColor);
45479
45482
 
45480
45483
  if ("civic-ws" === config.sourceType) {
45481
45484
  // Ugly proxy for specialized track type
@@ -45588,7 +45591,7 @@
45588
45591
  return state;
45589
45592
  }
45590
45593
 
45591
- supportsWholeGenome() {
45594
+ get supportsWholeGenome() {
45592
45595
  return false;
45593
45596
  }
45594
45597
  /**
@@ -47679,7 +47682,7 @@
47679
47682
  return this.tracks.find(t => name === t.name);
47680
47683
  }
47681
47684
 
47682
- getChordset(name) {
47685
+ getChordSet(name) {
47683
47686
  return this.chordSets.find(cs => name === cs.name);
47684
47687
  }
47685
47688
 
@@ -47789,9 +47792,9 @@
47789
47792
  buttonContainer.appendChild(this.showControlsButton);
47790
47793
  this.showControlsButton.innerText = 'none' === this.controlPanel.style.display ? 'Show Controls' : 'Hide Controls';
47791
47794
  this.showControlsButton.addEventListener('click', event => {
47792
- const trackPanelRows = this.controlPanel.querySelectorAll('div');
47795
+ const panelRows = this.controlPanel.querySelectorAll('div');
47793
47796
 
47794
- if (trackPanelRows.length > 0) {
47797
+ if (panelRows.length > 0) {
47795
47798
  if ('none' === this.controlPanel.style.display) {
47796
47799
  this.controlPanel.style.display = 'flex';
47797
47800
  event.target.innerText = 'Hide Controls';
@@ -47871,10 +47874,10 @@
47871
47874
  hideShowButton.innerText = true === chordSet.visible ? 'Hide' : 'Show';
47872
47875
  hideShowButton.addEventListener('click', event => {
47873
47876
  if (true === chordSet.visible) {
47874
- this.hideTrack(chordSet.name);
47877
+ this.hideChordSet(chordSet.name);
47875
47878
  event.target.innerText = "Show";
47876
47879
  } else {
47877
- this.showTrack(chordSet.name);
47880
+ this.showChordSet(chordSet.name);
47878
47881
  event.target.innerText = "Hide";
47879
47882
  }
47880
47883
  }); // The alpha range slider. Create this here so we can reference it from the color picker
@@ -47901,7 +47904,7 @@
47901
47904
  rgbaString
47902
47905
  }) => {
47903
47906
  colorPickerButton.style.backgroundColor = setAlpha(rgbaString, 1);
47904
- this.setTrackColor(chordSet.name, rgbaString);
47907
+ this.setColor(chordSet.name, rgbaString);
47905
47908
  alphaSlider.value = alphaToValue(getAlpha(chordSet.color));
47906
47909
  }
47907
47910
  };
@@ -47919,7 +47922,7 @@
47919
47922
 
47920
47923
  alphaSlider.oninput = () => {
47921
47924
  const v = valueToAlpha(alphaSlider.value);
47922
- this.setTrackColor(chordSet.name, setAlpha(chordSet.color, v));
47925
+ this.setColor(chordSet.name, setAlpha(chordSet.color, v));
47923
47926
  picker.setColor(chordSet.color);
47924
47927
  };
47925
47928
 
@@ -47938,12 +47941,14 @@
47938
47941
 
47939
47942
 
47940
47943
  setAssembly(igvGenome) {
47941
- if (this.genomeId === igvGenome.id) {
47944
+ const id = this.genomeId || guid();
47945
+
47946
+ if (this.genomeId === id) {
47942
47947
  return;
47943
47948
  }
47944
47949
 
47945
47950
  this.chordManager.clearChords();
47946
- this.genomeId = igvGenome.id;
47951
+ this.genomeId = id;
47947
47952
  this.chrNames = new Set(igvGenome.chromosomes.map(chr => shortChrName$1(chr.name)));
47948
47953
  const regions = [];
47949
47954
  const colors = [];
@@ -47962,7 +47967,7 @@
47962
47967
  this.assembly = {
47963
47968
  name: igvGenome.name,
47964
47969
  sequence: {
47965
- trackId: igvGenome.id,
47970
+ trackId: id,
47966
47971
  type: 'ReferenceSequenceTrack',
47967
47972
  adapter: {
47968
47973
  type: 'FromConfigSequenceAdapter',
@@ -48000,7 +48005,7 @@
48000
48005
 
48001
48006
 
48002
48007
  addChords(newChords, options = {}) {
48003
- const tmp = options.track || options.name || "*";
48008
+ const tmp = options.name || options.track || "*";
48004
48009
  const trackName = tmp.split(' ')[0].replaceAll("%20", " ");
48005
48010
  const chordSetName = tmp.replaceAll("%20", " ");
48006
48011
  const chordSet = {
@@ -48050,20 +48055,6 @@
48050
48055
  clearSelection() {
48051
48056
  this.viewState.pluginManager.rootModel.session.clearSelection();
48052
48057
  }
48053
-
48054
- getFeature(featureId) {
48055
- // TODO -- broken
48056
- // const display = this.viewState.pluginManager.rootModel.session.view.tracks[0].displays[0]
48057
- // const feature = display.data.features.get(featureId)
48058
- // return feature;
48059
- const features = [...this.viewState.config.tracks[0].adapter.features.value];
48060
-
48061
- for (let f of features) {
48062
- if (featureId === f.uniqueId) {
48063
- return f;
48064
- }
48065
- }
48066
- }
48067
48058
  /**
48068
48059
  * Deprecated, use "visible" property
48069
48060
  */
@@ -48089,25 +48080,25 @@
48089
48080
  this.parent.style.display = isVisible ? 'block' : 'none';
48090
48081
  }
48091
48082
 
48092
- hideTrack(trackName) {
48093
- let track = this.getTrack(trackName);
48083
+ hideChordSet(trackName) {
48084
+ let cs = this.getChordSet(trackName);
48094
48085
 
48095
- if (track) {
48096
- track.visible = false;
48086
+ if (cs) {
48087
+ cs.visible = false;
48097
48088
  this.render();
48098
48089
  } else {
48099
48090
  console.warn(`No track with name: ${name}`);
48100
48091
  }
48101
48092
  }
48102
48093
 
48103
- showTrack(trackName) {
48104
- let track = this.getTrack(trackName);
48094
+ showChordSet(name) {
48095
+ let cs = this.getChordSet(name);
48105
48096
 
48106
- if (track) {
48107
- track.visible = true;
48097
+ if (cs) {
48098
+ cs.visible = true;
48108
48099
  this.render();
48109
48100
  } else {
48110
- console.warn(`No track with name: ${trackName}`);
48101
+ console.warn(`No track with name: ${name}`);
48111
48102
  }
48112
48103
  } // showTrack(trackID) {
48113
48104
  // let idx = this.tracks.findIndex(t => trackID === t.id)
@@ -48134,12 +48125,12 @@
48134
48125
  this.render();
48135
48126
  }
48136
48127
 
48137
- getTrack(name) {
48138
- return this.groupByTrack ? this.chordManager.getTrack(name) : this.chordManager.getChordset(name);
48128
+ getChordSet(name) {
48129
+ return this.groupByTrack ? this.chordManager.getTrack(name) : this.chordManager.getChordSet(name);
48139
48130
  }
48140
48131
 
48141
- setTrackColor(name, color) {
48142
- const t = this.getTrack(name);
48132
+ setColor(name, color) {
48133
+ const t = this.getChordSet(name);
48143
48134
 
48144
48135
  if (t) {
48145
48136
  t.color = color;
@@ -48240,7 +48231,7 @@
48240
48231
  }
48241
48232
 
48242
48233
  function embedCSS$1() {
48243
- const css = '.igv-circview-container {\n z-index: 2048;\n position: absolute;\n width: fit-content;\n height: fit-content;\n box-sizing: content-box;\n color: dimgray;\n font-family: \"Open Sans\", sans-serif;\n font-size: 12px;\n background-color: white;\n border-color: dimgray;\n border-style: solid;\n border-width: thin;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n}\n\n.igv-circview-toolbar {\n position: relative;\n width: 100%;\n height: 32px;\n background-color: lightgrey;\n border-bottom-style: solid;\n border-bottom-color: dimgray;\n border-bottom-width: thin;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n}\n\n.igv-circview-toolbar-button-container {\n height: 100%;\n width: fit-content;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-circview-toolbar-button-container > div {\n margin: 4px;\n}\n\n.igv-circview-track-panel {\n z-index: 1024;\n position: absolute;\n top: 33px;\n left: 0;\n width: 100%;\n height: fit-content;\n border-bottom-style: solid;\n border-bottom-color: dimgray;\n border-bottom-width: thin;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n}\n.igv-circview-track-panel > div {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-circview-track-panel > div > div {\n margin: 4px;\n}\n\n.igv-circview-swatch-button {\n cursor: pointer;\n padding: 5px;\n width: 8px;\n height: 8px;\n border: 1px solid #8d8b8b;\n border-radius: 16px;\n}\n\n.igv-circview-button {\n cursor: pointer;\n padding: 5px;\n color: #444;\n vertical-align: middle;\n text-align: center;\n font-family: \"Open Sans\", sans-serif;\n font-size: 12px;\n border: 1px solid #8d8b8b;\n border-radius: 4px;\n background: #efefef;\n box-shadow: 0 0 5px -1px rgba(0, 0, 0, 0.2);\n}\n\n.igv-circview-button:hover {\n background: #efefef;\n box-shadow: 0 0 5px -1px rgba(0, 0, 0, 0.6);\n}\n\n.igv-circview-button:active {\n color: #007bff;\n box-shadow: 0 0 5px -1px rgba(0, 0, 0, 0.6);\n}\n\n/*# sourceMappingURL=circular-view.css.map */\n';
48234
+ const css = '.igv-circview-container {\n z-index: 2048;\n width: fit-content;\n height: fit-content;\n box-sizing: content-box;\n color: dimgray;\n font-family: \"Open Sans\", sans-serif;\n font-size: 12px;\n background-color: white;\n border-color: dimgray;\n border-style: solid;\n border-width: thin;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n}\n\n.igv-circview-toolbar {\n position: relative;\n width: 100%;\n height: 32px;\n background-color: lightgrey;\n border-bottom-style: solid;\n border-bottom-color: dimgray;\n border-bottom-width: thin;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n}\n\n.igv-circview-toolbar-button-container {\n height: 100%;\n width: fit-content;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-circview-toolbar-button-container > div {\n margin: 4px;\n}\n\n.igv-circview-track-panel {\n z-index: 1024;\n position: absolute;\n top: 33px;\n left: 0;\n width: 100%;\n height: fit-content;\n border-bottom-style: solid;\n border-bottom-color: dimgray;\n border-bottom-width: thin;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n}\n.igv-circview-track-panel > div {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-circview-track-panel > div > div {\n margin: 4px;\n}\n\n.igv-circview-swatch-button {\n cursor: pointer;\n padding: 5px;\n width: 8px;\n height: 8px;\n border: 1px solid #8d8b8b;\n border-radius: 16px;\n}\n\n.igv-circview-button {\n cursor: pointer;\n padding: 5px;\n color: #444;\n vertical-align: middle;\n text-align: center;\n font-family: \"Open Sans\", sans-serif;\n font-size: 12px;\n border: 1px solid #8d8b8b;\n border-radius: 4px;\n background: #efefef;\n box-shadow: 0 0 5px -1px rgba(0, 0, 0, 0.2);\n}\n\n.igv-circview-button:hover {\n background: #efefef;\n box-shadow: 0 0 5px -1px rgba(0, 0, 0, 0.6);\n}\n\n.igv-circview-button:active {\n color: #007bff;\n box-shadow: 0 0 5px -1px rgba(0, 0, 0, 0.6);\n}\n\n/*# sourceMappingURL=circular-view.css.map */\n';
48244
48235
  const style = document.createElement('style');
48245
48236
  style.setAttribute('type', 'text/css');
48246
48237
  style.innerHTML = css;
@@ -48398,7 +48389,9 @@
48398
48389
  track: chordSetName,
48399
48390
  color: chordSetColor,
48400
48391
  trackColor: trackColor
48401
- });
48392
+ }); // show circular view if hidden
48393
+
48394
+ if (!track.browser.circularViewVisible) track.browser.circularViewVisible = true;
48402
48395
  }
48403
48396
 
48404
48397
  function createCircularView(el, browser) {
@@ -48411,22 +48404,26 @@
48411
48404
 
48412
48405
  function addFrameForFeature(feature) {
48413
48406
  feature.chr = browser.genome.getChromosomeName(feature.refName);
48407
+ let frameFound = false;
48414
48408
 
48415
- for (let referenceFrame of browser.currentReferenceFrames()) {
48409
+ for (let referenceFrame of browser.referenceFrameList) {
48416
48410
  const l = Locus.fromLocusString(referenceFrame.getLocusString());
48417
48411
 
48418
48412
  if (l.contains(feature)) {
48413
+ frameFound = true;
48419
48414
  break;
48420
48415
  } else if (l.overlaps(feature)) {
48421
48416
  referenceFrame.extend(feature);
48422
- break;
48423
- } else {
48424
- const flanking = 2000;
48425
- const center = (feature.start + feature.end) / 2;
48426
- browser.addMultiLocusPanel(feature.chr, center - flanking, center + flanking);
48417
+ frameFound = true;
48427
48418
  break;
48428
48419
  }
48429
48420
  }
48421
+
48422
+ if (!frameFound) {
48423
+ const flanking = 2000;
48424
+ const center = (feature.start + feature.end) / 2;
48425
+ browser.addMultiLocusPanel(feature.chr, center - flanking, center + flanking);
48426
+ }
48430
48427
  }
48431
48428
  }
48432
48429
  });
@@ -48866,7 +48863,7 @@
48866
48863
  } // Add chords to JBrowse circular view, if present
48867
48864
 
48868
48865
 
48869
- if (this.browser.circularView && true === this.browser.circularViewVisible && (this.alignmentTrack.hasPairs || this.alignmentTrack.hasSupplemental)) {
48866
+ if (this.browser.circularView && (this.alignmentTrack.hasPairs || this.alignmentTrack.hasSupplemental)) {
48870
48867
  menuItems.push('<hr/>');
48871
48868
 
48872
48869
  if (this.alignmentTrack.hasPairs) {
@@ -49383,7 +49380,7 @@
49383
49380
  for (let alignment of alignmentRow.alignments) {
49384
49381
  this.hasPairs = this.hasPairs || alignment.isPaired();
49385
49382
 
49386
- if (this.browser.circularView && true === this.browser.circularViewVisible) {
49383
+ if (this.browser.circularView) {
49387
49384
  // This is an expensive check, only do it if needed
49388
49385
  this.hasSupplemental = this.hasSupplemental || alignment.hasTag('SA');
49389
49386
  }
@@ -49733,9 +49730,7 @@
49733
49730
  list.push({
49734
49731
  label: 'View read sequence',
49735
49732
  click: () => {
49736
- const alignment = clickedAlignment;
49737
- if (!alignment) return;
49738
- const seqstring = alignment.seq; //.map(b => String.fromCharCode(b)).join("");
49733
+ const seqstring = clickedAlignment.seq; //.map(b => String.fromCharCode(b)).join("");
49739
49734
 
49740
49735
  if (!seqstring || "*" === seqstring) {
49741
49736
  Alert.presentAlert("Read sequence: *");
@@ -49748,12 +49743,16 @@
49748
49743
  if (isSecureContext()) {
49749
49744
  list.push({
49750
49745
  label: 'Copy read sequence',
49751
- click: () => {
49752
- const alignment = clickedAlignment;
49753
- if (!alignment) return;
49754
- const seqstring = alignment.seq; //.map(b => String.fromCharCode(b)).join("");
49746
+ click: async () => {
49747
+ const seq = clickedAlignment.seq; //.map(b => String.fromCharCode(b)).join("");
49755
49748
 
49756
- navigator.clipboard.writeText(seqstring);
49749
+ try {
49750
+ //console.log(`seq: ${seq}`)
49751
+ await navigator.clipboard.writeText(seq);
49752
+ } catch (e) {
49753
+ console.error(e);
49754
+ Alert.presentAlert(`error copying sequence to clipboard ${e}`);
49755
+ }
49757
49756
  }
49758
49757
  });
49759
49758
  }
@@ -49763,7 +49762,7 @@
49763
49762
  } // Experimental JBrowse feature
49764
49763
 
49765
49764
 
49766
- if (this.browser.circularView && true === this.browser.circularViewVisible && (this.hasPairs || this.hasSupplemental)) {
49765
+ if (this.browser.circularView && (this.hasPairs || this.hasSupplemental)) {
49767
49766
  if (this.hasPairs) {
49768
49767
  list.push({
49769
49768
  label: 'Add discordant pairs to circular view',
@@ -49867,11 +49866,15 @@
49867
49866
  case "unexpectedPair":
49868
49867
  case "pairOrientation":
49869
49868
  if (this.pairOrientation && alignment.pairOrientation) {
49870
- var oTypes = orientationTypes[this.pairOrientation];
49869
+ const oTypes = orientationTypes[this.pairOrientation];
49871
49870
 
49872
49871
  if (oTypes) {
49873
- var pairColor = this.pairColors[oTypes[alignment.pairOrientation]];
49874
- if (pairColor) color = pairColor;
49872
+ const pairColor = this.pairColors[oTypes[alignment.pairOrientation]];
49873
+
49874
+ if (pairColor) {
49875
+ color = pairColor;
49876
+ break;
49877
+ }
49875
49878
  }
49876
49879
  }
49877
49880
 
@@ -50191,7 +50194,10 @@
50191
50194
  currentViewport = this;
50192
50195
  this.$tooltip.show();
50193
50196
  } else if (currentViewport.guid !== this.guid) {
50194
- currentViewport.$tooltip.hide();
50197
+ if (currentViewport.$tooltip) {
50198
+ currentViewport.$tooltip.hide();
50199
+ }
50200
+
50195
50201
  this.$tooltip.show();
50196
50202
  currentViewport = this;
50197
50203
  } else {
@@ -50225,7 +50231,9 @@
50225
50231
  }); // hide tooltip when movement stops
50226
50232
 
50227
50233
  clearTimeout(timer);
50228
- timer = setTimeout(() => this.$tooltip.hide(), toolTipTimeout);
50234
+ timer = setTimeout(() => {
50235
+ if (this.$tooltip) this.$tooltip.hide();
50236
+ }, toolTipTimeout);
50229
50237
  }
50230
50238
  }
50231
50239
 
@@ -50240,65 +50248,6 @@
50240
50248
 
50241
50249
  }
50242
50250
 
50243
- const viewportColumnManager = {
50244
- createColumns: (columnContainer, count) => {
50245
- for (let i = 0; i < count; i++) {
50246
- if (0 === i) {
50247
- createColumn(columnContainer, 'igv-column');
50248
- } else {
50249
- columnContainer.appendChild(div$1({
50250
- class: 'igv-column-shim'
50251
- }));
50252
- createColumn(columnContainer, 'igv-column');
50253
- }
50254
- }
50255
- },
50256
- removeColumnAtIndex: (i, column) => {
50257
- const shim = 0 === i ? column.nextElementSibling : column.previousElementSibling;
50258
- column.remove();
50259
- shim.remove();
50260
- },
50261
- insertAfter: referenceElement => {
50262
- const shim = div$1({
50263
- class: 'igv-column-shim'
50264
- });
50265
- insertElementAfter(shim, referenceElement);
50266
- const column = div$1({
50267
- class: 'igv-column'
50268
- });
50269
- insertElementAfter(column, shim);
50270
- return column;
50271
- },
50272
- insertBefore: (referenceElement, count) => {
50273
- for (let i = 0; i < count; i++) {
50274
- const column = div$1({
50275
- class: 'igv-column'
50276
- });
50277
- insertElementBefore(column, referenceElement);
50278
-
50279
- if (count > 1 && i > 0) {
50280
- const columnShim = div$1({
50281
- class: 'igv-column-shim'
50282
- });
50283
- insertElementBefore(columnShim, column);
50284
- }
50285
- }
50286
- },
50287
- indexOfColumn: (columnContainer, column) => {
50288
- const allColumns = columnContainer.querySelectorAll('.igv-column');
50289
-
50290
- for (let i = 0; i < allColumns.length; i++) {
50291
- const c = allColumns[i];
50292
-
50293
- if (c === column) {
50294
- return i;
50295
- }
50296
- }
50297
-
50298
- return undefined;
50299
- }
50300
- };
50301
-
50302
50251
  /*
50303
50252
  * The MIT License (MIT)
50304
50253
  *
@@ -50330,60 +50279,30 @@
50330
50279
  }
50331
50280
 
50332
50281
  initializationHelper() {
50333
- this.$ideogramCanvas = $$1('<canvas>', {
50334
- class: 'igv-ideogram-canvas'
50335
- });
50336
- this.$ideogramCanvas.insertBefore(this.$canvas);
50337
- const canvas = this.$ideogramCanvas.get(0);
50338
- this.ideogram_ctx = canvas.getContext('2d');
50339
- this.$canvas.remove();
50340
- this.canvas = undefined;
50341
- this.ctx = undefined;
50282
+ this.canvas = document.createElement('canvas');
50283
+ this.canvas.className = 'igv-ideogram-canvas';
50284
+ this.$content.append($$1(this.canvas));
50285
+ this.ideogram_ctx = this.canvas.getContext('2d');
50342
50286
  this.addMouseHandlers();
50343
50287
  }
50344
50288
 
50345
50289
  addMouseHandlers() {
50346
- this.addBrowserObserver();
50347
50290
  this.addViewportClickHandler(this.$viewport.get(0));
50348
50291
  }
50349
50292
 
50350
- removeMouseHandlers() {
50351
- this.removeBrowserObserver();
50352
- this.removeViewportClickHandler(this.$viewport.get(0));
50353
- }
50354
-
50355
- addBrowserObserver() {
50356
- function observerHandler(referenceFrameList) {
50357
- const column = this.$viewport.get(0).parentElement;
50358
-
50359
- if (null !== column) {
50360
- const index = viewportColumnManager.indexOfColumn(this.browser.columnContainer, column); // console.log(`ideogram-viewport - locus-change-handler index(${ index }) ${ referenceFrameList[ index ].getLocusString() } ${ Date.now() } `)
50361
-
50362
- this.update(this.ideogram_ctx, this.$viewport.width(), this.$viewport.height(), referenceFrameList[index]);
50363
- }
50364
- }
50365
-
50366
- this.boundObserverHandler = observerHandler.bind(this);
50367
- this.browser.on('locuschange', this.boundObserverHandler);
50368
- }
50369
-
50370
- removeBrowserObserver() {
50371
- this.browser.off('locuschange', this.boundObserverHandler);
50372
- }
50373
-
50374
50293
  addViewportClickHandler(viewport) {
50294
+ this.boundClickHandler = clickHandler.bind(this);
50295
+ viewport.addEventListener('click', this.boundClickHandler);
50296
+
50375
50297
  function clickHandler(event) {
50376
- const column = viewport.parentElement;
50377
- const index = viewportColumnManager.indexOfColumn(this.browser.columnContainer, column);
50378
- const referenceFrame = this.browser.referenceFrameList[index];
50379
50298
  const {
50380
50299
  xNormalized,
50381
50300
  width
50382
50301
  } = translateMouseCoordinates$1(event, this.ideogram_ctx.canvas);
50383
50302
  const {
50384
50303
  bpLength
50385
- } = this.browser.genome.getChromosome(referenceFrame.chr);
50386
- const locusLength = referenceFrame.bpPerPixel * width;
50304
+ } = this.browser.genome.getChromosome(this.referenceFrame.chr);
50305
+ const locusLength = this.referenceFrame.bpPerPixel * width;
50387
50306
  const chrCoveragePercentage = locusLength / bpLength;
50388
50307
  let xPercentage = xNormalized;
50389
50308
 
@@ -50397,18 +50316,11 @@
50397
50316
 
50398
50317
  const ss = Math.round((xPercentage - chrCoveragePercentage / 2.0) * bpLength);
50399
50318
  const ee = Math.round((xPercentage + chrCoveragePercentage / 2.0) * bpLength);
50400
- referenceFrame.start = ss;
50401
- referenceFrame.end = ee;
50402
- referenceFrame.bpPerPixel = (ee - ss) / width;
50403
- this.browser.updateViews(true);
50319
+ this.referenceFrame.start = ss;
50320
+ this.referenceFrame.end = ee;
50321
+ this.referenceFrame.bpPerPixel = (ee - ss) / width;
50322
+ this.browser.updateViews(this.referenceFrame, this.browser.trackViews, true);
50404
50323
  }
50405
-
50406
- this.boundClickHandler = clickHandler.bind(this);
50407
- viewport.addEventListener('click', this.boundClickHandler);
50408
- }
50409
-
50410
- removeViewportClickHandler(viewport) {
50411
- viewport.removeEventListener('click', this.boundClickHandler);
50412
50324
  }
50413
50325
 
50414
50326
  setWidth(width) {
@@ -50426,14 +50338,22 @@
50426
50338
  context.restore();
50427
50339
  }
50428
50340
 
50429
- update(context, pixelWidth, pixelHeight, referenceFrame) {
50430
- this.$canvas.hide();
50431
- IGVGraphics.configureHighDPICanvas(context, pixelWidth, pixelHeight);
50341
+ repaint() {
50342
+ this.draw({
50343
+ referenceFrame: this.referenceFrame
50344
+ });
50345
+ }
50346
+
50347
+ draw(_ref) {
50348
+ let {
50349
+ referenceFrame
50350
+ } = _ref;
50351
+ IGVGraphics.configureHighDPICanvas(this.ideogram_ctx, this.$viewport.width(), this.$viewport.height());
50432
50352
  this.trackView.track.draw({
50433
- context,
50353
+ context: this.ideogram_ctx,
50434
50354
  referenceFrame,
50435
- pixelWidth,
50436
- pixelHeight
50355
+ pixelWidth: this.$viewport.width(),
50356
+ pixelHeight: this.$viewport.height()
50437
50357
  });
50438
50358
  }
50439
50359
 
@@ -50471,7 +50391,7 @@
50471
50391
  function createViewport(trackView, column, referenceFrame, width) {
50472
50392
  if ('ruler' === trackView.track.type) {
50473
50393
  return new RulerViewport(trackView, column, referenceFrame, width);
50474
- } else if ('ideogram' === trackView.track.type) {
50394
+ } else if ('ideogram' === trackView.track.id) {
50475
50395
  return new IdeogramViewport(trackView, column, referenceFrame, width);
50476
50396
  } else {
50477
50397
  return new TrackViewport(trackView, column, referenceFrame, width);
@@ -50998,7 +50918,6 @@
50998
50918
 
50999
50919
  class TrackView {
51000
50920
  constructor(browser, columnContainer, track) {
51001
- this.namespace = `trackview-${guid$2()}`;
51002
50921
  this.browser = browser;
51003
50922
  this.track = track;
51004
50923
  track.trackView = this;
@@ -51024,7 +50943,7 @@
51024
50943
 
51025
50944
  addDOMToColumnContainer(browser, columnContainer, referenceFrameList) {
51026
50945
  // Axis
51027
- this.axis = this.createAxis(browser, this.track); // Track Viewports
50946
+ this.axis = this.createAxis(browser, this.track); // Create a viewport for each reference frame
51028
50947
 
51029
50948
  this.viewports = [];
51030
50949
  const viewportWidth = browser.calculateViewportWidth(referenceFrameList.length);
@@ -51091,7 +51010,6 @@
51091
51010
  this.axis.remove(); // Track Viewports
51092
51011
 
51093
51012
  for (let viewport of this.viewports) {
51094
- viewport.removeMouseHandlers();
51095
51013
  viewport.$viewport.remove();
51096
51014
  } // SampleName Viewport
51097
51015
 
@@ -51209,7 +51127,7 @@
51209
51127
  $viewport.height(newHeight);
51210
51128
  }
51211
51129
 
51212
- this.sampleNameViewport.viewport.style.height = `${newHeight}px`; // If the track does not manage its own content height set it here
51130
+ this.sampleNameViewport.viewport.style.height = `${newHeight}px`; // If the track does not manage its own content height set it equal to the viewport height here
51213
51131
 
51214
51132
  if (typeof this.track.computePixelHeight !== "function") {
51215
51133
  for (let vp of this.viewports) {
@@ -51261,14 +51179,6 @@
51261
51179
  if (viewport.isLoading()) return true;
51262
51180
  }
51263
51181
  }
51264
-
51265
- resize(viewportWidth) {
51266
- for (let viewport of this.viewports) {
51267
- viewport.setWidth(viewportWidth);
51268
- }
51269
-
51270
- this.updateViews(true);
51271
- }
51272
51182
  /**
51273
51183
  * Repaint all viewports without loading any new data. Use this for events that change visual aspect of data,
51274
51184
  * e.g. color, sort order, etc, but do not change the genomic state.
@@ -51301,6 +51211,19 @@
51301
51211
  setTrackLabelName(name) {
51302
51212
  this.viewports.forEach(viewport => viewport.setTrackLabel(name));
51303
51213
  }
51214
+ /**
51215
+ * Called in response to a window resize event, change in # of multilocus panels, or other event that changes
51216
+ * the width of the track view.
51217
+ *
51218
+ * @param viewportWidth The width of each viewport in this track view.
51219
+ */
51220
+
51221
+
51222
+ resize(viewportWidth) {
51223
+ for (let viewport of this.viewports) {
51224
+ viewport.setWidth(viewportWidth);
51225
+ }
51226
+ }
51304
51227
  /**
51305
51228
  * Update viewports to reflect current genomic state, possibly loading additional data.
51306
51229
  *
@@ -51309,22 +51232,25 @@
51309
51232
  */
51310
51233
 
51311
51234
 
51312
- async updateViews(force) {
51235
+ async updateViews() {
51313
51236
  if (!(this.browser && this.browser.referenceFrameList)) return;
51314
51237
  const visibleViewports = this.viewports.filter(viewport => viewport.isVisible()); // Shift viewports left/right to current genomic state (pans canvas)
51315
51238
 
51316
- visibleViewports.forEach(viewport => viewport.shift());
51317
- const isDragging = this.browser.dragObject;
51239
+ visibleViewports.forEach(viewport => viewport.shift()); // If dragging (panning) return
51318
51240
 
51319
- if (isDragging) {
51241
+ if (this.browser.dragObject) {
51320
51242
  return;
51321
- } // rpv: viewports whose image (canvas) does not fully cover current genomic range
51243
+ } // Get viewports to repaint
51244
+
51322
51245
 
51246
+ let viewportsToRepaint = this.track.autoscale || this.track.autoscaleGroup || this.track.type === 'ruler' ? visibleViewports : visibleViewports.filter(vp => vp.needsRepaint()); // Filter zoomed out views. This has the side effect or turning off or no the zoomed out notice
51323
51247
 
51324
- const reloadableViewports = force ? visibleViewports : this.viewportsToReload(); // Trigger viewport to load features needed to cover current genomic range
51248
+ viewportsToRepaint = viewportsToRepaint.filter(viewport => viewport.checkZoomIn()); // Get viewports that require a data load
51249
+
51250
+ const viewportsToReload = viewportsToRepaint.filter(viewport => viewport.needsReload()); // Trigger viewport to load features needed to cover current genomic range
51325
51251
  // NOTE: these must be loaded synchronously, do not user Promise.all, not all file readers are thread safe
51326
51252
 
51327
- for (let viewport of reloadableViewports) {
51253
+ for (let viewport of viewportsToReload) {
51328
51254
  await viewport.loadFeatures();
51329
51255
  }
51330
51256
 
@@ -51333,7 +51259,7 @@
51333
51259
  // section depends on data from all the views. We only need to adjust this however if any data was loaded
51334
51260
  // (i.e. reloadableViewports.length > 0)
51335
51261
 
51336
- if (this.track && typeof this.track.variantRowCount === 'function' && reloadableViewports.length > 0) {
51262
+ if (this.track && typeof this.track.variantRowCount === 'function' && viewportsToReload.length > 0) {
51337
51263
  let maxRow = 0;
51338
51264
 
51339
51265
  for (let viewport of this.viewports) {
@@ -51378,23 +51304,10 @@
51378
51304
  } else {
51379
51305
  this.track.dataRange = doAutoscale(allFeatures);
51380
51306
  }
51381
- } // Must repaint all viewports if autoscaling. Always repaint ruler.
51382
-
51383
-
51384
- if (this.track.autoscale || this.track.autoscaleGroup || this.track.type === 'ruler' || force) {
51385
- for (let vp of visibleViewports) {
51386
- vp.repaint();
51387
- }
51388
- } else {
51389
- const reloadedViewports = new Set(reloadableViewports);
51390
-
51391
- for (let vp of visibleViewports) {
51392
- const invalid = vp.canvas && vp.canvas._data && vp.canvas._data.invalidate;
51307
+ }
51393
51308
 
51394
- if (invalid || reloadedViewports.has(vp)) {
51395
- vp.repaint();
51396
- }
51397
- }
51309
+ for (let vp of viewportsToRepaint) {
51310
+ vp.repaint();
51398
51311
  }
51399
51312
 
51400
51313
  this.adjustTrackHeight(); // Repaint sample names last
@@ -51417,24 +51330,22 @@
51417
51330
  }
51418
51331
  }
51419
51332
  /**
51420
- * Return a promise to get all in-view features. Used for group autoscaling.
51333
+ * Return a promise to get all in-view features across all viewports. Used for group autoscaling.
51421
51334
  */
51422
51335
 
51423
51336
 
51424
51337
  async getInViewFeatures() {
51425
51338
  if (!(this.browser && this.browser.referenceFrameList)) {
51426
51339
  return [];
51427
- } // List of viewports that need reloading
51428
-
51340
+ }
51429
51341
 
51430
- const rpV = this.viewportsToReload();
51431
- const promises = rpV.map(function (vp) {
51432
- return vp.loadFeatures();
51433
- });
51434
- await Promise.all(promises);
51435
51342
  let allFeatures = [];
51436
51343
 
51437
51344
  for (let vp of this.viewports) {
51345
+ if (vp.needsReload()) {
51346
+ await vp.loadFeatures();
51347
+ }
51348
+
51438
51349
  if (vp.featureCache && vp.featureCache.features) {
51439
51350
  const referenceFrame = vp.referenceFrame;
51440
51351
  const start = referenceFrame.start;
@@ -51488,27 +51399,6 @@
51488
51399
  }
51489
51400
  }
51490
51401
 
51491
- viewportsToReload(force) {
51492
- // List of viewports that need reloading
51493
- const viewports = this.viewports.filter(viewport => {
51494
- if (!viewport.isVisible()) {
51495
- return false;
51496
- }
51497
-
51498
- if (!viewport.checkZoomIn()) {
51499
- return false;
51500
- } else {
51501
- const referenceFrame = viewport.referenceFrame;
51502
- const chr = viewport.referenceFrame.chr;
51503
- const start = referenceFrame.start;
51504
- const end = start + referenceFrame.toBP($$1(viewport.contentDiv).width());
51505
- const bpPerPixel = referenceFrame.bpPerPixel;
51506
- return !viewport.featureCache || !viewport.featureCache.containsRange(chr, start, end, bpPerPixel);
51507
- }
51508
- });
51509
- return viewports;
51510
- }
51511
-
51512
51402
  createTrackScrollbar(browser) {
51513
51403
  const outerScroll = div$1();
51514
51404
  browser.columnContainer.querySelector('.igv-scrollbar-column').appendChild(outerScroll);
@@ -51602,7 +51492,7 @@
51602
51492
  }
51603
51493
 
51604
51494
  addTrackDragMouseHandlers(browser) {
51605
- if ('ideogram' === this.track.type || 'ruler' === this.track.type) ; else {
51495
+ if ('ideogram' === this.track.id || 'ruler' === this.track.id) ; else {
51606
51496
  let currentDragHandle = undefined; // Mouse Down
51607
51497
 
51608
51498
  this.boundTrackDragMouseDownHandler = trackDragMouseDownHandler.bind(this);
@@ -51666,7 +51556,7 @@
51666
51556
  }
51667
51557
 
51668
51558
  removeTrackDragMouseHandlers() {
51669
- if ('ideogram' === this.track.type || 'ruler' === this.track.type) ; else {
51559
+ if ('ideogram' === this.track.id || 'ruler' === this.track.id) ; else {
51670
51560
  this.dragHandle.removeEventListener('mousedown', this.boundTrackDragMouseDownHandler);
51671
51561
  document.removeEventListener('mouseup', this.boundDocumentTrackDragMouseUpHandler);
51672
51562
  this.dragHandle.removeEventListener('mouseup', this.boundTrackDragMouseEnterHandler);
@@ -52561,7 +52451,7 @@
52561
52451
  });
52562
52452
  }
52563
52453
 
52564
- findUTRs(exons, feature.cdStart, feature.cdEnd);
52454
+ findUTRs$1(exons, feature.cdStart, feature.cdEnd);
52565
52455
  feature.exons = exons;
52566
52456
  } // Optional extra columns
52567
52457
 
@@ -52666,7 +52556,7 @@
52666
52556
  });
52667
52557
  }
52668
52558
 
52669
- findUTRs(exons, cdStart, cdEnd);
52559
+ findUTRs$1(exons, cdStart, cdEnd);
52670
52560
  feature.exons = exons;
52671
52561
  return feature;
52672
52562
  }
@@ -52708,7 +52598,7 @@
52708
52598
  });
52709
52599
  }
52710
52600
 
52711
- findUTRs(exons, cdStart, cdEnd);
52601
+ findUTRs$1(exons, cdStart, cdEnd);
52712
52602
  feature.exons = exons;
52713
52603
  return feature;
52714
52604
  }
@@ -52749,12 +52639,12 @@
52749
52639
  });
52750
52640
  }
52751
52641
 
52752
- findUTRs(exons, cdStart, cdEnd);
52642
+ findUTRs$1(exons, cdStart, cdEnd);
52753
52643
  feature.exons = exons;
52754
52644
  return feature;
52755
52645
  }
52756
52646
 
52757
- function findUTRs(exons, cdStart, cdEnd) {
52647
+ function findUTRs$1(exons, cdStart, cdEnd) {
52758
52648
  for (let exon of exons) {
52759
52649
  const end = exon.end;
52760
52650
  const start = exon.start;
@@ -56313,7 +56203,7 @@
56313
56203
  this.genome = genome;
56314
56204
  this.sourceType = config.sourceType === undefined ? "file" : config.sourceType;
56315
56205
  this.maxWGCount = config.maxWGCount || DEFAULT_MAX_WG_COUNT;
56316
- const queryableFormats = new Set(["bigwig", "bw", "bigbed", "bb", "biginteract", "tdf"]);
56206
+ const queryableFormats = new Set(["bigwig", "bw", "bigbed", "bb", "biginteract", "biggenepred", "bignarrowpeak", "tdf"]);
56317
56207
 
56318
56208
  if (config.features && Array.isArray(config.features)) {
56319
56209
  // Explicit array of features
@@ -56724,10 +56614,8 @@
56724
56614
 
56725
56615
  }
56726
56616
 
56727
- //table chromatinInteract
56728
-
56729
56617
  function getDecoder(definedFieldCount, fieldCount, autoSql, format) {
56730
- if (autoSql && 'chromatinInteract' === autoSql.table || "biginteract" === format) {
56618
+ if ("biginteract" === format || autoSql && 'chromatinInteract' === autoSql.table || 'interact' === autoSql.table) {
56731
56619
  return decodeInteract;
56732
56620
  } else {
56733
56621
  const standardFieldCount = definedFieldCount - 3;
@@ -56774,6 +56662,7 @@
56774
56662
  });
56775
56663
  }
56776
56664
 
56665
+ findUTRs(exons, feature.cdStart, feature.cdEnd);
56777
56666
  feature.exons = exons;
56778
56667
  }
56779
56668
 
@@ -56790,7 +56679,29 @@
56790
56679
  }
56791
56680
  }
56792
56681
  };
56793
- }
56682
+ } //table chromatinInteract
56683
+ // "Chromatin interaction between two regions"
56684
+ // (
56685
+ // string chrom; "Chromosome (or contig, scaffold, etc.). For interchromosomal, use 2 records"
56686
+ // uint chromStart; "Start position of lower region. For interchromosomal, set to chromStart of this region"
56687
+ // uint chromEnd; "End position of upper region. For interchromosomal, set to chromEnd of this region"
56688
+ // string name; "Name of item, for display"
56689
+ // uint score; "Score from 0-1000"
56690
+ // double value; "Strength of interaction or other data value. Typically basis for score"
56691
+ // string exp; "Experiment name (metadata for filtering). Use . if not applicable"
56692
+ // string color; "Item color. Specified as r,g,b or hexadecimal #RRGGBB or html color name, as in //www.w3.org/TR/css3-color/#html4."
56693
+ // string region1Chrom; "Chromosome of lower region. For non-directional interchromosomal, chrom of this region."
56694
+ // uint region1Start; "Start position of lower/this region"
56695
+ // uint region1End; "End position in chromosome of lower/this region"
56696
+ // string region1Name; "Identifier of lower/this region"
56697
+ // string region1Strand; "Orientation of lower/this region: + or -. Use . if not applicable"
56698
+ // string region2Chrom; "Chromosome of upper region. For non-directional interchromosomal, chrom of other region"
56699
+ // uint region2Start; "Start position in chromosome of upper/this region"
56700
+ // uint region2End; "End position in chromosome of upper/this region"
56701
+ // string region2Name; "Identifier of upper/this region"
56702
+ // string region2Strand; "Orientation of upper/this region: + or -. Use . if not applicable"
56703
+ // )
56704
+
56794
56705
 
56795
56706
  function decodeInteract(feature, tokens) {
56796
56707
  feature.chr1 = tokens[5];
@@ -56807,6 +56718,25 @@
56807
56718
  }
56808
56719
  }
56809
56720
 
56721
+ function findUTRs(exons, cdStart, cdEnd) {
56722
+ for (let exon of exons) {
56723
+ const end = exon.end;
56724
+ const start = exon.start;
56725
+
56726
+ if (end < cdStart || start > cdEnd) {
56727
+ exon.utr = true;
56728
+ } else {
56729
+ if (cdStart >= start && cdStart <= end) {
56730
+ exon.cdStart = cdStart;
56731
+ }
56732
+
56733
+ if (cdEnd >= start && cdEnd <= end) {
56734
+ exon.cdEnd = cdEnd;
56735
+ }
56736
+ }
56737
+ }
56738
+ }
56739
+
56810
56740
  function scoreShade(score, color) {
56811
56741
  const alpha = Math.min(1, 0.11 + 0.89 * (score / 779));
56812
56742
  return alpha.toString();
@@ -58252,7 +58182,7 @@
58252
58182
  return features;
58253
58183
  }
58254
58184
 
58255
- supportsWholeGenome() {
58185
+ get supportsWholeGenome() {
58256
58186
  return true;
58257
58187
  }
58258
58188
 
@@ -58357,11 +58287,12 @@
58357
58287
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
58358
58288
  * THE SOFTWARE.
58359
58289
  */
58290
+ const bbFormats = new Set(['bigwig', 'bw', 'bigbed', 'bb', 'biginteract', 'biggenepred', 'bignarrowpeak']);
58360
58291
 
58361
58292
  function FeatureSource(config, genome) {
58362
58293
  const format = config.format ? config.format.toLowerCase() : undefined;
58363
58294
 
58364
- if ('bigwig' === format || 'bigbed' === format || 'bb' === format || "biginteract" === format) {
58295
+ if (bbFormats.has(format)) {
58365
58296
  return new BWSource(config, genome);
58366
58297
  } else if ("tdf" === format) {
58367
58298
  return new TDFSource(config, genome);
@@ -58370,30 +58301,6 @@
58370
58301
  }
58371
58302
  }
58372
58303
 
58373
- const pairs = [['A', 'T'], ['G', 'C'], ['Y', 'R'], ['W', 'S'], ['K', 'M'], ['D', 'H'], ['B', 'V']];
58374
- const complements = new Map();
58375
-
58376
- for (let p of pairs) {
58377
- const p1 = p[0];
58378
- const p2 = p[1];
58379
- complements.set(p1, p2);
58380
- complements.set(p2, p1);
58381
- complements.set(p1.toLowerCase(), p2.toLowerCase());
58382
- complements.set(p2.toLowerCase(), p1.toLowerCase());
58383
- }
58384
-
58385
- function reverseComplementSequence(sequence) {
58386
- let comp = '';
58387
- let idx = sequence.length;
58388
-
58389
- while (idx-- > 0) {
58390
- const base = sequence[idx];
58391
- comp += complements.has(base) ? complements.get(base) : base;
58392
- }
58393
-
58394
- return comp;
58395
- }
58396
-
58397
58304
  const GtexUtils = {
58398
58305
  getTissueInfo: function (datasetId, baseURL) {
58399
58306
  datasetId = datasetId || 'gtex_v8';
@@ -58489,7 +58396,7 @@
58489
58396
  // single-exon transcript
58490
58397
  const xLeft = Math.max(0, coord.px);
58491
58398
  const xRight = Math.min(pixelWidth, coord.px1);
58492
- const width = Math.max(coord.pw, xRight - xLeft);
58399
+ const width = xRight - xLeft;
58493
58400
  ctx.fillRect(xLeft, py, width, h); // Arrows
58494
58401
  // Do not draw if strand is not +/-
58495
58402
 
@@ -58634,8 +58541,8 @@
58634
58541
  const xleft = centerX - textBox.width / 2;
58635
58542
  const xright = centerX + textBox.width / 2;
58636
58543
 
58637
- if (options.labelAllFeatures || xleft > options.rowLastX[feature.row] || gtexSelection) {
58638
- options.rowLastX[feature.row] = xright;
58544
+ if (options.labelAllFeatures || xleft > options.rowLastLabelX[feature.row] || gtexSelection) {
58545
+ options.rowLastLabelX[feature.row] = xright;
58639
58546
  IGVGraphics.fillText(ctx, name, centerX, labelY, geneFontStyle, transform);
58640
58547
  }
58641
58548
  } finally {
@@ -58916,7 +58823,7 @@
58916
58823
  return this;
58917
58824
  }
58918
58825
 
58919
- supportsWholeGenome() {
58826
+ get supportsWholeGenome() {
58920
58827
  return (this.config.indexed === false || !this.config.indexURL) && this.config.supportsWholeGenome !== false;
58921
58828
  }
58922
58829
 
@@ -58975,27 +58882,31 @@
58975
58882
  if (featureList) {
58976
58883
  const rowFeatureCount = [];
58977
58884
  options.rowLastX = [];
58885
+ options.rowLastLabelX = [];
58978
58886
 
58979
58887
  for (let feature of featureList) {
58980
- const row = feature.row || 0;
58888
+ if (feature.start > bpStart && feature.end < bpEnd) {
58889
+ const row = this.displayMode === "COLLAPSED" ? 0 : feature.row || 0;
58981
58890
 
58982
- if (rowFeatureCount[row] === undefined) {
58983
- rowFeatureCount[row] = 1;
58984
- } else {
58985
- rowFeatureCount[row]++;
58986
- }
58891
+ if (rowFeatureCount[row] === undefined) {
58892
+ rowFeatureCount[row] = 1;
58893
+ } else {
58894
+ rowFeatureCount[row]++;
58895
+ }
58987
58896
 
58988
- options.rowLastX[row] = -Number.MAX_SAFE_INTEGER;
58897
+ options.rowLastX[row] = -Number.MAX_SAFE_INTEGER;
58898
+ options.rowLastLabelX[row] = -Number.MAX_SAFE_INTEGER;
58899
+ }
58989
58900
  }
58990
58901
 
58902
+ const pixelsPerFeature = pixelWidth / Math.max(...rowFeatureCount);
58991
58903
  let lastPxEnd = [];
58992
58904
 
58993
58905
  for (let feature of featureList) {
58994
58906
  if (feature.end < bpStart) continue;
58995
58907
  if (feature.start > bpEnd) break;
58996
58908
  const row = this.displayMode === 'COLLAPSED' ? 0 : feature.row;
58997
- const featureDensity = pixelWidth / rowFeatureCount[row];
58998
- options.drawLabel = options.labelAllFeatures || featureDensity > 10;
58909
+ options.drawLabel = options.labelAllFeatures || pixelsPerFeature > 10;
58999
58910
  const pxEnd = Math.ceil((feature.end - bpStart) / bpPerPixel);
59000
58911
  const last = lastPxEnd[row];
59001
58912
 
@@ -59069,15 +58980,9 @@
59069
58980
  for (let fd of featureData) {
59070
58981
  data.push(fd);
59071
58982
 
59072
- if (infoURL) {
59073
- if (fd.name && fd.name.toLowerCase() === "name" && fd.value && isString$3(fd.value) && !fd.value.startsWith("<")) {
59074
- const url = this.infoURL || this.config.infoURL;
59075
- const href = url.replace("$$", feature.name);
59076
- data.push({
59077
- name: "Info",
59078
- value: `<a target="_blank" href=${href}>${fd.value}</a>`
59079
- });
59080
- }
58983
+ if (infoURL && fd.name && fd.name.toLowerCase() === "name" && fd.value && isString$3(fd.value) && !fd.value.startsWith("<")) {
58984
+ const href = infoURL.replace("$$", feature.name);
58985
+ fd.value = `<a target=_blank href=${href}>${fd.value}</a>`;
59081
58986
  }
59082
58987
  } //Array.prototype.push.apply(data, featureData);
59083
58988
  // If we have clicked over an exon number it.
@@ -59151,17 +59056,31 @@
59151
59056
  }
59152
59057
 
59153
59058
  contextMenuItemList(clickState) {
59154
- if (isSecureContext()) {
59155
- const features = this.clickedFeatures(clickState);
59059
+ const features = this.clickedFeatures(clickState);
59156
59060
 
59157
- if (features.length > 1) {
59158
- features.sort((a, b) => a.end - a.start - (b.end - b.start));
59159
- }
59061
+ if (features.length > 1) {
59062
+ features.sort((a, b) => b.end - b.start - (a.end - a.start));
59063
+ }
59064
+
59065
+ const f = features[0]; // The shortest clicked feature
59066
+
59067
+ if (f.end - f.start <= 1000000) {
59068
+ const list = [{
59069
+ label: 'View feature sequence',
59070
+ click: async () => {
59071
+ let seq = await this.browser.genome.getSequence(f.chr, f.start, f.end);
59072
+
59073
+ if (f.strand === '-') {
59074
+ seq = reverseComplementSequence(seq);
59075
+ }
59160
59076
 
59161
- const f = features[0]; // The longest feature
59077
+ if (!seq) seq = "Unknown sequence";
59078
+ Alert.presentAlert(seq);
59079
+ }
59080
+ }];
59162
59081
 
59163
- if (f.end - f.start <= 1000000) {
59164
- return [{
59082
+ if (isSecureContext() && navigator.clipboard !== undefined) {
59083
+ list.push({
59165
59084
  label: 'Copy feature sequence',
59166
59085
  click: async () => {
59167
59086
  let seq = await this.browser.genome.getSequence(f.chr, f.start, f.end);
@@ -59170,14 +59089,21 @@
59170
59089
  seq = reverseComplementSequence(seq);
59171
59090
  }
59172
59091
 
59173
- navigator.clipboard.writeText(seq);
59092
+ try {
59093
+ await navigator.clipboard.writeText(seq);
59094
+ } catch (e) {
59095
+ console.error(e);
59096
+ Alert.presentAlert(`error copying sequence to clipboard ${e}`);
59097
+ }
59174
59098
  }
59175
- }, '<hr/>'];
59099
+ });
59176
59100
  }
59177
- } // Either not a secure context (i.e. http: protocol), or feature is too long
59178
-
59179
59101
 
59180
- return undefined;
59102
+ list.push('<hr/>');
59103
+ return list;
59104
+ } else {
59105
+ return undefined;
59106
+ }
59181
59107
  }
59182
59108
 
59183
59109
  description() {
@@ -59516,7 +59442,7 @@
59516
59442
  }
59517
59443
  }
59518
59444
 
59519
- supportsWholeGenome() {
59445
+ get supportsWholeGenome() {
59520
59446
  return !this.config.indexURL && this.config.supportsWholeGenome !== false;
59521
59447
  }
59522
59448
  /**
@@ -60075,7 +60001,7 @@
60075
60001
  }];
60076
60002
  }
60077
60003
 
60078
- supportsWholeGenome() {
60004
+ get supportsWholeGenome() {
60079
60005
  return (this.config.indexed === false || !this.config.indexURL) && this.config.supportsWholeGenome !== false;
60080
60006
  }
60081
60007
 
@@ -60295,7 +60221,7 @@
60295
60221
  }
60296
60222
  }
60297
60223
 
60298
- supportsWholeGenome() {
60224
+ get supportsWholeGenome() {
60299
60225
  return this.tracks.every(track => track.supportsWholeGenome());
60300
60226
  }
60301
60227
 
@@ -60433,7 +60359,7 @@
60433
60359
  return this;
60434
60360
  }
60435
60361
 
60436
- supportsWholeGenome() {
60362
+ get supportsWholeGenome() {
60437
60363
  return true;
60438
60364
  }
60439
60365
 
@@ -60854,7 +60780,7 @@
60854
60780
  items = items.concat(MenuUtils.numericDataMenuItems(this.trackView));
60855
60781
  }
60856
60782
 
60857
- if (this.browser.circularView && true === this.browser.circularViewVisible) {
60783
+ if (this.browser.circularView) {
60858
60784
  items.push('<hr/>');
60859
60785
  items.push({
60860
60786
  label: 'Add interactions to circular view',
@@ -60871,7 +60797,7 @@
60871
60797
 
60872
60798
  contextMenuItemList(clickState) {
60873
60799
  // Experimental JBrowse feature
60874
- if (this.browser.circularView && true === this.browser.circularViewVisible) {
60800
+ if (this.browser.circularView) {
60875
60801
  const viewport = clickState.viewport;
60876
60802
  const list = [];
60877
60803
  list.push({
@@ -60897,7 +60823,6 @@
60897
60823
 
60898
60824
  const inView = cachedFeatures.filter(f => f.drawState);
60899
60825
  if (inView.length === 0) return;
60900
- this.browser.circularViewVisible = true;
60901
60826
  const chords = makeBedPEChords(inView);
60902
60827
  sendChords(chords, this, refFrame, 0.5); //
60903
60828
  //
@@ -61368,7 +61293,7 @@
61368
61293
  return this;
61369
61294
  }
61370
61295
 
61371
- supportsWholeGenome() {
61296
+ get supportsWholeGenome() {
61372
61297
  return this.config.indexed === false || this.config.supportsWholeGenome === true;
61373
61298
  }
61374
61299
 
@@ -61859,7 +61784,7 @@
61859
61784
  } // Experimental JBrowse circular view integration
61860
61785
 
61861
61786
 
61862
- if (this.browser.circularView && true === this.browser.circularViewVisible) {
61787
+ if (this.browser.circularView) {
61863
61788
  menuItems.push('<hr>');
61864
61789
  menuItems.push({
61865
61790
  label: 'Add SVs to circular view',
@@ -61877,7 +61802,7 @@
61877
61802
 
61878
61803
  contextMenuItemList(clickState) {
61879
61804
  // Experimental JBrowse circular view integration
61880
- if (this.browser.circularView && true === this.browser.circularViewVisible) {
61805
+ if (this.browser.circularView) {
61881
61806
  const viewport = clickState.viewport;
61882
61807
  const list = [];
61883
61808
  list.push({
@@ -62407,7 +62332,7 @@
62407
62332
  return this;
62408
62333
  }
62409
62334
 
62410
- supportsWholeGenome() {
62335
+ get supportsWholeGenome() {
62411
62336
  return true;
62412
62337
  }
62413
62338
 
@@ -62973,7 +62898,7 @@
62973
62898
  return items;
62974
62899
  }
62975
62900
 
62976
- supportsWholeGenome() {
62901
+ get supportsWholeGenome() {
62977
62902
  return false;
62978
62903
  }
62979
62904
 
@@ -63334,12 +63259,15 @@
63334
63259
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
63335
63260
  * THE SOFTWARE.
63336
63261
  */
63262
+ /**
63263
+ * Class represents an ideogram of a chromsome cytobands. It is used for the header of a track panel.
63264
+ *
63265
+ */
63337
63266
 
63338
63267
  class IdeogramTrack {
63339
63268
  constructor(browser) {
63340
63269
  this.browser = browser;
63341
63270
  this.type = 'ideogram';
63342
- this.id = this.type;
63343
63271
  this.height = 16;
63344
63272
  this.order = Number.MIN_SAFE_INTEGER;
63345
63273
  this.disableButtons = true;
@@ -63599,7 +63527,7 @@
63599
63527
  return this;
63600
63528
  }
63601
63529
 
63602
- supportsWholeGenome() {
63530
+ get supportsWholeGenome() {
63603
63531
  return false;
63604
63532
  }
63605
63533
 
@@ -64640,23 +64568,6 @@
64640
64568
  });
64641
64569
  }
64642
64570
 
64643
- function adjustReferenceFrame(scaleFactor, referenceFrame, viewportWidth, alignmentStart, alignmentLength) {
64644
- referenceFrame.bpPerPixel *= scaleFactor;
64645
- const alignmentEE = alignmentStart + alignmentLength;
64646
- const alignmentCC = (alignmentStart + alignmentEE) / 2;
64647
- referenceFrame.start = alignmentCC - referenceFrame.bpPerPixel * (viewportWidth / 2);
64648
- referenceFrame.end = referenceFrame.start + referenceFrame.bpPerPixel * viewportWidth;
64649
- referenceFrame.locusSearchString = referenceFrame.getLocusString();
64650
- }
64651
-
64652
- function createReferenceFrameWithAlignment(genome, chromosomeName, bpp, viewportWidth, alignmentStart, alignmentLength) {
64653
- const alignmentEE = alignmentStart + alignmentLength;
64654
- const alignmentCC = (alignmentStart + alignmentEE) / 2;
64655
- const ss = alignmentCC - bpp * (viewportWidth / 2);
64656
- const ee = ss + bpp * viewportWidth;
64657
- return new ReferenceFrame(genome, chromosomeName, ss, ee, bpp);
64658
- }
64659
-
64660
64571
  const defaultNucleotideColors = {
64661
64572
  "A": "rgb( 0, 200, 0)",
64662
64573
  "C": "rgb( 0,0,200)",
@@ -65703,6 +65614,65 @@
65703
65614
  button.addEventListener('click', () => browser.saveSVGtoFile({}));
65704
65615
  };
65705
65616
 
65617
+ const viewportColumnManager = {
65618
+ createColumns: (columnContainer, count) => {
65619
+ for (let i = 0; i < count; i++) {
65620
+ if (0 === i) {
65621
+ createColumn(columnContainer, 'igv-column');
65622
+ } else {
65623
+ columnContainer.appendChild(div$1({
65624
+ class: 'igv-column-shim'
65625
+ }));
65626
+ createColumn(columnContainer, 'igv-column');
65627
+ }
65628
+ }
65629
+ },
65630
+ removeColumnAtIndex: (i, column) => {
65631
+ const shim = 0 === i ? column.nextElementSibling : column.previousElementSibling;
65632
+ column.remove();
65633
+ shim.remove();
65634
+ },
65635
+ insertAfter: referenceElement => {
65636
+ const shim = div$1({
65637
+ class: 'igv-column-shim'
65638
+ });
65639
+ insertElementAfter(shim, referenceElement);
65640
+ const column = div$1({
65641
+ class: 'igv-column'
65642
+ });
65643
+ insertElementAfter(column, shim);
65644
+ return column;
65645
+ },
65646
+ insertBefore: (referenceElement, count) => {
65647
+ for (let i = 0; i < count; i++) {
65648
+ const column = div$1({
65649
+ class: 'igv-column'
65650
+ });
65651
+ insertElementBefore(column, referenceElement);
65652
+
65653
+ if (count > 1 && i > 0) {
65654
+ const columnShim = div$1({
65655
+ class: 'igv-column-shim'
65656
+ });
65657
+ insertElementBefore(columnShim, column);
65658
+ }
65659
+ }
65660
+ },
65661
+ indexOfColumn: (columnContainer, column) => {
65662
+ const allColumns = columnContainer.querySelectorAll('.igv-column');
65663
+
65664
+ for (let i = 0; i < allColumns.length; i++) {
65665
+ const c = allColumns[i];
65666
+
65667
+ if (c === column) {
65668
+ return i;
65669
+ }
65670
+ }
65671
+
65672
+ return undefined;
65673
+ }
65674
+ };
65675
+
65706
65676
  /*
65707
65677
  * The MIT License (MIT)
65708
65678
  *
@@ -65953,7 +65923,7 @@
65953
65923
  }
65954
65924
  }
65955
65925
 
65956
- supportsWholeGenome() {
65926
+ get supportsWholeGenome() {
65957
65927
  return true;
65958
65928
  }
65959
65929
 
@@ -66485,7 +66455,9 @@
66485
66455
  // deferred because ideogram and ruler are treated as "tracks", and tracks require a reference frame
66486
66456
 
66487
66457
  if (false !== session.showIdeogram) {
66488
- this.trackViews.push(new TrackView(this, this.columnContainer, new IdeogramTrack(this)));
66458
+ const ideogramTrack = new IdeogramTrack(this);
66459
+ ideogramTrack.id = 'ideogram';
66460
+ this.trackViews.push(new TrackView(this, this.columnContainer, ideogramTrack));
66489
66461
  }
66490
66462
 
66491
66463
  if (false !== session.showRuler) {
@@ -66535,9 +66507,9 @@
66535
66507
  }
66536
66508
  }
66537
66509
 
66538
- await this.loadTrackList(trackConfigurations); // The ruler track is not explicitly loaded, but needs updated nonetheless.
66510
+ await this.loadTrackList(trackConfigurations); // The ruler and ideogram tracks are not explicitly loaded, but needs updated nonetheless.
66539
66511
 
66540
- for (let rtv of this.trackViews.filter(tv => tv.track.type === 'ruler')) {
66512
+ for (let rtv of this.trackViews.filter(tv => tv.track.type === 'ruler' || tv.track.type === 'ideogram')) {
66541
66513
  rtv.updateViews();
66542
66514
  }
66543
66515
 
@@ -67077,10 +67049,11 @@
67077
67049
  this.navbarManager.navbarDidResize(this.$navigation.width(), isWGV);
67078
67050
  }
67079
67051
 
67080
- await resize.call(this);
67052
+ resize.call(this);
67053
+ await this.updateViews();
67081
67054
  }
67082
67055
 
67083
- async updateViews(force) {
67056
+ async updateViews() {
67084
67057
  const trackViews = this.trackViews;
67085
67058
  this.updateLocusSearchWidget();
67086
67059
 
@@ -67091,7 +67064,7 @@
67091
67064
 
67092
67065
  if (this.dragObject) {
67093
67066
  for (let trackView of trackViews) {
67094
- await trackView.updateViews(force);
67067
+ await trackView.updateViews();
67095
67068
  }
67096
67069
  } else {
67097
67070
  // Group autoscale
@@ -67140,14 +67113,14 @@
67140
67113
  for (let trackView of groupTrackViews) {
67141
67114
  trackView.track.dataRange = dataRange;
67142
67115
  trackView.track.autoscale = false;
67143
- p.push(trackView.updateViews(force));
67116
+ p.push(trackView.updateViews());
67144
67117
  }
67145
67118
 
67146
67119
  await Promise.all(p);
67147
67120
  }
67148
67121
  }
67149
67122
 
67150
- await Promise.all(otherTracks.map(tv => tv.updateViews(force))); // for (let trackView of otherTracks) {
67123
+ await Promise.all(otherTracks.map(tv => tv.updateViews())); // for (let trackView of otherTracks) {
67151
67124
  // await trackView.updateViews(force);
67152
67125
  // }
67153
67126
  }
@@ -67216,42 +67189,6 @@
67216
67189
  referenceFrame.zoomWithScaleFactor(this, scaleFactor, viewportWidth, centerBPOrUndefined);
67217
67190
  }
67218
67191
  }
67219
-
67220
- async presentMultiLocusPanel(alignment, referenceFrameLeft) {
67221
- // account for reduced viewport width as a result of adding right mate pair panel
67222
- const viewportWidth = this.calculateViewportWidth(1 + this.referenceFrameList.length);
67223
- const scaleFactor = this.calculateViewportWidth(this.referenceFrameList.length) / this.calculateViewportWidth(1 + this.referenceFrameList.length);
67224
- adjustReferenceFrame(scaleFactor, referenceFrameLeft, viewportWidth, alignment.start, alignment.lengthOnRef); // create right mate pair reference frame
67225
-
67226
- const mateChrName = this.genome.getChromosomeName(alignment.mate.chr);
67227
- const referenceFrameRight = createReferenceFrameWithAlignment(this.genome, mateChrName, referenceFrameLeft.bpPerPixel, viewportWidth, alignment.mate.position, alignment.lengthOnRef); // add right mate panel beside left mate panel
67228
-
67229
- const indexLeft = this.referenceFrameList.indexOf(referenceFrameLeft);
67230
- const indexRight = 1 + this.referenceFrameList.indexOf(referenceFrameLeft);
67231
- const {
67232
- $viewport
67233
- } = this.trackViews[0].viewports[indexLeft];
67234
- const viewportColumn = viewportColumnManager.insertAfter($viewport.get(0).parentElement);
67235
-
67236
- if (indexRight === this.referenceFrameList.length) {
67237
- this.referenceFrameList.push(referenceFrameRight);
67238
-
67239
- for (let trackView of this.trackViews) {
67240
- const viewport = createViewport(trackView, viewportColumn, referenceFrameRight);
67241
- trackView.viewports.push(viewport);
67242
- }
67243
- } else {
67244
- this.referenceFrameList.splice(indexRight, 0, referenceFrameRight);
67245
-
67246
- for (let trackView of this.trackViews) {
67247
- const viewport = createViewport(trackView, viewportColumn, referenceFrameRight);
67248
- trackView.viewports.splice(indexRight, 0, viewport);
67249
- }
67250
- }
67251
-
67252
- this.centerLineList = this.createCenterLineList(this.columnContainer);
67253
- await resize.call(this);
67254
- }
67255
67192
  /**
67256
67193
  * Add a new multi-locus panel for the specified region
67257
67194
  * @param chr
@@ -67298,6 +67235,7 @@
67298
67235
 
67299
67236
  this.centerLineList = this.createCenterLineList(this.columnContainer);
67300
67237
  resize.call(this);
67238
+ await this.updateViews(true);
67301
67239
  }
67302
67240
 
67303
67241
  async removeMultiLocusPanel(referenceFrame) {
@@ -67635,11 +67573,6 @@
67635
67573
  const surl = (idx > 0 ? path.substring(0, idx) : path) + "?sessionURL=blob:" + this.compressedSession();
67636
67574
  return surl;
67637
67575
  }
67638
-
67639
- currentReferenceFrames() {
67640
- const anyTrackView = this.trackViews[0];
67641
- return anyTrackView.viewports.map(vp => vp.referenceFrame);
67642
- }
67643
67576
  /**
67644
67577
  * Record a mouse click on a specific viewport. This might be the start of a drag operation. Dragging
67645
67578
  * (panning) is handled here so that the mouse can move out of a specific viewport (e.g. stray into another
@@ -67674,10 +67607,22 @@
67674
67607
  this.fireEvent('trackdragend');
67675
67608
  }
67676
67609
  }
67610
+ /**
67611
+ * Track drag here refers to vertical dragging to reorder tracks, not horizontal panning.
67612
+ *
67613
+ * @param trackView
67614
+ */
67615
+
67677
67616
 
67678
67617
  startTrackDrag(trackView) {
67679
67618
  this.dragTrack = trackView;
67680
67619
  }
67620
+ /**
67621
+ * Track drag here refers to vertical dragging to reorder tracks, not horizontal panning.
67622
+ *
67623
+ * @param dragDestination
67624
+ */
67625
+
67681
67626
 
67682
67627
  updateTrackDrag(dragDestination) {
67683
67628
  if (dragDestination && this.dragTrack) {
@@ -67825,6 +67770,8 @@
67825
67770
  }
67826
67771
 
67827
67772
  createCircularView(container, show) {
67773
+ show = show === true; // convert undefined to boolean
67774
+
67828
67775
  this.circularView = createCircularView(container, this);
67829
67776
  this.circularViewControl = new CircularViewControl(this.$toggle_button_container.get(0), this);
67830
67777
  this.circularView.setAssembly({
@@ -67832,7 +67779,8 @@
67832
67779
  id: this.genome.id,
67833
67780
  chromosomes: makeCircViewChromosomes(this.genome)
67834
67781
  });
67835
- this.circularViewVisible = show === true;
67782
+ this.circularViewVisible = show;
67783
+ return this.circularView;
67836
67784
  }
67837
67785
 
67838
67786
  get circularViewVisible() {
@@ -67885,7 +67833,8 @@
67885
67833
  }
67886
67834
  }
67887
67835
 
67888
- this.updateUIWithReferenceFrameList();
67836
+ this.updateUIWithReferenceFrameList(); //TODO -- update view only if needed. Reducing size never needed. Increasing size maybe
67837
+
67889
67838
  await this.updateViews(true);
67890
67839
  }
67891
67840