igv 2.11.2 → 2.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -10
- package/dist/igv.esm.js +993 -1118
- package/dist/igv.esm.min.js +9 -9
- package/dist/igv.esm.min.js.map +1 -1
- package/dist/igv.js +982 -1041
- package/dist/igv.min.js +10 -10
- package/dist/igv.min.js.map +1 -1
- package/package.json +4 -4
package/dist/igv.js
CHANGED
|
@@ -17943,7 +17943,7 @@
|
|
|
17943
17943
|
* @constructor
|
|
17944
17944
|
*/
|
|
17945
17945
|
|
|
17946
|
-
class FeatureCache {
|
|
17946
|
+
class FeatureCache$1 {
|
|
17947
17947
|
constructor(featureList, genome, range) {
|
|
17948
17948
|
featureList = featureList || [];
|
|
17949
17949
|
this.treeMap = this.buildTreeMap(featureList, genome);
|
|
@@ -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,6 @@
|
|
|
20043
20044
|
case "bed":
|
|
20044
20045
|
case "bigbed":
|
|
20045
20046
|
case "bb":
|
|
20046
|
-
case "biginteract":
|
|
20047
20047
|
return "bedtype";
|
|
20048
20048
|
|
|
20049
20049
|
default:
|
|
@@ -20287,6 +20287,30 @@
|
|
|
20287
20287
|
return window.location.protocol === "https:" || window.location.hostname === "localhost";
|
|
20288
20288
|
}
|
|
20289
20289
|
|
|
20290
|
+
const pairs = [['A', 'T'], ['G', 'C'], ['Y', 'R'], ['W', 'S'], ['K', 'M'], ['D', 'H'], ['B', 'V']];
|
|
20291
|
+
const complements = new Map();
|
|
20292
|
+
|
|
20293
|
+
for (let p of pairs) {
|
|
20294
|
+
const p1 = p[0];
|
|
20295
|
+
const p2 = p[1];
|
|
20296
|
+
complements.set(p1, p2);
|
|
20297
|
+
complements.set(p2, p1);
|
|
20298
|
+
complements.set(p1.toLowerCase(), p2.toLowerCase());
|
|
20299
|
+
complements.set(p2.toLowerCase(), p1.toLowerCase());
|
|
20300
|
+
}
|
|
20301
|
+
|
|
20302
|
+
function reverseComplementSequence(sequence) {
|
|
20303
|
+
let comp = '';
|
|
20304
|
+
let idx = sequence.length;
|
|
20305
|
+
|
|
20306
|
+
while (idx-- > 0) {
|
|
20307
|
+
const base = sequence[idx];
|
|
20308
|
+
comp += complements.has(base) ? complements.get(base) : base;
|
|
20309
|
+
}
|
|
20310
|
+
|
|
20311
|
+
return comp;
|
|
20312
|
+
}
|
|
20313
|
+
|
|
20290
20314
|
/*
|
|
20291
20315
|
* The MIT License (MIT)
|
|
20292
20316
|
*
|
|
@@ -20440,16 +20464,21 @@
|
|
|
20440
20464
|
const viewport = clickState.viewport;
|
|
20441
20465
|
|
|
20442
20466
|
if (viewport.referenceFrame.bpPerPixel <= 1) {
|
|
20467
|
+
const pixelWidth = viewport.getWidth();
|
|
20468
|
+
const bpWindow = pixelWidth * viewport.referenceFrame.bpPerPixel;
|
|
20469
|
+
const chr = viewport.referenceFrame.chr;
|
|
20470
|
+
const start = Math.floor(viewport.referenceFrame.start);
|
|
20471
|
+
const end = Math.ceil(start + bpWindow);
|
|
20443
20472
|
const items = [{
|
|
20444
|
-
label: 'View visible sequence...',
|
|
20473
|
+
label: this.reversed ? 'View visible sequence (reversed)...' : 'View visible sequence...',
|
|
20445
20474
|
click: async () => {
|
|
20446
|
-
|
|
20447
|
-
|
|
20448
|
-
|
|
20449
|
-
|
|
20450
|
-
|
|
20451
|
-
|
|
20452
|
-
Alert.presentAlert(
|
|
20475
|
+
let seq = await this.browser.genome.sequence.getSequence(chr, start, end);
|
|
20476
|
+
|
|
20477
|
+
if (this.reversed) {
|
|
20478
|
+
seq = reverseComplementSequence(seq);
|
|
20479
|
+
}
|
|
20480
|
+
|
|
20481
|
+
Alert.presentAlert(seq);
|
|
20453
20482
|
}
|
|
20454
20483
|
}];
|
|
20455
20484
|
|
|
@@ -20457,13 +20486,18 @@
|
|
|
20457
20486
|
items.push({
|
|
20458
20487
|
label: 'Copy visible sequence',
|
|
20459
20488
|
click: async () => {
|
|
20460
|
-
|
|
20461
|
-
|
|
20462
|
-
|
|
20463
|
-
|
|
20464
|
-
|
|
20465
|
-
|
|
20466
|
-
|
|
20489
|
+
let seq = await this.browser.genome.sequence.getSequence(chr, start, end);
|
|
20490
|
+
|
|
20491
|
+
if (this.reversed) {
|
|
20492
|
+
seq = reverseComplementSequence(seq);
|
|
20493
|
+
}
|
|
20494
|
+
|
|
20495
|
+
try {
|
|
20496
|
+
await navigator.clipboard.writeText(seq);
|
|
20497
|
+
} catch (e) {
|
|
20498
|
+
console.error(e);
|
|
20499
|
+
Alert.presentAlert(`error copying sequence to clipboard ${e}`);
|
|
20500
|
+
}
|
|
20467
20501
|
}
|
|
20468
20502
|
});
|
|
20469
20503
|
}
|
|
@@ -20604,7 +20638,7 @@
|
|
|
20604
20638
|
}
|
|
20605
20639
|
}
|
|
20606
20640
|
|
|
20607
|
-
supportsWholeGenome() {
|
|
20641
|
+
get supportsWholeGenome() {
|
|
20608
20642
|
return false;
|
|
20609
20643
|
}
|
|
20610
20644
|
|
|
@@ -20672,12 +20706,13 @@
|
|
|
20672
20706
|
});
|
|
20673
20707
|
this.$viewport.append(this.$content);
|
|
20674
20708
|
this.$content.height(this.$viewport.height());
|
|
20675
|
-
this.contentDiv = this.$content.get(0);
|
|
20676
|
-
this.$
|
|
20677
|
-
|
|
20678
|
-
this.canvas = this.$canvas.get(0)
|
|
20679
|
-
this.ctx = this.canvas.getContext("2d")
|
|
20680
|
-
|
|
20709
|
+
this.contentDiv = this.$content.get(0); // this.$canvas = $('<canvas>')
|
|
20710
|
+
// this.$content.append(this.$canvas)
|
|
20711
|
+
//
|
|
20712
|
+
// this.canvas = this.$canvas.get(0)
|
|
20713
|
+
// this.ctx = this.canvas.getContext("2d")
|
|
20714
|
+
|
|
20715
|
+
this.$viewport.width(width);
|
|
20681
20716
|
this.initializationHelper();
|
|
20682
20717
|
}
|
|
20683
20718
|
|
|
@@ -20733,14 +20768,13 @@
|
|
|
20733
20768
|
console.log('Viewport - draw(drawConfiguration, features, roiFeatures)');
|
|
20734
20769
|
}
|
|
20735
20770
|
|
|
20736
|
-
checkContentHeight() {
|
|
20771
|
+
checkContentHeight(features) {
|
|
20737
20772
|
let track = this.trackView.track;
|
|
20773
|
+
features = features || this.cachedFeatures;
|
|
20738
20774
|
|
|
20739
20775
|
if ("FILL" === track.displayMode) {
|
|
20740
20776
|
this.setContentHeight(this.$viewport.height());
|
|
20741
20777
|
} else if (typeof track.computePixelHeight === 'function') {
|
|
20742
|
-
let features = this.cachedFeatures;
|
|
20743
|
-
|
|
20744
20778
|
if (features && features.length > 0) {
|
|
20745
20779
|
let requiredContentHeight = track.computePixelHeight(features);
|
|
20746
20780
|
let currentContentHeight = this.$content.height();
|
|
@@ -20760,7 +20794,6 @@
|
|
|
20760
20794
|
// Maximum height of a canvas is ~32,000 pixels on Chrome, possibly smaller on other platforms
|
|
20761
20795
|
contentHeight = Math.min(contentHeight, 32000);
|
|
20762
20796
|
this.$content.height(contentHeight);
|
|
20763
|
-
if (this.tile) this.tile.invalidate = true;
|
|
20764
20797
|
}
|
|
20765
20798
|
|
|
20766
20799
|
isLoading() {
|
|
@@ -20775,8 +20808,6 @@
|
|
|
20775
20808
|
|
|
20776
20809
|
setWidth(width) {
|
|
20777
20810
|
this.$viewport.width(width);
|
|
20778
|
-
this.canvas.style.width = `${width}px`;
|
|
20779
|
-
this.canvas.setAttribute('width', width);
|
|
20780
20811
|
}
|
|
20781
20812
|
|
|
20782
20813
|
getWidth() {
|
|
@@ -20804,7 +20835,6 @@
|
|
|
20804
20835
|
this.popover.dispose();
|
|
20805
20836
|
}
|
|
20806
20837
|
|
|
20807
|
-
this.removeMouseHandlers();
|
|
20808
20838
|
this.$viewport.get(0).remove(); // Null out all properties -- this should not be neccessary, but just in case there is a
|
|
20809
20839
|
// reference to self somewhere we want to free memory.
|
|
20810
20840
|
|
|
@@ -23061,7 +23091,7 @@
|
|
|
23061
23091
|
}
|
|
23062
23092
|
};
|
|
23063
23093
|
|
|
23064
|
-
const _version = "2.
|
|
23094
|
+
const _version = "2.12.0";
|
|
23065
23095
|
|
|
23066
23096
|
function version$1() {
|
|
23067
23097
|
return _version;
|
|
@@ -23411,6 +23441,7 @@
|
|
|
23411
23441
|
}
|
|
23412
23442
|
|
|
23413
23443
|
async getSequence(chr, start, end) {
|
|
23444
|
+
chr = this.getChromosomeName(chr);
|
|
23414
23445
|
return this.sequence.getSequence(chr, start, end);
|
|
23415
23446
|
}
|
|
23416
23447
|
|
|
@@ -23537,15 +23568,13 @@
|
|
|
23537
23568
|
});
|
|
23538
23569
|
this.$viewport.append(this.$spinner);
|
|
23539
23570
|
this.$spinner.append($$1('<div>'));
|
|
23540
|
-
const
|
|
23541
|
-
track
|
|
23542
|
-
} = this.trackView;
|
|
23571
|
+
const track = this.trackView.track;
|
|
23543
23572
|
|
|
23544
23573
|
if ('sequence' !== track.type) {
|
|
23545
23574
|
this.$zoomInNotice = this.createZoomInNotice(this.$content);
|
|
23546
23575
|
}
|
|
23547
23576
|
|
|
23548
|
-
if (track.name && "sequence" !== track.
|
|
23577
|
+
if (track.name && "sequence" !== track.id) {
|
|
23549
23578
|
this.$trackLabel = $$1('<div class="igv-track-label">');
|
|
23550
23579
|
this.$viewport.append(this.$trackLabel);
|
|
23551
23580
|
this.setTrackLabel(track.name);
|
|
@@ -23559,6 +23588,11 @@
|
|
|
23559
23588
|
this.addMouseHandlers();
|
|
23560
23589
|
}
|
|
23561
23590
|
|
|
23591
|
+
setContentHeight(contentHeight) {
|
|
23592
|
+
super.setContentHeight(contentHeight);
|
|
23593
|
+
if (this.featureCache) this.featureCache.redraw = true;
|
|
23594
|
+
}
|
|
23595
|
+
|
|
23562
23596
|
setTrackLabel(label) {
|
|
23563
23597
|
this.$trackLabel.empty();
|
|
23564
23598
|
this.$trackLabel.html(label);
|
|
@@ -23575,55 +23609,72 @@
|
|
|
23575
23609
|
this.$spinner.hide();
|
|
23576
23610
|
}
|
|
23577
23611
|
}
|
|
23612
|
+
/**
|
|
23613
|
+
* Test to determine if we are zoomed in far enough to see features. Applicable to tracks with visibility windows.
|
|
23614
|
+
*
|
|
23615
|
+
* As a side effect the viewports canvas is removed if zoomed out.
|
|
23616
|
+
*
|
|
23617
|
+
* @returns {boolean} true if we are zoomed in past visibility window, false otherwise
|
|
23618
|
+
*/
|
|
23578
23619
|
|
|
23579
|
-
checkZoomIn() {
|
|
23580
|
-
const showZoomInNotice = () => {
|
|
23581
|
-
const referenceFrame = this.referenceFrame;
|
|
23582
23620
|
|
|
23583
|
-
|
|
23621
|
+
checkZoomIn() {
|
|
23622
|
+
const zoomedOutOfWindow = () => {
|
|
23623
|
+
if (this.referenceFrame.chr.toLowerCase() === "all" && !this.trackView.track.supportsWholeGenome) {
|
|
23584
23624
|
return true;
|
|
23585
23625
|
} else {
|
|
23586
23626
|
const visibilityWindow = this.trackView.track.visibilityWindow;
|
|
23587
|
-
return visibilityWindow !== undefined && visibilityWindow > 0 && referenceFrame.bpPerPixel * this.$viewport.width() > visibilityWindow;
|
|
23627
|
+
return visibilityWindow !== undefined && visibilityWindow > 0 && this.referenceFrame.bpPerPixel * this.$viewport.width() > visibilityWindow;
|
|
23588
23628
|
}
|
|
23589
23629
|
};
|
|
23590
23630
|
|
|
23631
|
+
if (this.trackView.track && "sequence" === this.trackView.track.type && this.referenceFrame.bpPerPixel > 1) {
|
|
23632
|
+
$$1(this.canvas).remove();
|
|
23633
|
+
this.canvas = undefined; //this.featureCache = undefined
|
|
23634
|
+
|
|
23635
|
+
return false;
|
|
23636
|
+
}
|
|
23637
|
+
|
|
23591
23638
|
if (!this.viewIsReady()) {
|
|
23592
23639
|
return false;
|
|
23593
23640
|
}
|
|
23594
23641
|
|
|
23595
|
-
if (
|
|
23596
|
-
|
|
23597
|
-
|
|
23598
|
-
|
|
23599
|
-
|
|
23600
|
-
|
|
23601
|
-
}
|
|
23642
|
+
if (zoomedOutOfWindow()) {
|
|
23643
|
+
// Out of visibility window
|
|
23644
|
+
if (this.canvas) {
|
|
23645
|
+
$$1(this.canvas).remove();
|
|
23646
|
+
this.canvas = undefined; //this.featureCache = undefined
|
|
23647
|
+
}
|
|
23602
23648
|
|
|
23603
|
-
|
|
23649
|
+
if (this.trackView.track.autoHeight) {
|
|
23650
|
+
const minHeight = this.trackView.minHeight || 0;
|
|
23651
|
+
this.setContentHeight(minHeight);
|
|
23652
|
+
}
|
|
23604
23653
|
|
|
23605
|
-
|
|
23606
|
-
|
|
23607
|
-
|
|
23608
|
-
}
|
|
23654
|
+
if (this.$zoomInNotice) {
|
|
23655
|
+
this.$zoomInNotice.show();
|
|
23656
|
+
}
|
|
23609
23657
|
|
|
23610
|
-
|
|
23611
|
-
|
|
23658
|
+
return false;
|
|
23659
|
+
} else {
|
|
23660
|
+
if (this.$zoomInNotice) {
|
|
23612
23661
|
this.$zoomInNotice.hide();
|
|
23613
|
-
return true;
|
|
23614
23662
|
}
|
|
23615
|
-
}
|
|
23616
23663
|
|
|
23617
|
-
|
|
23664
|
+
return true;
|
|
23665
|
+
}
|
|
23618
23666
|
}
|
|
23667
|
+
/**
|
|
23668
|
+
* Adjust the canvas to the current genomic state.
|
|
23669
|
+
*/
|
|
23670
|
+
|
|
23619
23671
|
|
|
23620
23672
|
shift() {
|
|
23621
|
-
const
|
|
23622
|
-
const referenceFrame = self.referenceFrame;
|
|
23673
|
+
const referenceFrame = this.referenceFrame;
|
|
23623
23674
|
|
|
23624
|
-
if (
|
|
23625
|
-
const pixelOffset = Math.round((
|
|
23626
|
-
|
|
23675
|
+
if (this.canvas && this.canvas._data && this.canvas._data.chr === this.referenceFrame.chr && this.canvas._data.bpPerPixel === referenceFrame.bpPerPixel) {
|
|
23676
|
+
const pixelOffset = Math.round((this.canvas._data.startBP - referenceFrame.start) / referenceFrame.bpPerPixel);
|
|
23677
|
+
this.canvas.style.left = pixelOffset + "px";
|
|
23627
23678
|
}
|
|
23628
23679
|
}
|
|
23629
23680
|
|
|
@@ -23649,9 +23700,10 @@
|
|
|
23649
23700
|
this.startSpinner();
|
|
23650
23701
|
|
|
23651
23702
|
try {
|
|
23652
|
-
const
|
|
23703
|
+
const track = this.trackView.track;
|
|
23704
|
+
const features = await this.getFeatures(track, chr, bpStart, bpEnd, referenceFrame.bpPerPixel);
|
|
23653
23705
|
let roiFeatures = [];
|
|
23654
|
-
const roi = mergeArrays(this.browser.roi,
|
|
23706
|
+
const roi = mergeArrays(this.browser.roi, track.roi);
|
|
23655
23707
|
|
|
23656
23708
|
if (roi) {
|
|
23657
23709
|
for (let r of roi) {
|
|
@@ -23663,11 +23715,13 @@
|
|
|
23663
23715
|
}
|
|
23664
23716
|
}
|
|
23665
23717
|
|
|
23666
|
-
|
|
23718
|
+
const mr = track && ("wig" === track.type || "merged" === track.type); // wig tracks are potentially multiresolution (e.g. bigwig)
|
|
23719
|
+
|
|
23720
|
+
this.featureCache = new FeatureCache(chr, bpStart, bpEnd, referenceFrame.bpPerPixel, features, roiFeatures, mr);
|
|
23667
23721
|
this.loading = false;
|
|
23668
23722
|
this.hideMessage();
|
|
23669
23723
|
this.stopSpinner();
|
|
23670
|
-
return this.
|
|
23724
|
+
return this.featureCache;
|
|
23671
23725
|
} catch (error) {
|
|
23672
23726
|
// Track might have been removed during load
|
|
23673
23727
|
if (this.trackView && this.trackView.disposed !== true) {
|
|
@@ -23680,38 +23734,32 @@
|
|
|
23680
23734
|
this.stopSpinner();
|
|
23681
23735
|
}
|
|
23682
23736
|
}
|
|
23737
|
+
/**
|
|
23738
|
+
* Repaint the canvas using the cached features
|
|
23739
|
+
*
|
|
23740
|
+
*/
|
|
23683
23741
|
|
|
23684
|
-
|
|
23685
|
-
|
|
23742
|
+
|
|
23743
|
+
repaint() {
|
|
23744
|
+
if (undefined === this.featureCache) {
|
|
23686
23745
|
return;
|
|
23687
23746
|
}
|
|
23688
23747
|
|
|
23689
23748
|
let {
|
|
23690
23749
|
features,
|
|
23691
|
-
roiFeatures
|
|
23692
|
-
|
|
23693
|
-
|
|
23694
|
-
endBP
|
|
23695
|
-
} = this.tile; // const isWGV = GenomeUtils.isWholeGenomeView(this.browser.referenceFrameList[0].chr)
|
|
23696
|
-
|
|
23697
|
-
const isWGV = GenomeUtils.isWholeGenomeView(this.referenceFrame.chr);
|
|
23698
|
-
let pixelWidth;
|
|
23699
|
-
|
|
23700
|
-
if (isWGV) {
|
|
23701
|
-
bpPerPixel = this.referenceFrame.end / this.$viewport.width();
|
|
23702
|
-
startBP = 0;
|
|
23703
|
-
endBP = this.referenceFrame.end;
|
|
23704
|
-
pixelWidth = this.$viewport.width();
|
|
23705
|
-
} else {
|
|
23706
|
-
pixelWidth = Math.ceil((endBP - startBP) / bpPerPixel);
|
|
23707
|
-
} // For deep tracks we paint a canvas == 3*viewportHeight centered on the current vertical scroll position
|
|
23750
|
+
roiFeatures
|
|
23751
|
+
} = this.featureCache; //this.tile.bpPerPixel = this.referenceFrame.bpPerPixel
|
|
23752
|
+
// const isWGV = GenomeUtils.isWholeGenomeView(this.browser.referenceFrameList[0].chr)
|
|
23708
23753
|
|
|
23754
|
+
const isWGV = GenomeUtils.isWholeGenomeView(this.referenceFrame.chr); // Canvas dimensions. There is no left-right panning for WGV so canvas width is viewport width.
|
|
23755
|
+
// For deep tracks we paint a canvas == 3*viewportHeight centered on the current vertical scroll position
|
|
23709
23756
|
|
|
23757
|
+
const pixelWidth = isWGV ? this.$viewport.width() : 3 * this.$viewport.width();
|
|
23710
23758
|
const viewportHeight = this.$viewport.height();
|
|
23711
23759
|
const contentHeight = this.getContentHeight();
|
|
23712
23760
|
const minHeight = roiFeatures ? Math.max(contentHeight, viewportHeight) : contentHeight; // Need to fill viewport for ROIs.
|
|
23713
23761
|
|
|
23714
|
-
|
|
23762
|
+
const pixelHeight = Math.min(minHeight, 3 * viewportHeight);
|
|
23715
23763
|
|
|
23716
23764
|
if (0 === pixelWidth || 0 === pixelHeight) {
|
|
23717
23765
|
if (this.canvas) {
|
|
@@ -23721,26 +23769,22 @@
|
|
|
23721
23769
|
return;
|
|
23722
23770
|
}
|
|
23723
23771
|
|
|
23724
|
-
const canvasTop = Math.max(0, -this.$content.position().top - viewportHeight);
|
|
23725
|
-
|
|
23726
|
-
|
|
23727
|
-
|
|
23728
|
-
|
|
23729
|
-
devicePixelRatio = window.devicePixelRatio;
|
|
23730
|
-
} else {
|
|
23731
|
-
devicePixelRatio = this.trackView.track.supportHiDPI === false ? 1 : window.devicePixelRatio;
|
|
23732
|
-
}
|
|
23733
|
-
|
|
23734
|
-
const pixelXOffset = Math.round((startBP - this.referenceFrame.start) / this.referenceFrame.bpPerPixel);
|
|
23772
|
+
const canvasTop = Math.max(0, -this.$content.position().top - viewportHeight);
|
|
23773
|
+
const bpPerPixel = this.referenceFrame.bpPerPixel;
|
|
23774
|
+
const startBP = this.referenceFrame.start - (isWGV ? 0 : pixelWidth / 3 * bpPerPixel);
|
|
23775
|
+
const endBP = this.referenceFrame.end + (isWGV ? 0 : pixelWidth / 3 * bpPerPixel);
|
|
23776
|
+
const pixelXOffset = Math.round((startBP - this.referenceFrame.start) / bpPerPixel);
|
|
23735
23777
|
const newCanvas = $$1('<canvas class="igv-canvas">').get(0);
|
|
23736
|
-
const ctx = newCanvas.getContext("2d");
|
|
23737
23778
|
newCanvas.style.width = pixelWidth + "px";
|
|
23738
23779
|
newCanvas.style.height = pixelHeight + "px";
|
|
23780
|
+
newCanvas.style.left = pixelXOffset + "px";
|
|
23781
|
+
newCanvas.style.top = canvasTop + "px"; // Always use high DPI if in "FILL" display mode, otherwise use track setting;
|
|
23782
|
+
|
|
23783
|
+
const devicePixelRatio = "FILL" === this.trackView.track.displayMode || this.trackView.track.supportHiDPI !== false ? window.devicePixelRatio : 1;
|
|
23739
23784
|
newCanvas.width = devicePixelRatio * pixelWidth;
|
|
23740
23785
|
newCanvas.height = devicePixelRatio * pixelHeight;
|
|
23786
|
+
const ctx = newCanvas.getContext("2d");
|
|
23741
23787
|
ctx.scale(devicePixelRatio, devicePixelRatio);
|
|
23742
|
-
newCanvas.style.left = pixelXOffset + "px";
|
|
23743
|
-
newCanvas.style.top = canvasTop + "px";
|
|
23744
23788
|
ctx.translate(0, -canvasTop);
|
|
23745
23789
|
const drawConfiguration = {
|
|
23746
23790
|
context: ctx,
|
|
@@ -23757,20 +23801,32 @@
|
|
|
23757
23801
|
viewportWidth: this.$viewport.width()
|
|
23758
23802
|
};
|
|
23759
23803
|
this.draw(drawConfiguration, features, roiFeatures);
|
|
23760
|
-
this.
|
|
23761
|
-
|
|
23762
|
-
bottom: canvasTop + pixelHeight
|
|
23763
|
-
};
|
|
23804
|
+
this.featureCache.canvasTop = canvasTop;
|
|
23805
|
+
this.featureCache.height = pixelHeight;
|
|
23764
23806
|
|
|
23765
|
-
if (this
|
|
23766
|
-
this
|
|
23807
|
+
if (this.canvas) {
|
|
23808
|
+
$$1(this.canvas).remove();
|
|
23767
23809
|
}
|
|
23768
23810
|
|
|
23769
|
-
|
|
23770
|
-
|
|
23811
|
+
newCanvas._data = {
|
|
23812
|
+
chr: this.featureCache.chr,
|
|
23813
|
+
bpPerPixel,
|
|
23814
|
+
startBP,
|
|
23815
|
+
endBP,
|
|
23816
|
+
pixelHeight,
|
|
23817
|
+
pixelTop: canvasTop
|
|
23818
|
+
};
|
|
23771
23819
|
this.canvas = newCanvas;
|
|
23772
|
-
this.
|
|
23820
|
+
this.$content.append($$1(newCanvas));
|
|
23773
23821
|
}
|
|
23822
|
+
/**
|
|
23823
|
+
* Draw the associated track.
|
|
23824
|
+
*
|
|
23825
|
+
* @param drawConfiguration
|
|
23826
|
+
* @param features
|
|
23827
|
+
* @param roiFeatures
|
|
23828
|
+
*/
|
|
23829
|
+
|
|
23774
23830
|
|
|
23775
23831
|
draw(drawConfiguration, features, roiFeatures) {
|
|
23776
23832
|
// console.log(`${ Date.now() } viewport draw(). track ${ this.trackView.track.type }. content-css-top ${ this.$content.css('top') }. canvas-top ${ drawConfiguration.pixelTop }.`)
|
|
@@ -23785,51 +23841,6 @@
|
|
|
23785
23841
|
r.track.draw(drawConfiguration);
|
|
23786
23842
|
}
|
|
23787
23843
|
}
|
|
23788
|
-
} // TODO: Nolonger used. Will discard
|
|
23789
|
-
|
|
23790
|
-
|
|
23791
|
-
async toSVG(tile) {
|
|
23792
|
-
// Nothing to do if zoomInNotice is active
|
|
23793
|
-
if (this.$zoomInNotice && this.$zoomInNotice.is(":visible")) {
|
|
23794
|
-
return;
|
|
23795
|
-
}
|
|
23796
|
-
|
|
23797
|
-
const referenceFrame = this.referenceFrame;
|
|
23798
|
-
const bpPerPixel = tile.bpPerPixel;
|
|
23799
|
-
const features = tile.features;
|
|
23800
|
-
const roiFeatures = tile.roiFeatures;
|
|
23801
|
-
const pixelWidth = this.$viewport.width();
|
|
23802
|
-
const pixelHeight = this.$viewport.height();
|
|
23803
|
-
const bpStart = referenceFrame.start;
|
|
23804
|
-
const bpEnd = referenceFrame.start + pixelWidth * referenceFrame.bpPerPixel;
|
|
23805
|
-
const ctx$1 = new ctx({
|
|
23806
|
-
// svg
|
|
23807
|
-
width: pixelWidth,
|
|
23808
|
-
height: pixelHeight,
|
|
23809
|
-
viewbox: {
|
|
23810
|
-
x: 0,
|
|
23811
|
-
y: -this.$content.position().top,
|
|
23812
|
-
width: pixelWidth,
|
|
23813
|
-
height: pixelHeight
|
|
23814
|
-
}
|
|
23815
|
-
});
|
|
23816
|
-
const drawConfiguration = {
|
|
23817
|
-
viewport: this,
|
|
23818
|
-
context: ctx$1,
|
|
23819
|
-
top: -this.$content.position().top,
|
|
23820
|
-
pixelTop: 0,
|
|
23821
|
-
// for compatibility with canvas draw
|
|
23822
|
-
pixelWidth,
|
|
23823
|
-
pixelHeight,
|
|
23824
|
-
bpStart,
|
|
23825
|
-
bpEnd,
|
|
23826
|
-
bpPerPixel,
|
|
23827
|
-
referenceFrame: this.referenceFrame,
|
|
23828
|
-
selection: this.selection,
|
|
23829
|
-
viewportWidth: pixelWidth
|
|
23830
|
-
};
|
|
23831
|
-
this.draw(drawConfiguration, features, roiFeatures);
|
|
23832
|
-
return ctx$1.getSerializedSvg(true);
|
|
23833
23844
|
}
|
|
23834
23845
|
|
|
23835
23846
|
containsPosition(chr, position) {
|
|
@@ -23844,15 +23855,17 @@
|
|
|
23844
23855
|
return this.loading;
|
|
23845
23856
|
}
|
|
23846
23857
|
|
|
23847
|
-
|
|
23848
|
-
if (!this.
|
|
23849
|
-
const
|
|
23858
|
+
savePNG() {
|
|
23859
|
+
if (!this.canvas) return;
|
|
23860
|
+
const canvasMetadata = this.featureCache;
|
|
23861
|
+
const canvasTop = canvasMetadata ? canvasMetadata.canvasTop : 0;
|
|
23850
23862
|
const devicePixelRatio = window.devicePixelRatio;
|
|
23851
23863
|
const w = this.$viewport.width() * devicePixelRatio;
|
|
23852
23864
|
const h = this.$viewport.height() * devicePixelRatio;
|
|
23853
23865
|
const x = -$$1(this.canvas).position().left * devicePixelRatio;
|
|
23854
23866
|
const y = (-this.$content.position().top - canvasTop) * devicePixelRatio;
|
|
23855
|
-
const
|
|
23867
|
+
const ctx = this.canvas.getContext("2d");
|
|
23868
|
+
const imageData = ctx.getImageData(x, y, w, h);
|
|
23856
23869
|
const exportCanvas = document.createElement('canvas');
|
|
23857
23870
|
const exportCtx = exportCanvas.getContext('2d');
|
|
23858
23871
|
exportCanvas.width = imageData.width;
|
|
@@ -23968,29 +23981,44 @@
|
|
|
23968
23981
|
viewportWidth: width,
|
|
23969
23982
|
selection: this.selection
|
|
23970
23983
|
};
|
|
23971
|
-
const features = this.
|
|
23972
|
-
const roiFeatures = this.
|
|
23984
|
+
const features = this.featureCache ? this.featureCache.features : [];
|
|
23985
|
+
const roiFeatures = this.featureCache ? this.featureCache.roiFeatures : undefined;
|
|
23973
23986
|
this.draw(config, features, roiFeatures);
|
|
23974
23987
|
context.restore();
|
|
23975
23988
|
}
|
|
23976
23989
|
|
|
23977
|
-
|
|
23978
|
-
return this.
|
|
23990
|
+
get cachedFeatures() {
|
|
23991
|
+
return this.featureCache ? this.featureCache.features : [];
|
|
23979
23992
|
}
|
|
23980
23993
|
|
|
23981
23994
|
async getFeatures(track, chr, start, end, bpPerPixel) {
|
|
23982
|
-
if (this.
|
|
23983
|
-
return this.
|
|
23995
|
+
if (this.featureCache && this.featureCache.containsRange(chr, start, end, bpPerPixel)) {
|
|
23996
|
+
return this.featureCache.features;
|
|
23984
23997
|
} else if (typeof track.getFeatures === "function") {
|
|
23985
23998
|
const features = await track.getFeatures(chr, start, end, bpPerPixel, this);
|
|
23986
|
-
this.
|
|
23987
|
-
this.checkContentHeight();
|
|
23999
|
+
this.checkContentHeight(features);
|
|
23988
24000
|
return features;
|
|
23989
24001
|
} else {
|
|
23990
24002
|
return undefined;
|
|
23991
24003
|
}
|
|
23992
24004
|
}
|
|
23993
24005
|
|
|
24006
|
+
needsRepaint() {
|
|
24007
|
+
if (!this.canvas) return true;
|
|
24008
|
+
const data = this.canvas._data;
|
|
24009
|
+
return !data || this.referenceFrame.start < data.startBP || this.referenceFrame.end > data.endBP || this.referenceFrame.chr !== data.chr || this.referenceFrame.bpPerPixel != data.bpPerPixel;
|
|
24010
|
+
}
|
|
24011
|
+
|
|
24012
|
+
needsReload() {
|
|
24013
|
+
if (!this.featureCache) return true;
|
|
24014
|
+
const referenceFrame = this.referenceFrame;
|
|
24015
|
+
const chr = this.referenceFrame.chr;
|
|
24016
|
+
const start = referenceFrame.start;
|
|
24017
|
+
const end = start + referenceFrame.toBP($$1(this.contentDiv).width());
|
|
24018
|
+
const bpPerPixel = referenceFrame.bpPerPixel;
|
|
24019
|
+
return !this.featureCache.containsRange(chr, start, end, bpPerPixel);
|
|
24020
|
+
}
|
|
24021
|
+
|
|
23994
24022
|
createZoomInNotice($parent) {
|
|
23995
24023
|
const $container = $$1('<div>', {
|
|
23996
24024
|
class: 'igv-zoom-in-notice-container'
|
|
@@ -24008,36 +24036,42 @@
|
|
|
24008
24036
|
}
|
|
24009
24037
|
|
|
24010
24038
|
addMouseHandlers() {
|
|
24011
|
-
this
|
|
24012
|
-
this.
|
|
24013
|
-
this.addViewportTouchStartHandler(this.$viewport.get(0));
|
|
24014
|
-
this.addViewportMouseUpHandler(this.$viewport.get(0));
|
|
24015
|
-
this.addViewportTouchEndHandler(this.$viewport.get(0));
|
|
24016
|
-
this.addViewportClickHandler(this.$viewport.get(0));
|
|
24039
|
+
const viewport = this.$viewport.get(0);
|
|
24040
|
+
this.addViewportContextMenuHandler(viewport);
|
|
24017
24041
|
|
|
24018
|
-
|
|
24019
|
-
this.
|
|
24020
|
-
|
|
24021
|
-
|
|
24042
|
+
const md = event => {
|
|
24043
|
+
this.enableClick = true;
|
|
24044
|
+
this.browser.mouseDownOnViewport(event, this);
|
|
24045
|
+
pageCoordinates$1(event);
|
|
24046
|
+
};
|
|
24022
24047
|
|
|
24023
|
-
|
|
24024
|
-
|
|
24025
|
-
|
|
24026
|
-
|
|
24027
|
-
|
|
24028
|
-
|
|
24029
|
-
|
|
24048
|
+
viewport.addEventListener('mousedown', md);
|
|
24049
|
+
viewport.addEventListener('touchstart', md);
|
|
24050
|
+
|
|
24051
|
+
const mu = event => {
|
|
24052
|
+
// Any mouse up cancels drag and scrolling
|
|
24053
|
+
if (this.browser.dragObject || this.browser.isScrolling) {
|
|
24054
|
+
this.browser.cancelTrackPan(); // event.preventDefault();
|
|
24055
|
+
// event.stopPropagation();
|
|
24056
|
+
|
|
24057
|
+
this.enableClick = false; // Until next mouse down
|
|
24058
|
+
} else {
|
|
24059
|
+
this.browser.cancelTrackPan();
|
|
24060
|
+
this.browser.endTrackDrag();
|
|
24061
|
+
}
|
|
24062
|
+
};
|
|
24063
|
+
|
|
24064
|
+
viewport.addEventListener('mouseup', mu);
|
|
24065
|
+
viewport.addEventListener('touchend', mu);
|
|
24066
|
+
this.addViewportClickHandler(this.$viewport.get(0));
|
|
24030
24067
|
|
|
24031
24068
|
if (this.trackView.track.name && "sequence" !== this.trackView.track.config.type) {
|
|
24032
|
-
this.
|
|
24069
|
+
this.addTrackLabelClickHandler(this.$trackLabel.get(0));
|
|
24033
24070
|
}
|
|
24034
24071
|
}
|
|
24035
24072
|
|
|
24036
24073
|
addViewportContextMenuHandler(viewport) {
|
|
24037
|
-
|
|
24038
|
-
viewport.addEventListener('contextmenu', this.boundContextMenuHandler);
|
|
24039
|
-
|
|
24040
|
-
function contextMenuHandler(event) {
|
|
24074
|
+
viewport.addEventListener('contextmenu', event => {
|
|
24041
24075
|
// Ignore if we are doing a drag. This can happen with touch events.
|
|
24042
24076
|
if (this.browser.dragObject) {
|
|
24043
24077
|
return false;
|
|
@@ -24077,54 +24111,11 @@
|
|
|
24077
24111
|
click: () => this.saveSVG()
|
|
24078
24112
|
});
|
|
24079
24113
|
this.browser.menuPopup.presentTrackContextMenu(event, menuItems);
|
|
24080
|
-
}
|
|
24081
|
-
}
|
|
24082
|
-
|
|
24083
|
-
removeViewportContextMenuHandler(viewport) {
|
|
24084
|
-
viewport.removeEventListener('contextmenu', this.boundContextMenuHandler);
|
|
24085
|
-
}
|
|
24086
|
-
|
|
24087
|
-
addViewportMouseDownHandler(viewport) {
|
|
24088
|
-
this.boundMouseDownHandler = mouseDownHandler.bind(this);
|
|
24089
|
-
viewport.addEventListener('mousedown', this.boundMouseDownHandler);
|
|
24090
|
-
}
|
|
24091
|
-
|
|
24092
|
-
removeViewportMouseDownHandler(viewport) {
|
|
24093
|
-
viewport.removeEventListener('mousedown', this.boundMouseDownHandler);
|
|
24094
|
-
}
|
|
24095
|
-
|
|
24096
|
-
addViewportTouchStartHandler(viewport) {
|
|
24097
|
-
this.boundTouchStartHandler = mouseDownHandler.bind(this);
|
|
24098
|
-
viewport.addEventListener('touchstart', this.boundTouchStartHandler);
|
|
24099
|
-
}
|
|
24100
|
-
|
|
24101
|
-
removeViewportTouchStartHandler(viewport) {
|
|
24102
|
-
viewport.removeEventListener('touchstart', this.boundTouchStartHandler);
|
|
24103
|
-
}
|
|
24104
|
-
|
|
24105
|
-
addViewportMouseUpHandler(viewport) {
|
|
24106
|
-
this.boundMouseUpHandler = mouseUpHandler.bind(this);
|
|
24107
|
-
viewport.addEventListener('mouseup', this.boundMouseUpHandler);
|
|
24108
|
-
}
|
|
24109
|
-
|
|
24110
|
-
removeViewportMouseUpHandler(viewport) {
|
|
24111
|
-
viewport.removeEventListener('mouseup', this.boundMouseUpHandler);
|
|
24112
|
-
}
|
|
24113
|
-
|
|
24114
|
-
addViewportTouchEndHandler(viewport) {
|
|
24115
|
-
this.boundTouchEndHandler = mouseUpHandler.bind(this);
|
|
24116
|
-
viewport.addEventListener('touchend', this.boundTouchEndHandler);
|
|
24117
|
-
}
|
|
24118
|
-
|
|
24119
|
-
removeViewportTouchEndHandler(viewport) {
|
|
24120
|
-
viewport.removeEventListener('touchend', this.boundTouchEndHandler);
|
|
24114
|
+
});
|
|
24121
24115
|
}
|
|
24122
24116
|
|
|
24123
24117
|
addViewportClickHandler(viewport) {
|
|
24124
|
-
|
|
24125
|
-
viewport.addEventListener('click', this.boundClickHandler);
|
|
24126
|
-
|
|
24127
|
-
function clickHandler(event) {
|
|
24118
|
+
viewport.addEventListener('click', event => {
|
|
24128
24119
|
if (this.enableClick) {
|
|
24129
24120
|
if (3 === event.which || event.ctrlKey) {
|
|
24130
24121
|
return;
|
|
@@ -24199,18 +24190,11 @@
|
|
|
24199
24190
|
|
|
24200
24191
|
lastClickTime = time;
|
|
24201
24192
|
}
|
|
24202
|
-
}
|
|
24203
|
-
}
|
|
24204
|
-
|
|
24205
|
-
removeViewportClickHandler(viewport) {
|
|
24206
|
-
viewport.removeEventListener('click', this.boundClickHandler);
|
|
24193
|
+
});
|
|
24207
24194
|
}
|
|
24208
24195
|
|
|
24209
24196
|
addTrackLabelClickHandler(trackLabel) {
|
|
24210
|
-
|
|
24211
|
-
trackLabel.addEventListener('click', this.boundTrackLabelClickHandler);
|
|
24212
|
-
|
|
24213
|
-
function clickHandler(event) {
|
|
24197
|
+
trackLabel.addEventListener('click', event => {
|
|
24214
24198
|
event.stopPropagation();
|
|
24215
24199
|
const {
|
|
24216
24200
|
track
|
|
@@ -24231,44 +24215,16 @@
|
|
|
24231
24215
|
this.popover = new Popover(this.browser.columnContainer, track.name || '');
|
|
24232
24216
|
this.popover.presentContentWithEvent(event, str);
|
|
24233
24217
|
}
|
|
24234
|
-
}
|
|
24235
|
-
}
|
|
24236
|
-
|
|
24237
|
-
removeTrackLabelClickHandler(trackLabel) {
|
|
24238
|
-
trackLabel.removeEventListener('click', this.boundTrackLabelClickHandler);
|
|
24218
|
+
});
|
|
24239
24219
|
}
|
|
24240
24220
|
|
|
24241
24221
|
}
|
|
24242
24222
|
|
|
24243
|
-
function mouseDownHandler(event) {
|
|
24244
|
-
this.enableClick = true;
|
|
24245
|
-
this.browser.mouseDownOnViewport(event, this);
|
|
24246
|
-
pageCoordinates$1(event);
|
|
24247
|
-
}
|
|
24248
|
-
|
|
24249
|
-
function mouseUpHandler(event) {
|
|
24250
|
-
// Any mouse up cancels drag and scrolling
|
|
24251
|
-
if (this.browser.dragObject || this.browser.isScrolling) {
|
|
24252
|
-
this.browser.cancelTrackPan(); // event.preventDefault();
|
|
24253
|
-
// event.stopPropagation();
|
|
24254
|
-
|
|
24255
|
-
this.enableClick = false; // Until next mouse down
|
|
24256
|
-
} else {
|
|
24257
|
-
this.browser.cancelTrackPan();
|
|
24258
|
-
this.browser.endTrackDrag();
|
|
24259
|
-
}
|
|
24260
|
-
}
|
|
24261
|
-
|
|
24262
24223
|
function createClickState(event, viewport) {
|
|
24263
24224
|
const referenceFrame = viewport.referenceFrame;
|
|
24264
24225
|
const viewportCoords = translateMouseCoordinates$1(event, viewport.contentDiv);
|
|
24265
24226
|
const canvasCoords = translateMouseCoordinates$1(event, viewport.canvas);
|
|
24266
24227
|
const genomicLocation = referenceFrame.start + referenceFrame.toBP(viewportCoords.x);
|
|
24267
|
-
|
|
24268
|
-
if (undefined === genomicLocation || null === viewport.tile) {
|
|
24269
|
-
return undefined;
|
|
24270
|
-
}
|
|
24271
|
-
|
|
24272
24228
|
return {
|
|
24273
24229
|
event,
|
|
24274
24230
|
viewport,
|
|
@@ -24322,22 +24278,28 @@
|
|
|
24322
24278
|
return rows.join('');
|
|
24323
24279
|
}
|
|
24324
24280
|
|
|
24325
|
-
|
|
24326
|
-
|
|
24327
|
-
|
|
24328
|
-
|
|
24329
|
-
|
|
24330
|
-
|
|
24331
|
-
|
|
24332
|
-
|
|
24281
|
+
class FeatureCache {
|
|
24282
|
+
constructor(chr, tileStart, tileEnd, bpPerPixel, features, roiFeatures, multiresolution) {
|
|
24283
|
+
this.chr = chr;
|
|
24284
|
+
this.startBP = tileStart;
|
|
24285
|
+
this.endBP = tileEnd;
|
|
24286
|
+
this.bpPerPixel = bpPerPixel;
|
|
24287
|
+
this.features = features;
|
|
24288
|
+
this.roiFeatures = roiFeatures;
|
|
24289
|
+
this.multiresolution = multiresolution;
|
|
24290
|
+
}
|
|
24333
24291
|
|
|
24334
|
-
|
|
24335
|
-
|
|
24336
|
-
|
|
24292
|
+
containsRange(chr, start, end, bpPerPixel) {
|
|
24293
|
+
// For multi-resolution tracks allow for a 2X change in bpPerPixel
|
|
24294
|
+
const r = this.multiresolution ? this.bpPerPixel / bpPerPixel : 1;
|
|
24295
|
+
return start >= this.startBP && end <= this.endBP && chr === this.chr && r > 0.5 && r < 2;
|
|
24296
|
+
}
|
|
24337
24297
|
|
|
24338
|
-
|
|
24339
|
-
|
|
24340
|
-
|
|
24298
|
+
overlapsRange(chr, start, end) {
|
|
24299
|
+
return this.chr === chr && end >= this.startBP && start <= this.endBP;
|
|
24300
|
+
}
|
|
24301
|
+
|
|
24302
|
+
}
|
|
24341
24303
|
/**
|
|
24342
24304
|
* Merge 2 arrays. a and/or b can be undefined. If both are undefined, return undefined
|
|
24343
24305
|
* @param a An array or undefined
|
|
@@ -24476,10 +24438,12 @@
|
|
|
24476
24438
|
end: bp(this.rulerViewport.referenceFrame, left + width)
|
|
24477
24439
|
};
|
|
24478
24440
|
validateLocusExtent(this.rulerViewport.browser.genome.getChromosome(this.rulerViewport.referenceFrame.chr).bpLength, extent, this.rulerViewport.browser.minimumBases());
|
|
24479
|
-
|
|
24480
|
-
|
|
24481
|
-
this.rulerViewport.referenceFrame.
|
|
24482
|
-
this.rulerViewport.
|
|
24441
|
+
const newStart = Math.round(extent.start);
|
|
24442
|
+
const newEnd = Math.round(extent.end);
|
|
24443
|
+
this.rulerViewport.referenceFrame.bpPerPixel = (newEnd - newStart) / this.rulerViewport.contentDiv.clientWidth;
|
|
24444
|
+
this.rulerViewport.referenceFrame.start = newStart;
|
|
24445
|
+
this.rulerViewport.referenceFrame.end = newEnd;
|
|
24446
|
+
this.rulerViewport.browser.updateViews();
|
|
24483
24447
|
}
|
|
24484
24448
|
}
|
|
24485
24449
|
}
|
|
@@ -25481,6 +25445,18 @@
|
|
|
25481
25445
|
return true; // By definition
|
|
25482
25446
|
}
|
|
25483
25447
|
|
|
25448
|
+
isMateMapped() {
|
|
25449
|
+
return true; // By definition
|
|
25450
|
+
}
|
|
25451
|
+
|
|
25452
|
+
isProperPair() {
|
|
25453
|
+
return this.firstAlignment.isProperPair();
|
|
25454
|
+
}
|
|
25455
|
+
|
|
25456
|
+
get fragmentLength() {
|
|
25457
|
+
return Math.abs(this.firstAlignment.fragmentLength);
|
|
25458
|
+
}
|
|
25459
|
+
|
|
25484
25460
|
firstOfPairStrand() {
|
|
25485
25461
|
if (this.firstAlignment.isFirstOfPair()) {
|
|
25486
25462
|
return this.firstAlignment.strand;
|
|
@@ -25847,7 +25823,15 @@
|
|
|
25847
25823
|
*/
|
|
25848
25824
|
|
|
25849
25825
|
class AlignmentContainer {
|
|
25850
|
-
|
|
25826
|
+
// this.config.samplingWindowSize, this.config.samplingDepth,
|
|
25827
|
+
// this.config.pairsSupported, this.config.alleleFreqThreshold)
|
|
25828
|
+
constructor(chr, start, end, _ref) {
|
|
25829
|
+
let {
|
|
25830
|
+
samplingWindowSize,
|
|
25831
|
+
samplingDepth,
|
|
25832
|
+
pairsSupported,
|
|
25833
|
+
alleleFreqThreshold
|
|
25834
|
+
} = _ref;
|
|
25851
25835
|
this.chr = chr;
|
|
25852
25836
|
this.start = Math.floor(start);
|
|
25853
25837
|
this.end = Math.ceil(end);
|
|
@@ -25870,17 +25854,10 @@
|
|
|
25870
25854
|
// TODO -- pass this in
|
|
25871
25855
|
return alignment.isMapped() && !alignment.isFailsVendorQualityCheck();
|
|
25872
25856
|
};
|
|
25873
|
-
|
|
25874
|
-
this.pairedEndStats = new PairedEndStats();
|
|
25875
25857
|
}
|
|
25876
25858
|
|
|
25877
25859
|
push(alignment) {
|
|
25878
25860
|
if (this.filter(alignment) === false) return;
|
|
25879
|
-
|
|
25880
|
-
if (alignment.isPaired()) {
|
|
25881
|
-
this.pairedEndStats.push(alignment);
|
|
25882
|
-
}
|
|
25883
|
-
|
|
25884
25861
|
this.coverageMap.incCounts(alignment); // Count coverage before any downsampling
|
|
25885
25862
|
|
|
25886
25863
|
if (this.pairsSupported && this.downsampledReads.has(alignment.readName)) {
|
|
@@ -25909,7 +25886,6 @@
|
|
|
25909
25886
|
});
|
|
25910
25887
|
this.pairsCache = undefined;
|
|
25911
25888
|
this.downsampledReads = undefined;
|
|
25912
|
-
this.pairedEndStats.compute();
|
|
25913
25889
|
}
|
|
25914
25890
|
|
|
25915
25891
|
contains(chr, start, end) {
|
|
@@ -26204,62 +26180,6 @@
|
|
|
26204
26180
|
|
|
26205
26181
|
}
|
|
26206
26182
|
|
|
26207
|
-
class PairedEndStats {
|
|
26208
|
-
constructor(lowerPercentile, upperPercentile) {
|
|
26209
|
-
this.totalCount = 0;
|
|
26210
|
-
this.frCount = 0;
|
|
26211
|
-
this.rfCount = 0;
|
|
26212
|
-
this.ffCount = 0;
|
|
26213
|
-
this.sumF = 0;
|
|
26214
|
-
this.sumF2 = 0; //this.lp = lowerPercentile === undefined ? 0.005 : lowerPercentile;
|
|
26215
|
-
//this.up = upperPercentile === undefined ? 0.995 : upperPercentile;
|
|
26216
|
-
//this.digest = new Digest();
|
|
26217
|
-
}
|
|
26218
|
-
|
|
26219
|
-
push(alignment) {
|
|
26220
|
-
if (alignment.isProperPair()) {
|
|
26221
|
-
var fragmentLength = Math.abs(alignment.fragmentLength); //this.digest.push(fragmentLength);
|
|
26222
|
-
|
|
26223
|
-
this.sumF += fragmentLength;
|
|
26224
|
-
this.sumF2 += fragmentLength * fragmentLength;
|
|
26225
|
-
var po = alignment.pairOrientation;
|
|
26226
|
-
|
|
26227
|
-
if (typeof po === "string" && po.length === 4) {
|
|
26228
|
-
var tmp = '' + po.charAt(0) + po.charAt(2);
|
|
26229
|
-
|
|
26230
|
-
switch (tmp) {
|
|
26231
|
-
case 'FF':
|
|
26232
|
-
case 'RR':
|
|
26233
|
-
this.ffCount++;
|
|
26234
|
-
break;
|
|
26235
|
-
|
|
26236
|
-
case "FR":
|
|
26237
|
-
this.frCount++;
|
|
26238
|
-
break;
|
|
26239
|
-
|
|
26240
|
-
case "RF":
|
|
26241
|
-
this.rfCount++;
|
|
26242
|
-
}
|
|
26243
|
-
}
|
|
26244
|
-
|
|
26245
|
-
this.totalCount++;
|
|
26246
|
-
}
|
|
26247
|
-
}
|
|
26248
|
-
|
|
26249
|
-
compute() {
|
|
26250
|
-
if (this.totalCount > 100) {
|
|
26251
|
-
if (this.ffCount / this.totalCount > 0.9) this.orienation = "ff";else if (this.frCount / this.totalCount > 0.9) this.orienation = "fr";else if (this.rfCount / this.totalCount > 0.9) this.orienation = "rf";
|
|
26252
|
-
var fMean = this.sumF / this.totalCount;
|
|
26253
|
-
var stdDev = Math.sqrt((this.totalCount * this.sumF2 - this.sumF * this.sumF) / (this.totalCount * this.totalCount));
|
|
26254
|
-
this.lowerFragmentLength = fMean - 3 * stdDev;
|
|
26255
|
-
this.upperFragmentLength = fMean + 3 * stdDev; //this.lowerFragmentLength = this.digest.percentile(this.lp);
|
|
26256
|
-
//this.upperFragmentLength = this.digest.percentile(this.up);
|
|
26257
|
-
//this.digest = undefined;
|
|
26258
|
-
}
|
|
26259
|
-
}
|
|
26260
|
-
|
|
26261
|
-
}
|
|
26262
|
-
|
|
26263
26183
|
class SupplementaryAlignment {
|
|
26264
26184
|
constructor(rec) {
|
|
26265
26185
|
const tokens = rec.split(',');
|
|
@@ -27022,7 +26942,7 @@
|
|
|
27022
26942
|
const lseq = readInt(ba, offset + 20);
|
|
27023
26943
|
const mateChrIdx = readInt(ba, offset + 24);
|
|
27024
26944
|
const matePos = readInt(ba, offset + 28);
|
|
27025
|
-
const
|
|
26945
|
+
const fragmentLength = readInt(ba, offset + 32);
|
|
27026
26946
|
let readName = [];
|
|
27027
26947
|
|
|
27028
26948
|
for (let j = 0; j < nl - 1; ++j) {
|
|
@@ -27063,7 +26983,7 @@
|
|
|
27063
26983
|
alignment.readName = readName;
|
|
27064
26984
|
alignment.cigar = cigar;
|
|
27065
26985
|
alignment.lengthOnRef = lengthOnRef;
|
|
27066
|
-
alignment.fragmentLength =
|
|
26986
|
+
alignment.fragmentLength = fragmentLength;
|
|
27067
26987
|
alignment.mq = mq;
|
|
27068
26988
|
BamUtils.bam_tag2cigar(ba, blockEnd, p, lseq, alignment, cigarArray);
|
|
27069
26989
|
alignment.end = alignment.start + alignment.lengthOnRef;
|
|
@@ -27463,7 +27383,7 @@
|
|
|
27463
27383
|
const header = this.header;
|
|
27464
27384
|
const queryChr = header.chrAliasTable.hasOwnProperty(chr) ? header.chrAliasTable[chr] : chr;
|
|
27465
27385
|
const qAlignments = this.alignmentCache.queryFeatures(queryChr, bpStart, bpEnd);
|
|
27466
|
-
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.
|
|
27386
|
+
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config);
|
|
27467
27387
|
|
|
27468
27388
|
for (let a of qAlignments) {
|
|
27469
27389
|
alignmentContainer.push(a);
|
|
@@ -27490,13 +27410,13 @@
|
|
|
27490
27410
|
const alignments = [];
|
|
27491
27411
|
this.header = BamUtils.decodeBamHeader(data);
|
|
27492
27412
|
BamUtils.decodeBamRecords(data, this.header.size, alignments, this.header.chrNames);
|
|
27493
|
-
this.alignmentCache = new FeatureCache(alignments, this.genome);
|
|
27413
|
+
this.alignmentCache = new FeatureCache$1(alignments, this.genome);
|
|
27494
27414
|
}
|
|
27495
27415
|
|
|
27496
27416
|
fetchAlignments(chr, bpStart, bpEnd) {
|
|
27497
27417
|
const queryChr = this.header.chrAliasTable.hasOwnProperty(chr) ? this.header.chrAliasTable[chr] : chr;
|
|
27498
27418
|
const features = this.alignmentCache.queryFeatures(queryChr, bpStart, bpEnd);
|
|
27499
|
-
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.
|
|
27419
|
+
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config);
|
|
27500
27420
|
|
|
27501
27421
|
for (let feature of features) {
|
|
27502
27422
|
alignmentContainer.push(feature);
|
|
@@ -28496,7 +28416,7 @@
|
|
|
28496
28416
|
const chrToIndex = await this.getChrIndex();
|
|
28497
28417
|
const queryChr = this.chrAliasTable.hasOwnProperty(chr) ? this.chrAliasTable[chr] : chr;
|
|
28498
28418
|
const chrId = chrToIndex[queryChr];
|
|
28499
|
-
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config
|
|
28419
|
+
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config);
|
|
28500
28420
|
|
|
28501
28421
|
if (chrId === undefined) {
|
|
28502
28422
|
return alignmentContainer;
|
|
@@ -28645,7 +28565,7 @@
|
|
|
28645
28565
|
|
|
28646
28566
|
async readAlignments(chr, start, end) {
|
|
28647
28567
|
if (!this.bamReaders.hasOwnProperty(chr)) {
|
|
28648
|
-
return new AlignmentContainer(chr, start, end);
|
|
28568
|
+
return new AlignmentContainer(chr, start, end, this.config);
|
|
28649
28569
|
} else {
|
|
28650
28570
|
let reader = this.bamReaders[chr];
|
|
28651
28571
|
const a = await reader.readAlignments(chr, start, end);
|
|
@@ -28717,7 +28637,7 @@
|
|
|
28717
28637
|
return igvxhr.loadString(url, buildOptions(self.config)).then(function (sam) {
|
|
28718
28638
|
var alignmentContainer;
|
|
28719
28639
|
header.chrToIndex[queryChr];
|
|
28720
|
-
alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, self.
|
|
28640
|
+
alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, self.config);
|
|
28721
28641
|
BamUtils.decodeSamRecords(sam, alignmentContainer, queryChr, bpStart, bpEnd, self.filter);
|
|
28722
28642
|
return alignmentContainer;
|
|
28723
28643
|
});
|
|
@@ -28927,7 +28847,7 @@
|
|
|
28927
28847
|
|
|
28928
28848
|
const ba = unbgzf(compressedData.buffer);
|
|
28929
28849
|
const chrIdx = this.header.chrToIndex[chr];
|
|
28930
|
-
const alignmentContainer = new AlignmentContainer(chr, start, end, this.
|
|
28850
|
+
const alignmentContainer = new AlignmentContainer(chr, start, end, this.config);
|
|
28931
28851
|
BamUtils.decodeBamRecords(ba, this.header.size, alignmentContainer, this.header.chrNames, chrIdx, start, end);
|
|
28932
28852
|
alignmentContainer.finish();
|
|
28933
28853
|
return alignmentContainer;
|
|
@@ -44465,7 +44385,7 @@
|
|
|
44465
44385
|
const header = await this.getHeader();
|
|
44466
44386
|
const queryChr = header.chrAliasTable.hasOwnProperty(chr) ? header.chrAliasTable[chr] : chr;
|
|
44467
44387
|
const chrIdx = header.chrToIndex[queryChr];
|
|
44468
|
-
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.
|
|
44388
|
+
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config);
|
|
44469
44389
|
|
|
44470
44390
|
if (chrIdx === undefined) {
|
|
44471
44391
|
return alignmentContainer;
|
|
@@ -45074,7 +44994,7 @@
|
|
|
45074
44994
|
"pageSize": "10000"
|
|
45075
44995
|
},
|
|
45076
44996
|
decode: decodeGa4ghReads,
|
|
45077
|
-
results: new AlignmentContainer(chr, bpStart, bpEnd, self.
|
|
44997
|
+
results: new AlignmentContainer(chr, bpStart, bpEnd, self.config)
|
|
45078
44998
|
});
|
|
45079
44999
|
});
|
|
45080
45000
|
|
|
@@ -45381,7 +45301,6 @@
|
|
|
45381
45301
|
const genome = browser.genome;
|
|
45382
45302
|
this.config = config;
|
|
45383
45303
|
this.genome = genome;
|
|
45384
|
-
this.alignmentContainer = undefined;
|
|
45385
45304
|
|
|
45386
45305
|
if (isDataURL(config.url)) {
|
|
45387
45306
|
if ("cram" === config.format) {
|
|
@@ -45432,58 +45351,44 @@
|
|
|
45432
45351
|
}
|
|
45433
45352
|
|
|
45434
45353
|
setViewAsPairs(bool) {
|
|
45435
|
-
|
|
45436
|
-
if (this.viewAsPairs !== bool) {
|
|
45437
|
-
this.viewAsPairs = bool; // if (this.alignmentContainer) {
|
|
45438
|
-
// this.alignmentContainer.setViewAsPairs(bool);
|
|
45439
|
-
// }
|
|
45440
|
-
}
|
|
45354
|
+
this.viewAsPairs = bool;
|
|
45441
45355
|
}
|
|
45442
45356
|
|
|
45443
45357
|
setShowSoftClips(bool) {
|
|
45444
|
-
|
|
45445
|
-
this.showSoftClips = bool;
|
|
45446
|
-
}
|
|
45358
|
+
this.showSoftClips = bool;
|
|
45447
45359
|
}
|
|
45448
45360
|
|
|
45449
45361
|
async getAlignments(chr, bpStart, bpEnd) {
|
|
45450
45362
|
const genome = this.genome;
|
|
45451
45363
|
const showSoftClips = this.showSoftClips;
|
|
45364
|
+
const alignmentContainer = await this.bamReader.readAlignments(chr, bpStart, bpEnd);
|
|
45365
|
+
let alignments = alignmentContainer.alignments;
|
|
45452
45366
|
|
|
45453
|
-
if (this.
|
|
45454
|
-
|
|
45455
|
-
|
|
45456
|
-
|
|
45457
|
-
|
|
45458
|
-
|
|
45459
|
-
if (!this.viewAsPairs) {
|
|
45460
|
-
alignments = unpairAlignments([{
|
|
45461
|
-
alignments: alignments
|
|
45462
|
-
}]);
|
|
45463
|
-
}
|
|
45464
|
-
|
|
45465
|
-
const hasAlignments = alignments.length > 0;
|
|
45466
|
-
alignmentContainer.packedAlignmentRows = packAlignmentRows(alignments, alignmentContainer.start, alignmentContainer.end, showSoftClips);
|
|
45467
|
-
alignmentContainer.alignments = undefined; // Don't need to hold onto these anymore
|
|
45367
|
+
if (!this.viewAsPairs) {
|
|
45368
|
+
alignments = unpairAlignments([{
|
|
45369
|
+
alignments: alignments
|
|
45370
|
+
}]);
|
|
45371
|
+
}
|
|
45468
45372
|
|
|
45469
|
-
|
|
45373
|
+
const hasAlignments = alignments.length > 0;
|
|
45374
|
+
alignmentContainer.packedAlignmentRows = packAlignmentRows(alignments, alignmentContainer.start, alignmentContainer.end, showSoftClips);
|
|
45375
|
+
this.alignmentContainer = alignmentContainer;
|
|
45470
45376
|
|
|
45471
|
-
|
|
45472
|
-
|
|
45377
|
+
if (hasAlignments) {
|
|
45378
|
+
const sequence = await genome.sequence.getSequence(chr, alignmentContainer.start, alignmentContainer.end);
|
|
45473
45379
|
|
|
45474
|
-
|
|
45475
|
-
|
|
45380
|
+
if (sequence) {
|
|
45381
|
+
alignmentContainer.coverageMap.refSeq = sequence; // TODO -- fix this
|
|
45476
45382
|
|
|
45477
|
-
|
|
45383
|
+
alignmentContainer.sequence = sequence; // TODO -- fix this
|
|
45478
45384
|
|
|
45479
|
-
|
|
45480
|
-
|
|
45481
|
-
|
|
45482
|
-
}
|
|
45385
|
+
return alignmentContainer;
|
|
45386
|
+
} else {
|
|
45387
|
+
console.error("No sequence for: " + chr + ":" + alignmentContainer.start + "-" + alignmentContainer.end);
|
|
45483
45388
|
}
|
|
45484
|
-
|
|
45485
|
-
return alignmentContainer;
|
|
45486
45389
|
}
|
|
45390
|
+
|
|
45391
|
+
return alignmentContainer;
|
|
45487
45392
|
}
|
|
45488
45393
|
|
|
45489
45394
|
}
|
|
@@ -45667,7 +45572,7 @@
|
|
|
45667
45572
|
return state;
|
|
45668
45573
|
}
|
|
45669
45574
|
|
|
45670
|
-
supportsWholeGenome() {
|
|
45575
|
+
get supportsWholeGenome() {
|
|
45671
45576
|
return false;
|
|
45672
45577
|
}
|
|
45673
45578
|
/**
|
|
@@ -45828,7 +45733,7 @@
|
|
|
45828
45733
|
clickedFeatures(clickState, features) {
|
|
45829
45734
|
// We use the cached features rather than method to avoid async load. If the
|
|
45830
45735
|
// feature is not already loaded this won't work, but the user wouldn't be mousing over it either.
|
|
45831
|
-
if (!features) features = clickState.viewport.
|
|
45736
|
+
if (!features) features = clickState.viewport.cachedFeatures;
|
|
45832
45737
|
|
|
45833
45738
|
if (!features || features.length === 0) {
|
|
45834
45739
|
return [];
|
|
@@ -48467,52 +48372,125 @@
|
|
|
48467
48372
|
return regions;
|
|
48468
48373
|
}
|
|
48469
48374
|
|
|
48375
|
+
function sendChords(chords, track, refFrame, alpha) {
|
|
48376
|
+
const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? track.color : getChrColor(refFrame.chr), alpha);
|
|
48377
|
+
const trackColor = IGVColor.addAlpha(track.color || 'rgb(0,0,255)', alpha); // name the chord set to include locus and filtering information
|
|
48378
|
+
|
|
48379
|
+
const encodedName = track.name.replaceAll(' ', '%20');
|
|
48380
|
+
const chordSetName = "all" === refFrame.chr ? encodedName : `${encodedName} ${refFrame.chr}:${refFrame.start}-${refFrame.end}`;
|
|
48381
|
+
track.browser.circularView.addChords(chords, {
|
|
48382
|
+
track: chordSetName,
|
|
48383
|
+
color: chordSetColor,
|
|
48384
|
+
trackColor: trackColor
|
|
48385
|
+
}); // show circular view if hidden
|
|
48386
|
+
|
|
48387
|
+
if (!track.browser.circularViewVisible) track.browser.circularViewVisible = true;
|
|
48388
|
+
}
|
|
48389
|
+
|
|
48470
48390
|
function createCircularView(el, browser) {
|
|
48471
48391
|
const circularView = new CircularView(el, {
|
|
48472
48392
|
onChordClick: (feature, chordTrack, pluginManager) => {
|
|
48473
48393
|
const f1 = feature.data;
|
|
48474
48394
|
const f2 = f1.mate;
|
|
48475
|
-
|
|
48476
|
-
|
|
48477
|
-
|
|
48478
|
-
|
|
48479
|
-
|
|
48480
|
-
|
|
48481
|
-
|
|
48482
|
-
|
|
48483
|
-
|
|
48484
|
-
|
|
48485
|
-
|
|
48486
|
-
|
|
48487
|
-
|
|
48488
|
-
|
|
48489
|
-
|
|
48490
|
-
|
|
48491
|
-
|
|
48492
|
-
if (!loci.some(locus => {
|
|
48493
|
-
return locus.contains(l);
|
|
48494
|
-
})) {
|
|
48495
|
-
// add flanking
|
|
48496
|
-
l.start = Math.max(0, l.start - flanking);
|
|
48497
|
-
l.end += flanking;
|
|
48498
|
-
loci.push(l);
|
|
48395
|
+
addFrameForFeature(f1);
|
|
48396
|
+
addFrameForFeature(f2);
|
|
48397
|
+
|
|
48398
|
+
function addFrameForFeature(feature) {
|
|
48399
|
+
feature.chr = browser.genome.getChromosomeName(feature.refName);
|
|
48400
|
+
let frameFound = false;
|
|
48401
|
+
|
|
48402
|
+
for (let referenceFrame of browser.referenceFrameList) {
|
|
48403
|
+
const l = Locus.fromLocusString(referenceFrame.getLocusString());
|
|
48404
|
+
|
|
48405
|
+
if (l.contains(feature)) {
|
|
48406
|
+
frameFound = true;
|
|
48407
|
+
break;
|
|
48408
|
+
} else if (l.overlaps(feature)) {
|
|
48409
|
+
referenceFrame.extend(feature);
|
|
48410
|
+
frameFound = true;
|
|
48411
|
+
break;
|
|
48499
48412
|
}
|
|
48500
48413
|
}
|
|
48501
|
-
} else {
|
|
48502
|
-
l1.start = Math.max(0, l1.start - flanking);
|
|
48503
|
-
l1.end += flanking;
|
|
48504
|
-
l2.start = Math.max(0, l2.start - flanking);
|
|
48505
|
-
l2.end += flanking;
|
|
48506
|
-
loci = [l1, l2];
|
|
48507
|
-
}
|
|
48508
48414
|
|
|
48509
|
-
|
|
48510
|
-
|
|
48415
|
+
if (!frameFound) {
|
|
48416
|
+
const flanking = 2000;
|
|
48417
|
+
const center = (feature.start + feature.end) / 2;
|
|
48418
|
+
browser.addMultiLocusPanel(feature.chr, center - flanking, center + flanking);
|
|
48419
|
+
}
|
|
48420
|
+
}
|
|
48511
48421
|
}
|
|
48512
48422
|
});
|
|
48513
48423
|
return circularView;
|
|
48514
48424
|
}
|
|
48515
48425
|
|
|
48426
|
+
class PairedEndStats {
|
|
48427
|
+
constructor(alignments, _ref) {
|
|
48428
|
+
let {
|
|
48429
|
+
minTLENPercentile,
|
|
48430
|
+
maxTLENPercentile
|
|
48431
|
+
} = _ref;
|
|
48432
|
+
this.totalCount = 0;
|
|
48433
|
+
this.frCount = 0;
|
|
48434
|
+
this.rfCount = 0;
|
|
48435
|
+
this.ffCount = 0;
|
|
48436
|
+
this.sumF = 0;
|
|
48437
|
+
this.sumF2 = 0;
|
|
48438
|
+
this.lp = minTLENPercentile === undefined ? 0.1 : minTLENPercentile;
|
|
48439
|
+
this.up = maxTLENPercentile === undefined ? 99.5 : maxTLENPercentile;
|
|
48440
|
+
this.isizes = [];
|
|
48441
|
+
this.compute(alignments);
|
|
48442
|
+
}
|
|
48443
|
+
|
|
48444
|
+
compute(alignments) {
|
|
48445
|
+
for (let alignment of alignments) {
|
|
48446
|
+
if (alignment.isProperPair()) {
|
|
48447
|
+
var tlen = Math.abs(alignment.fragmentLength);
|
|
48448
|
+
this.sumF += tlen;
|
|
48449
|
+
this.sumF2 += tlen * tlen;
|
|
48450
|
+
this.isizes.push(tlen);
|
|
48451
|
+
var po = alignment.pairOrientation;
|
|
48452
|
+
|
|
48453
|
+
if (typeof po === "string" && po.length === 4) {
|
|
48454
|
+
var tmp = '' + po.charAt(0) + po.charAt(2);
|
|
48455
|
+
|
|
48456
|
+
switch (tmp) {
|
|
48457
|
+
case 'FF':
|
|
48458
|
+
case 'RR':
|
|
48459
|
+
this.ffCount++;
|
|
48460
|
+
break;
|
|
48461
|
+
|
|
48462
|
+
case "FR":
|
|
48463
|
+
this.frCount++;
|
|
48464
|
+
break;
|
|
48465
|
+
|
|
48466
|
+
case "RF":
|
|
48467
|
+
this.rfCount++;
|
|
48468
|
+
}
|
|
48469
|
+
}
|
|
48470
|
+
|
|
48471
|
+
this.totalCount++;
|
|
48472
|
+
}
|
|
48473
|
+
}
|
|
48474
|
+
|
|
48475
|
+
if (this.ffCount / this.totalCount > 0.9) this.orienation = "ff";else if (this.frCount / this.totalCount > 0.9) this.orienation = "fr";else if (this.rfCount / this.totalCount > 0.9) this.orienation = "rf";
|
|
48476
|
+
this.minTLEN = this.lp === 0 ? 0 : percentile(this.isizes, this.lp);
|
|
48477
|
+
this.maxTLEN = percentile(this.isizes, this.up); // var fMean = this.sumF / this.totalCount
|
|
48478
|
+
// var stdDev = Math.sqrt((this.totalCount * this.sumF2 - this.sumF * this.sumF) / (this.totalCount * this.totalCount))
|
|
48479
|
+
// this.minTLEN = fMean - 3 * stdDev
|
|
48480
|
+
// this.maxTLEN = fMean + 3 * stdDev
|
|
48481
|
+
}
|
|
48482
|
+
|
|
48483
|
+
}
|
|
48484
|
+
|
|
48485
|
+
function percentile(array, p) {
|
|
48486
|
+
if (array.length === 0) return undefined;
|
|
48487
|
+
var k = Math.floor(array.length * (p / 100));
|
|
48488
|
+
array.sort(function (a, b) {
|
|
48489
|
+
return a - b;
|
|
48490
|
+
});
|
|
48491
|
+
return array[k];
|
|
48492
|
+
}
|
|
48493
|
+
|
|
48516
48494
|
/*
|
|
48517
48495
|
* The MIT License (MIT)
|
|
48518
48496
|
*
|
|
@@ -48572,10 +48550,7 @@
|
|
|
48572
48550
|
this.showInsertions = false !== config.showInsertions;
|
|
48573
48551
|
this.showMismatches = false !== config.showMismatches;
|
|
48574
48552
|
this.color = config.color;
|
|
48575
|
-
this.coverageColor = config.coverageColor;
|
|
48576
|
-
this.minFragmentLength = config.minFragmentLength; // Optional, might be undefined
|
|
48577
|
-
|
|
48578
|
-
this.maxFragmentLength = config.maxFragmentLength || 1000; // The sort object can be an array in the case of multi-locus view, however if multiple sort positions
|
|
48553
|
+
this.coverageColor = config.coverageColor; // The sort object can be an array in the case of multi-locus view, however if multiple sort positions
|
|
48579
48554
|
// are present for a given reference frame the last one will take precedence
|
|
48580
48555
|
|
|
48581
48556
|
if (config.sort) {
|
|
@@ -48603,12 +48578,22 @@
|
|
|
48603
48578
|
return this._height;
|
|
48604
48579
|
}
|
|
48605
48580
|
|
|
48581
|
+
get minTemplateLength() {
|
|
48582
|
+
const configMinTLEN = this.config.minTLEN !== undefined ? this.config.minTLEN : this.config.minFragmentLength;
|
|
48583
|
+
return configMinTLEN !== undefined ? configMinTLEN : this._pairedEndStats ? this._pairedEndStats.minTLEN : 0;
|
|
48584
|
+
}
|
|
48585
|
+
|
|
48586
|
+
get maxTemplateLength() {
|
|
48587
|
+
const configMaxTLEN = this.config.maxTLEN !== undefined ? this.config.maxTLEN : this.config.maxFragmentLength;
|
|
48588
|
+
return configMaxTLEN !== undefined ? configMaxTLEN : this._pairedEndStats ? this._pairedEndStats.maxTLEN : 1000;
|
|
48589
|
+
}
|
|
48590
|
+
|
|
48606
48591
|
sort(options) {
|
|
48607
48592
|
options = this.assignSort(options);
|
|
48608
48593
|
|
|
48609
48594
|
for (let vp of this.trackView.viewports) {
|
|
48610
48595
|
if (vp.containsPosition(options.chr, options.position)) {
|
|
48611
|
-
const alignmentContainer = vp.
|
|
48596
|
+
const alignmentContainer = vp.cachedFeatures;
|
|
48612
48597
|
|
|
48613
48598
|
if (alignmentContainer) {
|
|
48614
48599
|
sortAlignmentRows(options, alignmentContainer);
|
|
@@ -48643,16 +48628,16 @@
|
|
|
48643
48628
|
async getFeatures(chr, bpStart, bpEnd, bpPerPixel, viewport) {
|
|
48644
48629
|
const alignmentContainer = await this.featureSource.getAlignments(chr, bpStart, bpEnd);
|
|
48645
48630
|
|
|
48646
|
-
if (alignmentContainer.
|
|
48647
|
-
|
|
48648
|
-
this.minFragmentLength = alignmentContainer.pairedEndStats.lowerFragmentLength;
|
|
48649
|
-
}
|
|
48631
|
+
if (alignmentContainer.paired && !this._pairedEndStats && !this.config.maxFragmentLength) {
|
|
48632
|
+
const pairedEndStats = new PairedEndStats(alignmentContainer.alignments, this.config);
|
|
48650
48633
|
|
|
48651
|
-
if (
|
|
48652
|
-
this.
|
|
48634
|
+
if (pairedEndStats.totalCount > 99) {
|
|
48635
|
+
this._pairedEndStats = pairedEndStats;
|
|
48653
48636
|
}
|
|
48654
48637
|
}
|
|
48655
48638
|
|
|
48639
|
+
alignmentContainer.alignments = undefined; // Don't need to hold onto these anymore
|
|
48640
|
+
|
|
48656
48641
|
const sort = this.sortObject;
|
|
48657
48642
|
|
|
48658
48643
|
if (sort) {
|
|
@@ -48757,7 +48742,7 @@
|
|
|
48757
48742
|
label: 'pair orientation'
|
|
48758
48743
|
});
|
|
48759
48744
|
colorByMenuItems.push({
|
|
48760
|
-
key: '
|
|
48745
|
+
key: 'tlen',
|
|
48761
48746
|
label: 'insert size (TLEN)'
|
|
48762
48747
|
});
|
|
48763
48748
|
colorByMenuItems.push({
|
|
@@ -48868,36 +48853,19 @@
|
|
|
48868
48853
|
this.trackView.repaintViews();
|
|
48869
48854
|
}
|
|
48870
48855
|
});
|
|
48871
|
-
} //
|
|
48856
|
+
} // Add chords to JBrowse circular view, if present
|
|
48872
48857
|
|
|
48873
48858
|
|
|
48874
|
-
if (this.browser.circularView &&
|
|
48859
|
+
if (this.browser.circularView && (this.alignmentTrack.hasPairs || this.alignmentTrack.hasSupplemental)) {
|
|
48875
48860
|
menuItems.push('<hr/>');
|
|
48876
48861
|
|
|
48877
48862
|
if (this.alignmentTrack.hasPairs) {
|
|
48878
48863
|
menuItems.push({
|
|
48879
48864
|
label: 'Add discordant pairs to circular view',
|
|
48880
48865
|
click: () => {
|
|
48881
|
-
const maxFragmentLength = this.maxFragmentLength;
|
|
48882
|
-
const inView = [];
|
|
48883
|
-
|
|
48884
48866
|
for (let viewport of this.trackView.viewports) {
|
|
48885
|
-
|
|
48886
|
-
const referenceFrame = viewport.referenceFrame;
|
|
48887
|
-
|
|
48888
|
-
if (a.end >= referenceFrame.start && a.start <= referenceFrame.end && a.mate && a.mate.chr && (a.mate.chr !== a.chr || Math.max(a.fragmentLength) > maxFragmentLength)) {
|
|
48889
|
-
inView.push(a);
|
|
48890
|
-
}
|
|
48891
|
-
}
|
|
48867
|
+
this.addPairedChordsForViewport(viewport);
|
|
48892
48868
|
}
|
|
48893
|
-
|
|
48894
|
-
this.browser.circularViewVisible = true;
|
|
48895
|
-
const chords = makePairedAlignmentChords(inView);
|
|
48896
|
-
const color = IGVColor.addAlpha(this.color || 'rgb(0,0,255)', 0.02);
|
|
48897
|
-
this.browser.circularView.addChords(chords, {
|
|
48898
|
-
track: this.name,
|
|
48899
|
-
color: color
|
|
48900
|
-
});
|
|
48901
48869
|
}
|
|
48902
48870
|
});
|
|
48903
48871
|
}
|
|
@@ -48906,25 +48874,9 @@
|
|
|
48906
48874
|
menuItems.push({
|
|
48907
48875
|
label: 'Add split reads to circular view',
|
|
48908
48876
|
click: () => {
|
|
48909
|
-
const inView = [];
|
|
48910
|
-
|
|
48911
48877
|
for (let viewport of this.trackView.viewports) {
|
|
48912
|
-
|
|
48913
|
-
const referenceFrame = viewport.referenceFrame;
|
|
48914
|
-
const sa = a.hasTag('SA');
|
|
48915
|
-
|
|
48916
|
-
if (a.end >= referenceFrame.start && a.start <= referenceFrame.end && sa) {
|
|
48917
|
-
inView.push(a);
|
|
48918
|
-
}
|
|
48919
|
-
}
|
|
48878
|
+
this.addSplitChordsForViewport(viewport);
|
|
48920
48879
|
}
|
|
48921
|
-
|
|
48922
|
-
const chords = makeSupplementalAlignmentChords(inView);
|
|
48923
|
-
const color = IGVColor.addAlpha(this.color || 'rgb(0,0,255)', 0.1);
|
|
48924
|
-
this.browser.circularView.addChords(chords, {
|
|
48925
|
-
track: this.name,
|
|
48926
|
-
color: color
|
|
48927
|
-
});
|
|
48928
48880
|
}
|
|
48929
48881
|
});
|
|
48930
48882
|
}
|
|
@@ -49043,7 +48995,7 @@
|
|
|
49043
48995
|
}
|
|
49044
48996
|
|
|
49045
48997
|
getCachedAlignmentContainers() {
|
|
49046
|
-
return this.trackView.viewports.map(vp => vp.
|
|
48998
|
+
return this.trackView.viewports.map(vp => vp.cachedFeatures);
|
|
49047
48999
|
}
|
|
49048
49000
|
|
|
49049
49001
|
get dataRange() {
|
|
@@ -49069,6 +49021,56 @@
|
|
|
49069
49021
|
set autoscale(autoscale) {
|
|
49070
49022
|
this.coverageTrack.autoscale = autoscale;
|
|
49071
49023
|
}
|
|
49024
|
+
/**
|
|
49025
|
+
* Add chords to the circular view for the given viewport, represented by its reference frame
|
|
49026
|
+
* @param refFrame
|
|
49027
|
+
*/
|
|
49028
|
+
|
|
49029
|
+
|
|
49030
|
+
addPairedChordsForViewport(viewport) {
|
|
49031
|
+
const maxTemplateLength = this.maxTemplateLength;
|
|
49032
|
+
const inView = [];
|
|
49033
|
+
const refFrame = viewport.referenceFrame;
|
|
49034
|
+
|
|
49035
|
+
for (let a of viewport.cachedFeatures.allAlignments()) {
|
|
49036
|
+
if (a.end >= refFrame.start && a.start <= refFrame.end && a.mate && a.mate.chr && (a.mate.chr !== a.chr || Math.max(a.fragmentLength) > maxTemplateLength)) {
|
|
49037
|
+
inView.push(a);
|
|
49038
|
+
}
|
|
49039
|
+
}
|
|
49040
|
+
|
|
49041
|
+
const chords = makePairedAlignmentChords(inView);
|
|
49042
|
+
sendChords(chords, this, refFrame, 0.02); // const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? this.color : getChrColor(refFrame.chr), 0.02)
|
|
49043
|
+
// const trackColor = IGVColor.addAlpha(this.color || 'rgb(0,0,255)', 0.02)
|
|
49044
|
+
//
|
|
49045
|
+
// // name the chord set to include track name and locus
|
|
49046
|
+
// const encodedName = this.name.replaceAll(' ', '%20')
|
|
49047
|
+
// const chordSetName = "all" === refFrame.chr ? encodedName :
|
|
49048
|
+
// `${encodedName} (${refFrame.chr}:${refFrame.start}-${refFrame.end}`
|
|
49049
|
+
// this.browser.circularView.addChords(chords, {name: chordSetName, color: chordSetColor, trackColor: trackColor})
|
|
49050
|
+
}
|
|
49051
|
+
|
|
49052
|
+
addSplitChordsForViewport(viewport) {
|
|
49053
|
+
const inView = [];
|
|
49054
|
+
const refFrame = viewport.referenceFrame;
|
|
49055
|
+
|
|
49056
|
+
for (let a of viewport.cachedFeatures.allAlignments()) {
|
|
49057
|
+
const sa = a.hasTag('SA');
|
|
49058
|
+
|
|
49059
|
+
if (a.end >= refFrame.start && a.start <= refFrame.end && sa) {
|
|
49060
|
+
inView.push(a);
|
|
49061
|
+
}
|
|
49062
|
+
}
|
|
49063
|
+
|
|
49064
|
+
const chords = makeSupplementalAlignmentChords(inView);
|
|
49065
|
+
sendChords(chords, this, refFrame, 0.02); // const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? this.color : getChrColor(refFrame.chr), 0.02)
|
|
49066
|
+
// const trackColor = IGVColor.addAlpha(this.color || 'rgb(0,0,255)', 0.02)
|
|
49067
|
+
//
|
|
49068
|
+
// // name the chord set to include track name and locus
|
|
49069
|
+
// const encodedName = this.name.replaceAll(' ', '%20')
|
|
49070
|
+
// const chordSetName = "all" === refFrame.chr ? encodedName :
|
|
49071
|
+
// `${encodedName} (${refFrame.chr}:${refFrame.start}-${refFrame.end}`
|
|
49072
|
+
// this.browser.circularView.addChords(chords, {name: chordSetName, color: chordSetColor, trackColor: trackColor})
|
|
49073
|
+
}
|
|
49072
49074
|
|
|
49073
49075
|
}
|
|
49074
49076
|
|
|
@@ -49179,7 +49181,7 @@
|
|
|
49179
49181
|
}
|
|
49180
49182
|
|
|
49181
49183
|
getClickedObject(clickState) {
|
|
49182
|
-
let features = clickState.viewport.
|
|
49184
|
+
let features = clickState.viewport.cachedFeatures;
|
|
49183
49185
|
if (!features || features.length === 0) return;
|
|
49184
49186
|
const genomicLocation = Math.floor(clickState.genomicLocation);
|
|
49185
49187
|
const coverageMap = features.coverageMap;
|
|
@@ -49265,14 +49267,14 @@
|
|
|
49265
49267
|
this.deletionColor = config.deletionColor || "black";
|
|
49266
49268
|
this.skippedColor = config.skippedColor || "rgb(150, 170, 170)";
|
|
49267
49269
|
this.pairConnectorColor = config.pairConnectorColor;
|
|
49268
|
-
this.
|
|
49269
|
-
this.
|
|
49270
|
+
this.smallTLENColor = config.smallTLENColor || config.smallFragmentLengthColor || "rgb(0, 0, 150)";
|
|
49271
|
+
this.largeTLENColor = config.largeTLENColor || config.largeFragmentLengthColor || "rgb(200, 0, 0)";
|
|
49270
49272
|
this.pairOrientation = config.pairOrienation || 'fr';
|
|
49271
49273
|
this.pairColors = {};
|
|
49272
49274
|
this.pairColors["RL"] = config.rlColor || "rgb(0, 150, 0)";
|
|
49273
49275
|
this.pairColors["RR"] = config.rrColor || "rgb(20, 50, 200)";
|
|
49274
49276
|
this.pairColors["LL"] = config.llColor || "rgb(0, 150, 150)";
|
|
49275
|
-
this.colorBy = config.colorBy || "
|
|
49277
|
+
this.colorBy = config.colorBy || "unexpectedPair";
|
|
49276
49278
|
this.colorByTag = config.colorByTag ? config.colorByTag.toUpperCase() : undefined;
|
|
49277
49279
|
this.bamColorTag = config.bamColorTag === undefined ? "YC" : config.bamColorTag;
|
|
49278
49280
|
this.hideSmallIndels = config.hideSmallIndels;
|
|
@@ -49371,7 +49373,7 @@
|
|
|
49371
49373
|
for (let alignment of alignmentRow.alignments) {
|
|
49372
49374
|
this.hasPairs = this.hasPairs || alignment.isPaired();
|
|
49373
49375
|
|
|
49374
|
-
if (this.browser.circularView
|
|
49376
|
+
if (this.browser.circularView) {
|
|
49375
49377
|
// This is an expensive check, only do it if needed
|
|
49376
49378
|
this.hasSupplemental = this.hasSupplemental || alignment.hasTag('SA');
|
|
49377
49379
|
}
|
|
@@ -49627,7 +49629,7 @@
|
|
|
49627
49629
|
direction: direction
|
|
49628
49630
|
};
|
|
49629
49631
|
this.parent.sortObject = newSortObject;
|
|
49630
|
-
sortAlignmentRows(newSortObject, viewport.
|
|
49632
|
+
sortAlignmentRows(newSortObject, viewport.cachedFeatures);
|
|
49631
49633
|
viewport.repaint();
|
|
49632
49634
|
};
|
|
49633
49635
|
|
|
@@ -49679,7 +49681,7 @@
|
|
|
49679
49681
|
};
|
|
49680
49682
|
this.sortByTag = tag;
|
|
49681
49683
|
this.parent.sortObject = newSortObject;
|
|
49682
|
-
sortAlignmentRows(newSortObject, viewport.
|
|
49684
|
+
sortAlignmentRows(newSortObject, viewport.cachedFeatures);
|
|
49683
49685
|
viewport.repaint();
|
|
49684
49686
|
}
|
|
49685
49687
|
}
|
|
@@ -49703,8 +49705,12 @@
|
|
|
49703
49705
|
const referenceFrame = clickState.viewport.referenceFrame;
|
|
49704
49706
|
|
|
49705
49707
|
if (this.browser.genome.getChromosome(clickedAlignment.mate.chr)) {
|
|
49706
|
-
this.highlightedAlignmentReadNamed = clickedAlignment.readName;
|
|
49707
|
-
|
|
49708
|
+
this.highlightedAlignmentReadNamed = clickedAlignment.readName; //this.browser.presentMultiLocusPanel(clickedAlignment, referenceFrame)
|
|
49709
|
+
|
|
49710
|
+
const bpWidth = referenceFrame.end - referenceFrame.start;
|
|
49711
|
+
const frameStart = clickedAlignment.mate.position - bpWidth / 2;
|
|
49712
|
+
const frameEnd = clickedAlignment.mate.position + bpWidth / 2;
|
|
49713
|
+
this.browser.addMultiLocusPanel(clickedAlignment.mate.chr, frameStart, frameEnd, referenceFrame);
|
|
49708
49714
|
} else {
|
|
49709
49715
|
Alert.presentAlert(`Reference does not contain chromosome: ${clickedAlignment.mate.chr}`);
|
|
49710
49716
|
}
|
|
@@ -49717,9 +49723,7 @@
|
|
|
49717
49723
|
list.push({
|
|
49718
49724
|
label: 'View read sequence',
|
|
49719
49725
|
click: () => {
|
|
49720
|
-
const
|
|
49721
|
-
if (!alignment) return;
|
|
49722
|
-
const seqstring = alignment.seq; //.map(b => String.fromCharCode(b)).join("");
|
|
49726
|
+
const seqstring = clickedAlignment.seq; //.map(b => String.fromCharCode(b)).join("");
|
|
49723
49727
|
|
|
49724
49728
|
if (!seqstring || "*" === seqstring) {
|
|
49725
49729
|
Alert.presentAlert("Read sequence: *");
|
|
@@ -49732,12 +49736,16 @@
|
|
|
49732
49736
|
if (isSecureContext()) {
|
|
49733
49737
|
list.push({
|
|
49734
49738
|
label: 'Copy read sequence',
|
|
49735
|
-
click: () => {
|
|
49736
|
-
const
|
|
49737
|
-
if (!alignment) return;
|
|
49738
|
-
const seqstring = alignment.seq; //.map(b => String.fromCharCode(b)).join("");
|
|
49739
|
+
click: async () => {
|
|
49740
|
+
const seq = clickedAlignment.seq; //.map(b => String.fromCharCode(b)).join("");
|
|
49739
49741
|
|
|
49740
|
-
|
|
49742
|
+
try {
|
|
49743
|
+
//console.log(`seq: ${seq}`)
|
|
49744
|
+
await navigator.clipboard.writeText(seq);
|
|
49745
|
+
} catch (e) {
|
|
49746
|
+
console.error(e);
|
|
49747
|
+
Alert.presentAlert(`error copying sequence to clipboard ${e}`);
|
|
49748
|
+
}
|
|
49741
49749
|
}
|
|
49742
49750
|
});
|
|
49743
49751
|
}
|
|
@@ -49747,25 +49755,12 @@
|
|
|
49747
49755
|
} // Experimental JBrowse feature
|
|
49748
49756
|
|
|
49749
49757
|
|
|
49750
|
-
if (this.browser.circularView &&
|
|
49758
|
+
if (this.browser.circularView && (this.hasPairs || this.hasSupplemental)) {
|
|
49751
49759
|
if (this.hasPairs) {
|
|
49752
49760
|
list.push({
|
|
49753
49761
|
label: 'Add discordant pairs to circular view',
|
|
49754
49762
|
click: () => {
|
|
49755
|
-
|
|
49756
|
-
const {
|
|
49757
|
-
referenceFrame
|
|
49758
|
-
} = viewport;
|
|
49759
|
-
const inView = viewport.getCachedFeatures().allAlignments().filter(a => {
|
|
49760
|
-
return a.end >= referenceFrame.start && a.start <= referenceFrame.end && a.mate && a.mate.chr && (a.mate.chr !== a.chr || Math.max(a.fragmentLength) > maxFragmentLength);
|
|
49761
|
-
});
|
|
49762
|
-
this.browser.circularViewVisible = true;
|
|
49763
|
-
const chords = makePairedAlignmentChords(inView);
|
|
49764
|
-
const color = IGVColor.addAlpha(this.parent.color || 'rgb(0,0,255)', 0.02);
|
|
49765
|
-
this.browser.circularView.addChords(chords, {
|
|
49766
|
-
track: this.parent.name,
|
|
49767
|
-
color: color
|
|
49768
|
-
});
|
|
49763
|
+
this.parent.addPairedChordsForViewport(viewport);
|
|
49769
49764
|
}
|
|
49770
49765
|
});
|
|
49771
49766
|
}
|
|
@@ -49774,23 +49769,7 @@
|
|
|
49774
49769
|
list.push({
|
|
49775
49770
|
label: 'Add split reads to circular view',
|
|
49776
49771
|
click: () => {
|
|
49777
|
-
|
|
49778
|
-
|
|
49779
|
-
for (let a of viewport.getCachedFeatures().allAlignments()) {
|
|
49780
|
-
const referenceFrame = viewport.referenceFrame;
|
|
49781
|
-
const sa = a.hasTag('SA');
|
|
49782
|
-
|
|
49783
|
-
if (a.end >= referenceFrame.start && a.start <= referenceFrame.end && sa) {
|
|
49784
|
-
inView.push(a);
|
|
49785
|
-
}
|
|
49786
|
-
}
|
|
49787
|
-
|
|
49788
|
-
const chords = makeSupplementalAlignmentChords(inView);
|
|
49789
|
-
const color = IGVColor.addAlpha(this.color || 'rgb(0,0,255)', 0.1);
|
|
49790
|
-
this.browser.circularView.addChords(chords, {
|
|
49791
|
-
track: this.name,
|
|
49792
|
-
color: color
|
|
49793
|
-
});
|
|
49772
|
+
this.parent.addSplitChordsForViewport(viewport);
|
|
49794
49773
|
}
|
|
49795
49774
|
});
|
|
49796
49775
|
}
|
|
@@ -49806,7 +49785,7 @@
|
|
|
49806
49785
|
const y = clickState.y;
|
|
49807
49786
|
const genomicLocation = clickState.genomicLocation;
|
|
49808
49787
|
const showSoftClips = this.parent.showSoftClips;
|
|
49809
|
-
let features = viewport.
|
|
49788
|
+
let features = viewport.cachedFeatures;
|
|
49810
49789
|
if (!features || features.length === 0) return;
|
|
49811
49790
|
let packedAlignmentRows = features.packedAlignmentRows;
|
|
49812
49791
|
let downsampledIntervals = features.downsampledIntervals;
|
|
@@ -49892,13 +49871,16 @@
|
|
|
49892
49871
|
break;
|
|
49893
49872
|
}
|
|
49894
49873
|
|
|
49874
|
+
case "tlen":
|
|
49895
49875
|
case "fragmentLength":
|
|
49896
|
-
if (alignment.mate && alignment.isMateMapped()
|
|
49897
|
-
|
|
49898
|
-
|
|
49899
|
-
|
|
49900
|
-
|
|
49901
|
-
|
|
49876
|
+
if (alignment.mate && alignment.isMateMapped()) {
|
|
49877
|
+
if (alignment.mate.chr !== alignment.chr) {
|
|
49878
|
+
color = getChrColor(alignment.mate.chr);
|
|
49879
|
+
} else if (this.parent.minTemplateLength && Math.abs(alignment.fragmentLength) < this.parent.minTemplateLength) {
|
|
49880
|
+
color = this.smallTLENColor;
|
|
49881
|
+
} else if (this.parent.maxTemplateLength && Math.abs(alignment.fragmentLength) > this.parent.maxTemplateLength) {
|
|
49882
|
+
color = this.largeTLENColor;
|
|
49883
|
+
}
|
|
49902
49884
|
}
|
|
49903
49885
|
|
|
49904
49886
|
break;
|
|
@@ -49940,10 +49922,7 @@
|
|
|
49940
49922
|
alignmentContainer.packedAlignmentRows.sort(function (rowA, rowB) {
|
|
49941
49923
|
const i = rowA.score > rowB.score ? 1 : rowA.score < rowB.score ? -1 : 0;
|
|
49942
49924
|
return true === direction ? i : -i;
|
|
49943
|
-
});
|
|
49944
|
-
// for(let r of alignmentContainer.packedAlignmentRows) {
|
|
49945
|
-
// console.log(r.score);
|
|
49946
|
-
// }
|
|
49925
|
+
});
|
|
49947
49926
|
}
|
|
49948
49927
|
|
|
49949
49928
|
function shadedBaseColor(qual, baseColor) {
|
|
@@ -50098,7 +50077,7 @@
|
|
|
50098
50077
|
});
|
|
50099
50078
|
this.$viewport.append(this.$rulerLabel);
|
|
50100
50079
|
this.$rulerLabel.click(async () => {
|
|
50101
|
-
await this.browser.
|
|
50080
|
+
await this.browser.gotoMultilocusPanel(this.referenceFrame); // const removals = this.browser.referenceFrameList.filter(r => this.referenceFrame !== r)
|
|
50102
50081
|
// for (let referenceFrame of removals) {
|
|
50103
50082
|
// await this.browser.removeMultiLocusPanel(referenceFrame)
|
|
50104
50083
|
// }
|
|
@@ -50204,7 +50183,10 @@
|
|
|
50204
50183
|
currentViewport = this;
|
|
50205
50184
|
this.$tooltip.show();
|
|
50206
50185
|
} else if (currentViewport.guid !== this.guid) {
|
|
50207
|
-
currentViewport.$tooltip
|
|
50186
|
+
if (currentViewport.$tooltip) {
|
|
50187
|
+
currentViewport.$tooltip.hide();
|
|
50188
|
+
}
|
|
50189
|
+
|
|
50208
50190
|
this.$tooltip.show();
|
|
50209
50191
|
currentViewport = this;
|
|
50210
50192
|
} else {
|
|
@@ -50238,7 +50220,9 @@
|
|
|
50238
50220
|
}); // hide tooltip when movement stops
|
|
50239
50221
|
|
|
50240
50222
|
clearTimeout(timer);
|
|
50241
|
-
timer = setTimeout(() =>
|
|
50223
|
+
timer = setTimeout(() => {
|
|
50224
|
+
if (this.$tooltip) this.$tooltip.hide();
|
|
50225
|
+
}, toolTipTimeout);
|
|
50242
50226
|
}
|
|
50243
50227
|
}
|
|
50244
50228
|
|
|
@@ -50253,65 +50237,6 @@
|
|
|
50253
50237
|
|
|
50254
50238
|
}
|
|
50255
50239
|
|
|
50256
|
-
const viewportColumnManager = {
|
|
50257
|
-
createColumns: (columnContainer, count) => {
|
|
50258
|
-
for (let i = 0; i < count; i++) {
|
|
50259
|
-
if (0 === i) {
|
|
50260
|
-
createColumn(columnContainer, 'igv-column');
|
|
50261
|
-
} else {
|
|
50262
|
-
columnContainer.appendChild(div$1({
|
|
50263
|
-
class: 'igv-column-shim'
|
|
50264
|
-
}));
|
|
50265
|
-
createColumn(columnContainer, 'igv-column');
|
|
50266
|
-
}
|
|
50267
|
-
}
|
|
50268
|
-
},
|
|
50269
|
-
removeColumnAtIndex: (i, column) => {
|
|
50270
|
-
const shim = 0 === i ? column.nextElementSibling : column.previousElementSibling;
|
|
50271
|
-
column.remove();
|
|
50272
|
-
shim.remove();
|
|
50273
|
-
},
|
|
50274
|
-
insertAfter: referenceElement => {
|
|
50275
|
-
const shim = div$1({
|
|
50276
|
-
class: 'igv-column-shim'
|
|
50277
|
-
});
|
|
50278
|
-
insertElementAfter(shim, referenceElement);
|
|
50279
|
-
const column = div$1({
|
|
50280
|
-
class: 'igv-column'
|
|
50281
|
-
});
|
|
50282
|
-
insertElementAfter(column, shim);
|
|
50283
|
-
return column;
|
|
50284
|
-
},
|
|
50285
|
-
insertBefore: (referenceElement, count) => {
|
|
50286
|
-
for (let i = 0; i < count; i++) {
|
|
50287
|
-
const column = div$1({
|
|
50288
|
-
class: 'igv-column'
|
|
50289
|
-
});
|
|
50290
|
-
insertElementBefore(column, referenceElement);
|
|
50291
|
-
|
|
50292
|
-
if (count > 1 && i > 0) {
|
|
50293
|
-
const columnShim = div$1({
|
|
50294
|
-
class: 'igv-column-shim'
|
|
50295
|
-
});
|
|
50296
|
-
insertElementBefore(columnShim, column);
|
|
50297
|
-
}
|
|
50298
|
-
}
|
|
50299
|
-
},
|
|
50300
|
-
indexOfColumn: (columnContainer, column) => {
|
|
50301
|
-
const allColumns = columnContainer.querySelectorAll('.igv-column');
|
|
50302
|
-
|
|
50303
|
-
for (let i = 0; i < allColumns.length; i++) {
|
|
50304
|
-
const c = allColumns[i];
|
|
50305
|
-
|
|
50306
|
-
if (c === column) {
|
|
50307
|
-
return i;
|
|
50308
|
-
}
|
|
50309
|
-
}
|
|
50310
|
-
|
|
50311
|
-
return undefined;
|
|
50312
|
-
}
|
|
50313
|
-
};
|
|
50314
|
-
|
|
50315
50240
|
/*
|
|
50316
50241
|
* The MIT License (MIT)
|
|
50317
50242
|
*
|
|
@@ -50343,60 +50268,30 @@
|
|
|
50343
50268
|
}
|
|
50344
50269
|
|
|
50345
50270
|
initializationHelper() {
|
|
50346
|
-
this
|
|
50347
|
-
|
|
50348
|
-
|
|
50349
|
-
this
|
|
50350
|
-
const canvas = this.$ideogramCanvas.get(0);
|
|
50351
|
-
this.ideogram_ctx = canvas.getContext('2d');
|
|
50352
|
-
this.$canvas.remove();
|
|
50353
|
-
this.canvas = undefined;
|
|
50354
|
-
this.ctx = undefined;
|
|
50271
|
+
this.canvas = document.createElement('canvas');
|
|
50272
|
+
this.canvas.className = 'igv-ideogram-canvas';
|
|
50273
|
+
this.$content.append($$1(this.canvas));
|
|
50274
|
+
this.ideogram_ctx = this.canvas.getContext('2d');
|
|
50355
50275
|
this.addMouseHandlers();
|
|
50356
50276
|
}
|
|
50357
50277
|
|
|
50358
50278
|
addMouseHandlers() {
|
|
50359
|
-
this.addBrowserObserver();
|
|
50360
50279
|
this.addViewportClickHandler(this.$viewport.get(0));
|
|
50361
50280
|
}
|
|
50362
50281
|
|
|
50363
|
-
removeMouseHandlers() {
|
|
50364
|
-
this.removeBrowserObserver();
|
|
50365
|
-
this.removeViewportClickHandler(this.$viewport.get(0));
|
|
50366
|
-
}
|
|
50367
|
-
|
|
50368
|
-
addBrowserObserver() {
|
|
50369
|
-
function observerHandler(referenceFrameList) {
|
|
50370
|
-
const column = this.$viewport.get(0).parentElement;
|
|
50371
|
-
|
|
50372
|
-
if (null !== column) {
|
|
50373
|
-
const index = viewportColumnManager.indexOfColumn(this.browser.columnContainer, column); // console.log(`ideogram-viewport - locus-change-handler index(${ index }) ${ referenceFrameList[ index ].getLocusString() } ${ Date.now() } `)
|
|
50374
|
-
|
|
50375
|
-
this.update(this.ideogram_ctx, this.$viewport.width(), this.$viewport.height(), referenceFrameList[index]);
|
|
50376
|
-
}
|
|
50377
|
-
}
|
|
50378
|
-
|
|
50379
|
-
this.boundObserverHandler = observerHandler.bind(this);
|
|
50380
|
-
this.browser.on('locuschange', this.boundObserverHandler);
|
|
50381
|
-
}
|
|
50382
|
-
|
|
50383
|
-
removeBrowserObserver() {
|
|
50384
|
-
this.browser.off('locuschange', this.boundObserverHandler);
|
|
50385
|
-
}
|
|
50386
|
-
|
|
50387
50282
|
addViewportClickHandler(viewport) {
|
|
50283
|
+
this.boundClickHandler = clickHandler.bind(this);
|
|
50284
|
+
viewport.addEventListener('click', this.boundClickHandler);
|
|
50285
|
+
|
|
50388
50286
|
function clickHandler(event) {
|
|
50389
|
-
const column = viewport.parentElement;
|
|
50390
|
-
const index = viewportColumnManager.indexOfColumn(this.browser.columnContainer, column);
|
|
50391
|
-
const referenceFrame = this.browser.referenceFrameList[index];
|
|
50392
50287
|
const {
|
|
50393
50288
|
xNormalized,
|
|
50394
50289
|
width
|
|
50395
50290
|
} = translateMouseCoordinates$1(event, this.ideogram_ctx.canvas);
|
|
50396
50291
|
const {
|
|
50397
50292
|
bpLength
|
|
50398
|
-
} = this.browser.genome.getChromosome(referenceFrame.chr);
|
|
50399
|
-
const locusLength = referenceFrame.bpPerPixel * width;
|
|
50293
|
+
} = this.browser.genome.getChromosome(this.referenceFrame.chr);
|
|
50294
|
+
const locusLength = this.referenceFrame.bpPerPixel * width;
|
|
50400
50295
|
const chrCoveragePercentage = locusLength / bpLength;
|
|
50401
50296
|
let xPercentage = xNormalized;
|
|
50402
50297
|
|
|
@@ -50410,18 +50305,11 @@
|
|
|
50410
50305
|
|
|
50411
50306
|
const ss = Math.round((xPercentage - chrCoveragePercentage / 2.0) * bpLength);
|
|
50412
50307
|
const ee = Math.round((xPercentage + chrCoveragePercentage / 2.0) * bpLength);
|
|
50413
|
-
referenceFrame.start = ss;
|
|
50414
|
-
referenceFrame.end = ee;
|
|
50415
|
-
referenceFrame.bpPerPixel = (ee - ss) / width;
|
|
50416
|
-
this.browser.updateViews(referenceFrame, this.browser.trackViews, true);
|
|
50308
|
+
this.referenceFrame.start = ss;
|
|
50309
|
+
this.referenceFrame.end = ee;
|
|
50310
|
+
this.referenceFrame.bpPerPixel = (ee - ss) / width;
|
|
50311
|
+
this.browser.updateViews(this.referenceFrame, this.browser.trackViews, true);
|
|
50417
50312
|
}
|
|
50418
|
-
|
|
50419
|
-
this.boundClickHandler = clickHandler.bind(this);
|
|
50420
|
-
viewport.addEventListener('click', this.boundClickHandler);
|
|
50421
|
-
}
|
|
50422
|
-
|
|
50423
|
-
removeViewportClickHandler(viewport) {
|
|
50424
|
-
viewport.removeEventListener('click', this.boundClickHandler);
|
|
50425
50313
|
}
|
|
50426
50314
|
|
|
50427
50315
|
setWidth(width) {
|
|
@@ -50439,14 +50327,22 @@
|
|
|
50439
50327
|
context.restore();
|
|
50440
50328
|
}
|
|
50441
50329
|
|
|
50442
|
-
|
|
50443
|
-
this
|
|
50444
|
-
|
|
50330
|
+
repaint() {
|
|
50331
|
+
this.draw({
|
|
50332
|
+
referenceFrame: this.referenceFrame
|
|
50333
|
+
});
|
|
50334
|
+
}
|
|
50335
|
+
|
|
50336
|
+
draw(_ref) {
|
|
50337
|
+
let {
|
|
50338
|
+
referenceFrame
|
|
50339
|
+
} = _ref;
|
|
50340
|
+
IGVGraphics.configureHighDPICanvas(this.ideogram_ctx, this.$viewport.width(), this.$viewport.height());
|
|
50445
50341
|
this.trackView.track.draw({
|
|
50446
|
-
context,
|
|
50342
|
+
context: this.ideogram_ctx,
|
|
50447
50343
|
referenceFrame,
|
|
50448
|
-
pixelWidth,
|
|
50449
|
-
pixelHeight
|
|
50344
|
+
pixelWidth: this.$viewport.width(),
|
|
50345
|
+
pixelHeight: this.$viewport.height()
|
|
50450
50346
|
});
|
|
50451
50347
|
}
|
|
50452
50348
|
|
|
@@ -50484,7 +50380,7 @@
|
|
|
50484
50380
|
function createViewport(trackView, column, referenceFrame, width) {
|
|
50485
50381
|
if ('ruler' === trackView.track.type) {
|
|
50486
50382
|
return new RulerViewport(trackView, column, referenceFrame, width);
|
|
50487
|
-
} else if ('ideogram' === trackView.track.
|
|
50383
|
+
} else if ('ideogram' === trackView.track.id) {
|
|
50488
50384
|
return new IdeogramViewport(trackView, column, referenceFrame, width);
|
|
50489
50385
|
} else {
|
|
50490
50386
|
return new TrackViewport(trackView, column, referenceFrame, width);
|
|
@@ -51011,7 +50907,6 @@
|
|
|
51011
50907
|
|
|
51012
50908
|
class TrackView {
|
|
51013
50909
|
constructor(browser, columnContainer, track) {
|
|
51014
|
-
this.namespace = `trackview-${guid$2()}`;
|
|
51015
50910
|
this.browser = browser;
|
|
51016
50911
|
this.track = track;
|
|
51017
50912
|
track.trackView = this;
|
|
@@ -51037,7 +50932,7 @@
|
|
|
51037
50932
|
|
|
51038
50933
|
addDOMToColumnContainer(browser, columnContainer, referenceFrameList) {
|
|
51039
50934
|
// Axis
|
|
51040
|
-
this.axis = this.createAxis(browser, this.track); //
|
|
50935
|
+
this.axis = this.createAxis(browser, this.track); // Create a viewport for each reference frame
|
|
51041
50936
|
|
|
51042
50937
|
this.viewports = [];
|
|
51043
50938
|
const viewportWidth = browser.calculateViewportWidth(referenceFrameList.length);
|
|
@@ -51104,7 +50999,6 @@
|
|
|
51104
50999
|
this.axis.remove(); // Track Viewports
|
|
51105
51000
|
|
|
51106
51001
|
for (let viewport of this.viewports) {
|
|
51107
|
-
viewport.removeMouseHandlers();
|
|
51108
51002
|
viewport.$viewport.remove();
|
|
51109
51003
|
} // SampleName Viewport
|
|
51110
51004
|
|
|
@@ -51222,7 +51116,7 @@
|
|
|
51222
51116
|
$viewport.height(newHeight);
|
|
51223
51117
|
}
|
|
51224
51118
|
|
|
51225
|
-
this.sampleNameViewport.viewport.style.height = `${newHeight}px`; // If the track does not manage its own content height set it here
|
|
51119
|
+
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
|
|
51226
51120
|
|
|
51227
51121
|
if (typeof this.track.computePixelHeight !== "function") {
|
|
51228
51122
|
for (let vp of this.viewports) {
|
|
@@ -51274,14 +51168,6 @@
|
|
|
51274
51168
|
if (viewport.isLoading()) return true;
|
|
51275
51169
|
}
|
|
51276
51170
|
}
|
|
51277
|
-
|
|
51278
|
-
resize(viewportWidth) {
|
|
51279
|
-
for (let viewport of this.viewports) {
|
|
51280
|
-
viewport.setWidth(viewportWidth);
|
|
51281
|
-
}
|
|
51282
|
-
|
|
51283
|
-
this.updateViews(true);
|
|
51284
|
-
}
|
|
51285
51171
|
/**
|
|
51286
51172
|
* Repaint all viewports without loading any new data. Use this for events that change visual aspect of data,
|
|
51287
51173
|
* e.g. color, sort order, etc, but do not change the genomic state.
|
|
@@ -51290,7 +51176,9 @@
|
|
|
51290
51176
|
|
|
51291
51177
|
repaintViews() {
|
|
51292
51178
|
for (let viewport of this.viewports) {
|
|
51293
|
-
viewport.
|
|
51179
|
+
if (viewport.isVisible()) {
|
|
51180
|
+
viewport.repaint();
|
|
51181
|
+
}
|
|
51294
51182
|
}
|
|
51295
51183
|
|
|
51296
51184
|
if (typeof this.track.paintAxis === 'function') {
|
|
@@ -51312,41 +51200,60 @@
|
|
|
51312
51200
|
setTrackLabelName(name) {
|
|
51313
51201
|
this.viewports.forEach(viewport => viewport.setTrackLabel(name));
|
|
51314
51202
|
}
|
|
51203
|
+
/**
|
|
51204
|
+
* Called in response to a window resize event, change in # of multilocus panels, or other event that changes
|
|
51205
|
+
* the width of the track view.
|
|
51206
|
+
*
|
|
51207
|
+
* @param viewportWidth The width of each viewport in this track view.
|
|
51208
|
+
*/
|
|
51209
|
+
|
|
51210
|
+
|
|
51211
|
+
resize(viewportWidth) {
|
|
51212
|
+
for (let viewport of this.viewports) {
|
|
51213
|
+
viewport.setWidth(viewportWidth);
|
|
51214
|
+
}
|
|
51215
|
+
}
|
|
51315
51216
|
/**
|
|
51316
51217
|
* Update viewports to reflect current genomic state, possibly loading additional data.
|
|
51218
|
+
*
|
|
51219
|
+
* @param force - if true, force a repaint even if no new data is loaded
|
|
51220
|
+
* @returns {Promise<void>}
|
|
51317
51221
|
*/
|
|
51318
51222
|
|
|
51319
51223
|
|
|
51320
|
-
async updateViews(
|
|
51224
|
+
async updateViews() {
|
|
51321
51225
|
if (!(this.browser && this.browser.referenceFrameList)) return;
|
|
51322
51226
|
const visibleViewports = this.viewports.filter(viewport => viewport.isVisible()); // Shift viewports left/right to current genomic state (pans canvas)
|
|
51323
51227
|
|
|
51324
|
-
visibleViewports.forEach(viewport => viewport.shift());
|
|
51325
|
-
const isDragging = this.browser.dragObject;
|
|
51228
|
+
visibleViewports.forEach(viewport => viewport.shift()); // If dragging (panning) return
|
|
51326
51229
|
|
|
51327
|
-
if (
|
|
51230
|
+
if (this.browser.dragObject) {
|
|
51328
51231
|
return;
|
|
51329
|
-
} //
|
|
51232
|
+
} // Get viewports to repaint
|
|
51233
|
+
|
|
51330
51234
|
|
|
51235
|
+
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
|
|
51331
51236
|
|
|
51332
|
-
|
|
51237
|
+
viewportsToRepaint = viewportsToRepaint.filter(viewport => viewport.checkZoomIn()); // Get viewports that require a data load
|
|
51238
|
+
|
|
51239
|
+
const viewportsToReload = viewportsToRepaint.filter(viewport => viewport.needsReload()); // Trigger viewport to load features needed to cover current genomic range
|
|
51333
51240
|
// NOTE: these must be loaded synchronously, do not user Promise.all, not all file readers are thread safe
|
|
51334
51241
|
|
|
51335
|
-
for (let viewport of
|
|
51242
|
+
for (let viewport of viewportsToReload) {
|
|
51336
51243
|
await viewport.loadFeatures();
|
|
51337
51244
|
}
|
|
51338
51245
|
|
|
51339
51246
|
if (this.disposed) return; // Track was removed during load
|
|
51340
|
-
//
|
|
51247
|
+
// Special case for variant tracks in multilocus view. The # of rows to allocate to the variant (site)
|
|
51341
51248
|
// section depends on data from all the views. We only need to adjust this however if any data was loaded
|
|
51342
51249
|
// (i.e. reloadableViewports.length > 0)
|
|
51343
51250
|
|
|
51344
|
-
if (this.track && typeof this.track.variantRowCount === 'function' &&
|
|
51251
|
+
if (this.track && typeof this.track.variantRowCount === 'function' && viewportsToReload.length > 0) {
|
|
51345
51252
|
let maxRow = 0;
|
|
51346
51253
|
|
|
51347
51254
|
for (let viewport of this.viewports) {
|
|
51348
|
-
if (viewport.
|
|
51349
|
-
maxRow = Math.max(maxRow, viewport.
|
|
51255
|
+
if (viewport.featureCache && viewport.featureCache.features) {
|
|
51256
|
+
maxRow = Math.max(maxRow, viewport.featureCache.features.reduce((a, f) => Math.max(a, f.row || 0), 0));
|
|
51350
51257
|
}
|
|
51351
51258
|
}
|
|
51352
51259
|
|
|
@@ -51369,14 +51276,14 @@
|
|
|
51369
51276
|
const start = referenceFrame.start;
|
|
51370
51277
|
const end = start + referenceFrame.toBP($$1(visibleViewport.contentDiv).width());
|
|
51371
51278
|
|
|
51372
|
-
if (visibleViewport.
|
|
51373
|
-
if (typeof visibleViewport.
|
|
51374
|
-
const max = visibleViewport.
|
|
51279
|
+
if (visibleViewport.featureCache && visibleViewport.featureCache.features) {
|
|
51280
|
+
if (typeof visibleViewport.featureCache.features.getMax === 'function') {
|
|
51281
|
+
const max = visibleViewport.featureCache.features.getMax(start, end);
|
|
51375
51282
|
allFeatures.push({
|
|
51376
51283
|
value: max
|
|
51377
51284
|
});
|
|
51378
51285
|
} else {
|
|
51379
|
-
allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(visibleViewport.
|
|
51286
|
+
allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(visibleViewport.featureCache.features, start, end));
|
|
51380
51287
|
}
|
|
51381
51288
|
}
|
|
51382
51289
|
}
|
|
@@ -51386,17 +51293,10 @@
|
|
|
51386
51293
|
} else {
|
|
51387
51294
|
this.track.dataRange = doAutoscale(allFeatures);
|
|
51388
51295
|
}
|
|
51389
|
-
}
|
|
51390
|
-
|
|
51296
|
+
}
|
|
51391
51297
|
|
|
51392
|
-
|
|
51393
|
-
|
|
51394
|
-
visibleViewport.repaint();
|
|
51395
|
-
}
|
|
51396
|
-
} else {
|
|
51397
|
-
for (let vp of reloadableViewports) {
|
|
51398
|
-
vp.repaint();
|
|
51399
|
-
}
|
|
51298
|
+
for (let vp of viewportsToRepaint) {
|
|
51299
|
+
vp.repaint();
|
|
51400
51300
|
}
|
|
51401
51301
|
|
|
51402
51302
|
this.adjustTrackHeight(); // Repaint sample names last
|
|
@@ -51419,36 +51319,34 @@
|
|
|
51419
51319
|
}
|
|
51420
51320
|
}
|
|
51421
51321
|
/**
|
|
51422
|
-
* Return a promise to get all in-view features. Used for group autoscaling.
|
|
51322
|
+
* Return a promise to get all in-view features across all viewports. Used for group autoscaling.
|
|
51423
51323
|
*/
|
|
51424
51324
|
|
|
51425
51325
|
|
|
51426
|
-
async getInViewFeatures(
|
|
51326
|
+
async getInViewFeatures() {
|
|
51427
51327
|
if (!(this.browser && this.browser.referenceFrameList)) {
|
|
51428
51328
|
return [];
|
|
51429
|
-
}
|
|
51430
|
-
|
|
51329
|
+
}
|
|
51431
51330
|
|
|
51432
|
-
const rpV = this.viewportsToReload(force);
|
|
51433
|
-
const promises = rpV.map(function (vp) {
|
|
51434
|
-
return vp.loadFeatures();
|
|
51435
|
-
});
|
|
51436
|
-
await Promise.all(promises);
|
|
51437
51331
|
let allFeatures = [];
|
|
51438
51332
|
|
|
51439
51333
|
for (let vp of this.viewports) {
|
|
51440
|
-
if (vp.
|
|
51334
|
+
if (vp.needsReload()) {
|
|
51335
|
+
await vp.loadFeatures();
|
|
51336
|
+
}
|
|
51337
|
+
|
|
51338
|
+
if (vp.featureCache && vp.featureCache.features) {
|
|
51441
51339
|
const referenceFrame = vp.referenceFrame;
|
|
51442
51340
|
const start = referenceFrame.start;
|
|
51443
51341
|
const end = start + referenceFrame.toBP($$1(vp.contentDiv).width());
|
|
51444
51342
|
|
|
51445
|
-
if (typeof vp.
|
|
51446
|
-
const max = vp.
|
|
51343
|
+
if (typeof vp.featureCache.features.getMax === 'function') {
|
|
51344
|
+
const max = vp.featureCache.features.getMax(start, end);
|
|
51447
51345
|
allFeatures.push({
|
|
51448
51346
|
value: max
|
|
51449
51347
|
});
|
|
51450
51348
|
} else {
|
|
51451
|
-
allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(vp.
|
|
51349
|
+
allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(vp.featureCache.features, start, end));
|
|
51452
51350
|
}
|
|
51453
51351
|
}
|
|
51454
51352
|
}
|
|
@@ -51490,27 +51388,6 @@
|
|
|
51490
51388
|
}
|
|
51491
51389
|
}
|
|
51492
51390
|
|
|
51493
|
-
viewportsToReload(force) {
|
|
51494
|
-
// List of viewports that need reloading
|
|
51495
|
-
const viewports = this.viewports.filter(viewport => {
|
|
51496
|
-
if (!viewport.isVisible()) {
|
|
51497
|
-
return false;
|
|
51498
|
-
}
|
|
51499
|
-
|
|
51500
|
-
if (!viewport.checkZoomIn()) {
|
|
51501
|
-
return false;
|
|
51502
|
-
} else {
|
|
51503
|
-
const referenceFrame = viewport.referenceFrame;
|
|
51504
|
-
const chr = viewport.referenceFrame.chr;
|
|
51505
|
-
const start = referenceFrame.start;
|
|
51506
|
-
const end = start + referenceFrame.toBP($$1(viewport.contentDiv).width());
|
|
51507
|
-
const bpPerPixel = referenceFrame.bpPerPixel;
|
|
51508
|
-
return force || !viewport.tile || viewport.tile.invalidate || !viewport.tile.containsRange(chr, start, end, bpPerPixel);
|
|
51509
|
-
}
|
|
51510
|
-
});
|
|
51511
|
-
return viewports;
|
|
51512
|
-
}
|
|
51513
|
-
|
|
51514
51391
|
createTrackScrollbar(browser) {
|
|
51515
51392
|
const outerScroll = div$1();
|
|
51516
51393
|
browser.columnContainer.querySelector('.igv-scrollbar-column').appendChild(outerScroll);
|
|
@@ -51604,7 +51481,7 @@
|
|
|
51604
51481
|
}
|
|
51605
51482
|
|
|
51606
51483
|
addTrackDragMouseHandlers(browser) {
|
|
51607
|
-
if ('ideogram' === this.track.
|
|
51484
|
+
if ('ideogram' === this.track.id || 'ruler' === this.track.id) ; else {
|
|
51608
51485
|
let currentDragHandle = undefined; // Mouse Down
|
|
51609
51486
|
|
|
51610
51487
|
this.boundTrackDragMouseDownHandler = trackDragMouseDownHandler.bind(this);
|
|
@@ -51668,7 +51545,7 @@
|
|
|
51668
51545
|
}
|
|
51669
51546
|
|
|
51670
51547
|
removeTrackDragMouseHandlers() {
|
|
51671
|
-
if ('ideogram' === this.track.
|
|
51548
|
+
if ('ideogram' === this.track.id || 'ruler' === this.track.id) ; else {
|
|
51672
51549
|
this.dragHandle.removeEventListener('mousedown', this.boundTrackDragMouseDownHandler);
|
|
51673
51550
|
document.removeEventListener('mouseup', this.boundDocumentTrackDragMouseUpHandler);
|
|
51674
51551
|
this.dragHandle.removeEventListener('mouseup', this.boundTrackDragMouseEnterHandler);
|
|
@@ -56327,7 +56204,7 @@
|
|
|
56327
56204
|
}
|
|
56328
56205
|
|
|
56329
56206
|
this.queryable = false;
|
|
56330
|
-
this.featureCache = new FeatureCache(features, genome);
|
|
56207
|
+
this.featureCache = new FeatureCache$1(features, genome);
|
|
56331
56208
|
} else if (config.reader) {
|
|
56332
56209
|
// Explicit reader implementation
|
|
56333
56210
|
this.reader = config.reader;
|
|
@@ -56498,13 +56375,13 @@
|
|
|
56498
56375
|
} // Note - replacing previous cache with new one. genomicInterval is optional (might be undefined => includes all features)
|
|
56499
56376
|
|
|
56500
56377
|
|
|
56501
|
-
this.featureCache = new FeatureCache(features, this.genome, genomicInterval); // If track is marked "searchable"< cache features by name -- use this with caution, memory intensive
|
|
56378
|
+
this.featureCache = new FeatureCache$1(features, this.genome, genomicInterval); // If track is marked "searchable"< cache features by name -- use this with caution, memory intensive
|
|
56502
56379
|
|
|
56503
56380
|
if (this.config.searchable || this.config.searchableFields) {
|
|
56504
56381
|
this.addFeaturesToDB(features);
|
|
56505
56382
|
}
|
|
56506
56383
|
} else {
|
|
56507
|
-
this.featureCache = new FeatureCache([], genomicInterval); // Empty cache
|
|
56384
|
+
this.featureCache = new FeatureCache$1([], genomicInterval); // Empty cache
|
|
56508
56385
|
}
|
|
56509
56386
|
}
|
|
56510
56387
|
|
|
@@ -58254,7 +58131,7 @@
|
|
|
58254
58131
|
return features;
|
|
58255
58132
|
}
|
|
58256
58133
|
|
|
58257
|
-
supportsWholeGenome() {
|
|
58134
|
+
get supportsWholeGenome() {
|
|
58258
58135
|
return true;
|
|
58259
58136
|
}
|
|
58260
58137
|
|
|
@@ -58372,30 +58249,6 @@
|
|
|
58372
58249
|
}
|
|
58373
58250
|
}
|
|
58374
58251
|
|
|
58375
|
-
const pairs = [['A', 'T'], ['G', 'C'], ['Y', 'R'], ['W', 'S'], ['K', 'M'], ['D', 'H'], ['B', 'V']];
|
|
58376
|
-
const complements = new Map();
|
|
58377
|
-
|
|
58378
|
-
for (let p of pairs) {
|
|
58379
|
-
const p1 = p[0];
|
|
58380
|
-
const p2 = p[1];
|
|
58381
|
-
complements.set(p1, p2);
|
|
58382
|
-
complements.set(p2, p1);
|
|
58383
|
-
complements.set(p1.toLowerCase(), p2.toLowerCase());
|
|
58384
|
-
complements.set(p2.toLowerCase(), p1.toLowerCase());
|
|
58385
|
-
}
|
|
58386
|
-
|
|
58387
|
-
function reverseComplementSequence(sequence) {
|
|
58388
|
-
let comp = '';
|
|
58389
|
-
let idx = sequence.length;
|
|
58390
|
-
|
|
58391
|
-
while (idx-- > 0) {
|
|
58392
|
-
const base = sequence[idx];
|
|
58393
|
-
comp += complements.has(base) ? complements.get(base) : base;
|
|
58394
|
-
}
|
|
58395
|
-
|
|
58396
|
-
return comp;
|
|
58397
|
-
}
|
|
58398
|
-
|
|
58399
58252
|
const GtexUtils = {
|
|
58400
58253
|
getTissueInfo: function (datasetId, baseURL) {
|
|
58401
58254
|
datasetId = datasetId || 'gtex_v8';
|
|
@@ -58918,7 +58771,7 @@
|
|
|
58918
58771
|
return this;
|
|
58919
58772
|
}
|
|
58920
58773
|
|
|
58921
|
-
supportsWholeGenome() {
|
|
58774
|
+
get supportsWholeGenome() {
|
|
58922
58775
|
return (this.config.indexed === false || !this.config.indexURL) && this.config.supportsWholeGenome !== false;
|
|
58923
58776
|
}
|
|
58924
58777
|
|
|
@@ -59071,15 +58924,9 @@
|
|
|
59071
58924
|
for (let fd of featureData) {
|
|
59072
58925
|
data.push(fd);
|
|
59073
58926
|
|
|
59074
|
-
if (infoURL) {
|
|
59075
|
-
|
|
59076
|
-
|
|
59077
|
-
const href = url.replace("$$", feature.name);
|
|
59078
|
-
data.push({
|
|
59079
|
-
name: "Info",
|
|
59080
|
-
value: `<a target="_blank" href=${href}>${fd.value}</a>`
|
|
59081
|
-
});
|
|
59082
|
-
}
|
|
58927
|
+
if (infoURL && fd.name && fd.name.toLowerCase() === "name" && fd.value && isString$3(fd.value) && !fd.value.startsWith("<")) {
|
|
58928
|
+
const href = infoURL.replace("$$", feature.name);
|
|
58929
|
+
fd.value = `<a target=_blank href=${href}>${fd.value}</a>`;
|
|
59083
58930
|
}
|
|
59084
58931
|
} //Array.prototype.push.apply(data, featureData);
|
|
59085
58932
|
// If we have clicked over an exon number it.
|
|
@@ -59153,17 +59000,31 @@
|
|
|
59153
59000
|
}
|
|
59154
59001
|
|
|
59155
59002
|
contextMenuItemList(clickState) {
|
|
59156
|
-
|
|
59157
|
-
const features = this.clickedFeatures(clickState);
|
|
59003
|
+
const features = this.clickedFeatures(clickState);
|
|
59158
59004
|
|
|
59159
|
-
|
|
59160
|
-
|
|
59161
|
-
|
|
59005
|
+
if (features.length > 1) {
|
|
59006
|
+
features.sort((a, b) => b.end - b.start - (a.end - a.start));
|
|
59007
|
+
}
|
|
59162
59008
|
|
|
59163
|
-
|
|
59009
|
+
const f = features[0]; // The shortest clicked feature
|
|
59164
59010
|
|
|
59165
|
-
|
|
59166
|
-
|
|
59011
|
+
if (f.end - f.start <= 1000000) {
|
|
59012
|
+
const list = [{
|
|
59013
|
+
label: 'View feature sequence',
|
|
59014
|
+
click: async () => {
|
|
59015
|
+
let seq = await this.browser.genome.getSequence(f.chr, f.start, f.end);
|
|
59016
|
+
|
|
59017
|
+
if (f.strand === '-') {
|
|
59018
|
+
seq = reverseComplementSequence(seq);
|
|
59019
|
+
}
|
|
59020
|
+
|
|
59021
|
+
if (!seq) seq = "Unknown sequence";
|
|
59022
|
+
Alert.presentAlert(seq);
|
|
59023
|
+
}
|
|
59024
|
+
}];
|
|
59025
|
+
|
|
59026
|
+
if (isSecureContext() && navigator.clipboard !== undefined) {
|
|
59027
|
+
list.push({
|
|
59167
59028
|
label: 'Copy feature sequence',
|
|
59168
59029
|
click: async () => {
|
|
59169
59030
|
let seq = await this.browser.genome.getSequence(f.chr, f.start, f.end);
|
|
@@ -59172,14 +59033,21 @@
|
|
|
59172
59033
|
seq = reverseComplementSequence(seq);
|
|
59173
59034
|
}
|
|
59174
59035
|
|
|
59175
|
-
|
|
59036
|
+
try {
|
|
59037
|
+
await navigator.clipboard.writeText(seq);
|
|
59038
|
+
} catch (e) {
|
|
59039
|
+
console.error(e);
|
|
59040
|
+
Alert.presentAlert(`error copying sequence to clipboard ${e}`);
|
|
59041
|
+
}
|
|
59176
59042
|
}
|
|
59177
|
-
}
|
|
59043
|
+
});
|
|
59178
59044
|
}
|
|
59179
|
-
} // Either not a secure context (i.e. http: protocol), or feature is too long
|
|
59180
|
-
|
|
59181
59045
|
|
|
59182
|
-
|
|
59046
|
+
list.push('<hr/>');
|
|
59047
|
+
return list;
|
|
59048
|
+
} else {
|
|
59049
|
+
return undefined;
|
|
59050
|
+
}
|
|
59183
59051
|
}
|
|
59184
59052
|
|
|
59185
59053
|
description() {
|
|
@@ -59275,14 +59143,12 @@
|
|
|
59275
59143
|
this.featureType = 'numeric';
|
|
59276
59144
|
this.paintAxis = paintAxis;
|
|
59277
59145
|
const format = config.format ? config.format.toLowerCase() : config.format;
|
|
59146
|
+
this.flipAxis = config.flipAxis ? config.flipAxis : false;
|
|
59147
|
+
this.logScale = config.logScale ? config.logScale : false;
|
|
59278
59148
|
|
|
59279
59149
|
if ("bigwig" === format) {
|
|
59280
|
-
this.flipAxis = config.flipAxis ? config.flipAxis : false;
|
|
59281
|
-
this.logScale = config.logScale ? config.logScale : false;
|
|
59282
59150
|
this.featureSource = new BWSource(config, this.browser.genome);
|
|
59283
59151
|
} else if ("tdf" === format) {
|
|
59284
|
-
this.flipAxis = config.flipAxis ? config.flipAxis : false;
|
|
59285
|
-
this.logScale = config.logScale ? config.logScale : false;
|
|
59286
59152
|
this.featureSource = new TDFSource(config, this.browser.genome);
|
|
59287
59153
|
} else {
|
|
59288
59154
|
this.featureSource = FeatureSource(config, this.browser.genome);
|
|
@@ -59520,7 +59386,7 @@
|
|
|
59520
59386
|
}
|
|
59521
59387
|
}
|
|
59522
59388
|
|
|
59523
|
-
supportsWholeGenome() {
|
|
59389
|
+
get supportsWholeGenome() {
|
|
59524
59390
|
return !this.config.indexURL && this.config.supportsWholeGenome !== false;
|
|
59525
59391
|
}
|
|
59526
59392
|
/**
|
|
@@ -60059,7 +59925,7 @@
|
|
|
60059
59925
|
|
|
60060
59926
|
const sortHandler = sort => {
|
|
60061
59927
|
const viewport = clickState.viewport;
|
|
60062
|
-
const features = viewport.
|
|
59928
|
+
const features = viewport.cachedFeatures;
|
|
60063
59929
|
this.sortSamples(sort.chr, sort.start, sort.end, sort.direction, features);
|
|
60064
59930
|
};
|
|
60065
59931
|
|
|
@@ -60079,7 +59945,7 @@
|
|
|
60079
59945
|
}];
|
|
60080
59946
|
}
|
|
60081
59947
|
|
|
60082
|
-
supportsWholeGenome() {
|
|
59948
|
+
get supportsWholeGenome() {
|
|
60083
59949
|
return (this.config.indexed === false || !this.config.indexURL) && this.config.supportsWholeGenome !== false;
|
|
60084
59950
|
}
|
|
60085
59951
|
|
|
@@ -60165,10 +60031,16 @@
|
|
|
60165
60031
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
60166
60032
|
* THE SOFTWARE.
|
|
60167
60033
|
*/
|
|
60034
|
+
/**
|
|
60035
|
+
* Represents 2 or more wig tracks overlaid on a common viewport.
|
|
60036
|
+
*/
|
|
60168
60037
|
|
|
60169
60038
|
class MergedTrack extends TrackBase {
|
|
60170
60039
|
constructor(config, browser) {
|
|
60171
60040
|
super(config, browser);
|
|
60041
|
+
this.type = "merged";
|
|
60042
|
+
this.featureType = 'numeric';
|
|
60043
|
+
this.paintAxis = paintAxis;
|
|
60172
60044
|
}
|
|
60173
60045
|
|
|
60174
60046
|
init(config) {
|
|
@@ -60179,21 +60051,6 @@
|
|
|
60179
60051
|
super.init(config);
|
|
60180
60052
|
}
|
|
60181
60053
|
|
|
60182
|
-
get height() {
|
|
60183
|
-
return this._height;
|
|
60184
|
-
}
|
|
60185
|
-
|
|
60186
|
-
set height(h) {
|
|
60187
|
-
this._height = h;
|
|
60188
|
-
|
|
60189
|
-
if (this.tracks) {
|
|
60190
|
-
for (let t of this.tracks) {
|
|
60191
|
-
t.height = h;
|
|
60192
|
-
t.config.height = h;
|
|
60193
|
-
}
|
|
60194
|
-
}
|
|
60195
|
-
}
|
|
60196
|
-
|
|
60197
60054
|
async postInit() {
|
|
60198
60055
|
this.tracks = [];
|
|
60199
60056
|
const p = [];
|
|
@@ -60215,45 +60072,83 @@
|
|
|
60215
60072
|
}
|
|
60216
60073
|
}
|
|
60217
60074
|
|
|
60218
|
-
this.
|
|
60075
|
+
this.flipAxis = this.config.flipAxis ? this.config.flipAxis : false;
|
|
60076
|
+
this.logScale = this.config.logScale ? this.config.logScale : false;
|
|
60077
|
+
this.autoscale = this.config.autoscale || this.config.max === undefined;
|
|
60078
|
+
|
|
60079
|
+
if (!this.autoscale) {
|
|
60080
|
+
this.dataRange = {
|
|
60081
|
+
min: this.config.min || 0,
|
|
60082
|
+
max: this.config.max
|
|
60083
|
+
};
|
|
60084
|
+
}
|
|
60085
|
+
|
|
60086
|
+
for (let t of this.tracks) {
|
|
60087
|
+
t.autoscale = false;
|
|
60088
|
+
t.dataRange = this.dataRange;
|
|
60089
|
+
}
|
|
60090
|
+
|
|
60091
|
+
this.height = this.config.height || 50;
|
|
60219
60092
|
return Promise.all(p);
|
|
60220
60093
|
}
|
|
60221
60094
|
|
|
60095
|
+
get height() {
|
|
60096
|
+
return this._height;
|
|
60097
|
+
}
|
|
60098
|
+
|
|
60099
|
+
set height(h) {
|
|
60100
|
+
this._height = h;
|
|
60101
|
+
|
|
60102
|
+
if (this.tracks) {
|
|
60103
|
+
for (let t of this.tracks) {
|
|
60104
|
+
t.height = h;
|
|
60105
|
+
t.config.height = h;
|
|
60106
|
+
}
|
|
60107
|
+
}
|
|
60108
|
+
}
|
|
60109
|
+
|
|
60110
|
+
menuItemList() {
|
|
60111
|
+
let items = [];
|
|
60112
|
+
|
|
60113
|
+
if (this.flipAxis !== undefined) {
|
|
60114
|
+
items.push({
|
|
60115
|
+
label: "Flip y-axis",
|
|
60116
|
+
click: () => {
|
|
60117
|
+
this.flipAxis = !this.flipAxis;
|
|
60118
|
+
this.trackView.repaintViews();
|
|
60119
|
+
}
|
|
60120
|
+
});
|
|
60121
|
+
}
|
|
60122
|
+
|
|
60123
|
+
items = items.concat(MenuUtils.numericDataMenuItems(this.trackView));
|
|
60124
|
+
return items;
|
|
60125
|
+
}
|
|
60126
|
+
|
|
60222
60127
|
async getFeatures(chr, bpStart, bpEnd, bpPerPixel) {
|
|
60223
60128
|
const promises = this.tracks.map(t => t.getFeatures(chr, bpStart, bpEnd, bpPerPixel));
|
|
60224
60129
|
return Promise.all(promises);
|
|
60225
60130
|
}
|
|
60226
60131
|
|
|
60227
60132
|
draw(options) {
|
|
60228
|
-
|
|
60229
|
-
mergedFeatures = options.features; // Array of feature arrays, 1 for each track
|
|
60133
|
+
const mergedFeatures = options.features; // Array of feature arrays, 1 for each track
|
|
60230
60134
|
|
|
60231
|
-
|
|
60135
|
+
if (this.autoscale) {
|
|
60136
|
+
this.dataRange = autoscale(options.referenceFrame.chr, mergedFeatures);
|
|
60137
|
+
}
|
|
60232
60138
|
|
|
60233
|
-
for (i = 0, len = this.tracks.length; i < len; i++) {
|
|
60234
|
-
trackOptions = Object.assign({}, options);
|
|
60139
|
+
for (let i = 0, len = this.tracks.length; i < len; i++) {
|
|
60140
|
+
const trackOptions = Object.assign({}, options);
|
|
60235
60141
|
trackOptions.features = mergedFeatures[i];
|
|
60236
|
-
this.tracks[i].dataRange = dataRange;
|
|
60142
|
+
this.tracks[i].dataRange = this.dataRange;
|
|
60143
|
+
this.tracks[i].flipAxis = this.flipAxis;
|
|
60144
|
+
this.tracks[i].logScale = this.logScale;
|
|
60145
|
+
this.tracks[i].graphType = this.graphType;
|
|
60237
60146
|
this.tracks[i].draw(trackOptions);
|
|
60238
60147
|
}
|
|
60239
60148
|
}
|
|
60240
60149
|
|
|
60241
|
-
paintAxis(ctx, pixelWidth, pixelHeight) {
|
|
60242
|
-
var i, len, autoscale, track;
|
|
60243
|
-
autoscale = true; // Hardcoded for now
|
|
60244
|
-
|
|
60245
|
-
for (i = 0, len = this.tracks.length; i < len; i++) {
|
|
60246
|
-
track = this.tracks[i];
|
|
60247
|
-
|
|
60248
|
-
if (typeof track.paintAxis === 'function') {
|
|
60249
|
-
track.paintAxis(ctx, pixelWidth, pixelHeight);
|
|
60250
|
-
if (autoscale) break;
|
|
60251
|
-
}
|
|
60252
|
-
}
|
|
60253
|
-
}
|
|
60254
|
-
|
|
60255
60150
|
popupData(clickState, features) {
|
|
60256
|
-
const featuresArray = features || clickState.viewport.
|
|
60151
|
+
const featuresArray = features || clickState.viewport.cachedFeatures;
|
|
60257
60152
|
|
|
60258
60153
|
if (featuresArray && featuresArray.length === this.tracks.length) {
|
|
60259
60154
|
// Array of feature arrays, 1 for each track
|
|
@@ -60270,40 +60165,24 @@
|
|
|
60270
60165
|
}
|
|
60271
60166
|
}
|
|
60272
60167
|
|
|
60273
|
-
supportsWholeGenome() {
|
|
60274
|
-
|
|
60275
|
-
return b;
|
|
60168
|
+
get supportsWholeGenome() {
|
|
60169
|
+
return this.tracks.every(track => track.supportsWholeGenome());
|
|
60276
60170
|
}
|
|
60277
60171
|
|
|
60278
60172
|
}
|
|
60279
60173
|
|
|
60280
60174
|
function autoscale(chr, featureArrays) {
|
|
60281
|
-
|
|
60282
|
-
|
|
60283
|
-
// if (chr === 'all') {
|
|
60284
|
-
// allValues = [];
|
|
60285
|
-
// featureArrays.forEach(function (features) {
|
|
60286
|
-
// features.forEach(function (f) {
|
|
60287
|
-
// if (!Number.isNaN(f.value)) {
|
|
60288
|
-
// allValues.push(f.value);
|
|
60289
|
-
// }
|
|
60290
|
-
// });
|
|
60291
|
-
// });
|
|
60292
|
-
//
|
|
60293
|
-
// min = Math.min(0, IGVMath.percentile(allValues, .1));
|
|
60294
|
-
// max = IGVMath.percentile(allValues, 99.9);
|
|
60295
|
-
//
|
|
60296
|
-
// }
|
|
60297
|
-
// else {
|
|
60175
|
+
let min = 0;
|
|
60176
|
+
let max = -Number.MAX_VALUE;
|
|
60298
60177
|
|
|
60299
|
-
|
|
60300
|
-
|
|
60178
|
+
for (let features of featureArrays) {
|
|
60179
|
+
for (let f of features) {
|
|
60301
60180
|
if (typeof f.value !== 'undefined' && !Number.isNaN(f.value)) {
|
|
60302
60181
|
min = Math.min(min, f.value);
|
|
60303
60182
|
max = Math.max(max, f.value);
|
|
60304
60183
|
}
|
|
60305
|
-
}
|
|
60306
|
-
}
|
|
60184
|
+
}
|
|
60185
|
+
}
|
|
60307
60186
|
|
|
60308
60187
|
return {
|
|
60309
60188
|
min: min,
|
|
@@ -60424,7 +60303,7 @@
|
|
|
60424
60303
|
return this;
|
|
60425
60304
|
}
|
|
60426
60305
|
|
|
60427
|
-
supportsWholeGenome() {
|
|
60306
|
+
get supportsWholeGenome() {
|
|
60428
60307
|
return true;
|
|
60429
60308
|
}
|
|
60430
60309
|
|
|
@@ -60845,7 +60724,7 @@
|
|
|
60845
60724
|
items = items.concat(MenuUtils.numericDataMenuItems(this.trackView));
|
|
60846
60725
|
}
|
|
60847
60726
|
|
|
60848
|
-
if (this.browser.circularView
|
|
60727
|
+
if (this.browser.circularView) {
|
|
60849
60728
|
items.push('<hr/>');
|
|
60850
60729
|
items.push({
|
|
60851
60730
|
label: 'Add interactions to circular view',
|
|
@@ -60862,7 +60741,7 @@
|
|
|
60862
60741
|
|
|
60863
60742
|
contextMenuItemList(clickState) {
|
|
60864
60743
|
// Experimental JBrowse feature
|
|
60865
|
-
if (this.browser.circularView
|
|
60744
|
+
if (this.browser.circularView) {
|
|
60866
60745
|
const viewport = clickState.viewport;
|
|
60867
60746
|
const list = [];
|
|
60868
60747
|
list.push({
|
|
@@ -60887,20 +60766,20 @@
|
|
|
60887
60766
|
const cachedFeatures = "all" === refFrame.chr ? this.featureSource.getAllFeatures() : this.featureSource.featureCache.queryFeatures(refFrame.chr, refFrame.start, refFrame.end); // inView features are simply features that have been drawn, i.e. have a drawState
|
|
60888
60767
|
|
|
60889
60768
|
const inView = cachedFeatures.filter(f => f.drawState);
|
|
60890
|
-
if (inView.length === 0)
|
|
60891
|
-
|
|
60892
|
-
|
|
60893
|
-
|
|
60894
|
-
|
|
60895
|
-
const
|
|
60896
|
-
|
|
60897
|
-
|
|
60898
|
-
|
|
60899
|
-
this.
|
|
60900
|
-
|
|
60901
|
-
|
|
60902
|
-
|
|
60903
|
-
})
|
|
60769
|
+
if (inView.length === 0) return;
|
|
60770
|
+
const chords = makeBedPEChords(inView);
|
|
60771
|
+
sendChords(chords, this, refFrame, 0.5); //
|
|
60772
|
+
//
|
|
60773
|
+
// // for filtered set, distinguishing the chromosomes is more critical than tracks
|
|
60774
|
+
// const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? this.color : getChrColor(refFrame.chr), 0.5)
|
|
60775
|
+
// const trackColor = IGVColor.addAlpha(this.color, 0.5)
|
|
60776
|
+
//
|
|
60777
|
+
// // name the chord set to include locus and filtering information
|
|
60778
|
+
// const encodedName = this.name.replaceAll(' ', '%20')
|
|
60779
|
+
// const chordSetName = "all" === refFrame.chr ?
|
|
60780
|
+
// encodedName :
|
|
60781
|
+
// `${encodedName} (${refFrame.chr}:${refFrame.start}-${refFrame.end} ; range:${this.dataRange.min}-${this.dataRange.max})`
|
|
60782
|
+
// this.browser.circularView.addChords(chords, {track: chordSetName, color: chordSetColor, trackColor: trackColor})
|
|
60904
60783
|
}
|
|
60905
60784
|
|
|
60906
60785
|
doAutoscale(features) {
|
|
@@ -60987,7 +60866,7 @@
|
|
|
60987
60866
|
clickedFeatures(clickState, features) {
|
|
60988
60867
|
// We use the cached features rather than method to avoid async load. If the
|
|
60989
60868
|
// feature is not already loaded this won't work, but the user wouldn't be mousing over it either.
|
|
60990
|
-
const featureList = features || clickState.viewport.
|
|
60869
|
+
const featureList = features || clickState.viewport.cachedFeatures;
|
|
60991
60870
|
const candidates = [];
|
|
60992
60871
|
|
|
60993
60872
|
if (featureList) {
|
|
@@ -61358,7 +61237,7 @@
|
|
|
61358
61237
|
return this;
|
|
61359
61238
|
}
|
|
61360
61239
|
|
|
61361
|
-
supportsWholeGenome() {
|
|
61240
|
+
get supportsWholeGenome() {
|
|
61362
61241
|
return this.config.indexed === false || this.config.supportsWholeGenome === true;
|
|
61363
61242
|
}
|
|
61364
61243
|
|
|
@@ -61573,7 +61452,7 @@
|
|
|
61573
61452
|
variantColor = "gray";
|
|
61574
61453
|
}
|
|
61575
61454
|
} else if (this._color) {
|
|
61576
|
-
variantColor =
|
|
61455
|
+
variantColor = this.color;
|
|
61577
61456
|
} else if ("NONVARIANT" === v.type) {
|
|
61578
61457
|
variantColor = this.nonRefColor;
|
|
61579
61458
|
} else if ("MIXED" === v.type) {
|
|
@@ -61585,6 +61464,10 @@
|
|
|
61585
61464
|
return variantColor;
|
|
61586
61465
|
}
|
|
61587
61466
|
|
|
61467
|
+
get color() {
|
|
61468
|
+
return this._color ? typeof this._color === "function" ? this._color(v) : this._color : this.defaultColor;
|
|
61469
|
+
}
|
|
61470
|
+
|
|
61588
61471
|
clickedFeatures(clickState, features) {
|
|
61589
61472
|
let featureList = super.clickedFeatures(clickState, features);
|
|
61590
61473
|
const vGap = this.displayMode === 'EXPANDED' ? this.expandedVGap : this.squishedVGap;
|
|
@@ -61845,29 +61728,15 @@
|
|
|
61845
61728
|
} // Experimental JBrowse circular view integration
|
|
61846
61729
|
|
|
61847
61730
|
|
|
61848
|
-
if (this.browser.circularView
|
|
61731
|
+
if (this.browser.circularView) {
|
|
61849
61732
|
menuItems.push('<hr>');
|
|
61850
61733
|
menuItems.push({
|
|
61851
61734
|
label: 'Add SVs to circular view',
|
|
61852
61735
|
click: () => {
|
|
61853
|
-
const inView = [];
|
|
61854
61736
|
|
|
61855
61737
|
for (let viewport of this.trackView.viewports) {
|
|
61856
|
-
|
|
61857
|
-
|
|
61858
|
-
for (let f of viewport.getCachedFeatures()) {
|
|
61859
|
-
if (f.end >= refFrame.start && f.start <= refFrame.end) {
|
|
61860
|
-
inView.push(f);
|
|
61861
|
-
}
|
|
61862
|
-
}
|
|
61738
|
+
this.sendChordsForViewport(viewport);
|
|
61863
61739
|
}
|
|
61864
|
-
|
|
61865
|
-
const chords = makeVCFChords(inView);
|
|
61866
|
-
const color = IGVColor.addAlpha(this._color || this.defaultColor, 0.5);
|
|
61867
|
-
this.browser.circularView.addChords(chords, {
|
|
61868
|
-
track: this.name,
|
|
61869
|
-
color: color
|
|
61870
|
-
});
|
|
61871
61740
|
}
|
|
61872
61741
|
});
|
|
61873
61742
|
}
|
|
@@ -61877,26 +61746,26 @@
|
|
|
61877
61746
|
|
|
61878
61747
|
contextMenuItemList(clickState) {
|
|
61879
61748
|
// Experimental JBrowse circular view integration
|
|
61880
|
-
if (this.browser.circularView
|
|
61749
|
+
if (this.browser.circularView) {
|
|
61881
61750
|
const viewport = clickState.viewport;
|
|
61882
61751
|
const list = [];
|
|
61883
61752
|
list.push({
|
|
61884
61753
|
label: 'Add SVs to Circular View',
|
|
61885
61754
|
click: () => {
|
|
61886
|
-
|
|
61887
|
-
const inView = "all" === refFrame.chr ? this.featureSource.getAllFeatures() : this.featureSource.featureCache.queryFeatures(refFrame.chr, refFrame.start, refFrame.end);
|
|
61888
|
-
const chords = makeVCFChords(inView);
|
|
61889
|
-
const color = IGVColor.addAlpha(this._color || this.defaultColor, 0.5);
|
|
61890
|
-
this.browser.circularView.addChords(chords, {
|
|
61891
|
-
track: this.name,
|
|
61892
|
-
color: color
|
|
61893
|
-
});
|
|
61755
|
+
this.sendChordsForViewport(viewport);
|
|
61894
61756
|
}
|
|
61895
61757
|
});
|
|
61896
61758
|
list.push('<hr/>');
|
|
61897
61759
|
return list;
|
|
61898
61760
|
}
|
|
61899
61761
|
}
|
|
61762
|
+
|
|
61763
|
+
sendChordsForViewport(viewport) {
|
|
61764
|
+
const refFrame = viewport.referenceFrame;
|
|
61765
|
+
const inView = "all" === refFrame.chr ? this.featureSource.getAllFeatures() : this.featureSource.featureCache.queryFeatures(refFrame.chr, refFrame.start, refFrame.end);
|
|
61766
|
+
const chords = makeVCFChords(inView);
|
|
61767
|
+
sendChords(chords, this, refFrame, 0.5);
|
|
61768
|
+
}
|
|
61900
61769
|
/**
|
|
61901
61770
|
* Create a "color by" checkbox menu item, optionally initially checked
|
|
61902
61771
|
* @param menuItem
|
|
@@ -62199,7 +62068,7 @@
|
|
|
62199
62068
|
|
|
62200
62069
|
|
|
62201
62070
|
popupData(clickState) {
|
|
62202
|
-
let features = clickState.viewport.
|
|
62071
|
+
let features = clickState.viewport.cachedFeatures;
|
|
62203
62072
|
if (!features || features.length === 0) return [];
|
|
62204
62073
|
const tolerance = 3;
|
|
62205
62074
|
const tissue = this.name;
|
|
@@ -62407,7 +62276,7 @@
|
|
|
62407
62276
|
return this;
|
|
62408
62277
|
}
|
|
62409
62278
|
|
|
62410
|
-
supportsWholeGenome() {
|
|
62279
|
+
get supportsWholeGenome() {
|
|
62411
62280
|
return true;
|
|
62412
62281
|
}
|
|
62413
62282
|
|
|
@@ -62545,7 +62414,7 @@
|
|
|
62545
62414
|
popupData(clickState) {
|
|
62546
62415
|
let data = [];
|
|
62547
62416
|
const track = clickState.viewport.trackView.track;
|
|
62548
|
-
const features = clickState.viewport.
|
|
62417
|
+
const features = clickState.viewport.cachedFeatures;
|
|
62549
62418
|
|
|
62550
62419
|
if (features) {
|
|
62551
62420
|
let count = 0;
|
|
@@ -62973,7 +62842,7 @@
|
|
|
62973
62842
|
return items;
|
|
62974
62843
|
}
|
|
62975
62844
|
|
|
62976
|
-
supportsWholeGenome() {
|
|
62845
|
+
get supportsWholeGenome() {
|
|
62977
62846
|
return false;
|
|
62978
62847
|
}
|
|
62979
62848
|
|
|
@@ -63234,7 +63103,7 @@
|
|
|
63234
63103
|
if (!this.featureCache) {
|
|
63235
63104
|
const options = buildOptions(this.config);
|
|
63236
63105
|
const data = await igvxhr.loadString(this.config.url, options);
|
|
63237
|
-
this.featureCache = new FeatureCache(parseBP(data), genome);
|
|
63106
|
+
this.featureCache = new FeatureCache$1(parseBP(data), genome);
|
|
63238
63107
|
return this.featureCache.queryFeatures(chr, start, end);
|
|
63239
63108
|
} else {
|
|
63240
63109
|
return this.featureCache.queryFeatures(chr, start, end);
|
|
@@ -63334,12 +63203,15 @@
|
|
|
63334
63203
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
63335
63204
|
* THE SOFTWARE.
|
|
63336
63205
|
*/
|
|
63206
|
+
/**
|
|
63207
|
+
* Class represents an ideogram of a chromsome cytobands. It is used for the header of a track panel.
|
|
63208
|
+
*
|
|
63209
|
+
*/
|
|
63337
63210
|
|
|
63338
63211
|
class IdeogramTrack {
|
|
63339
63212
|
constructor(browser) {
|
|
63340
63213
|
this.browser = browser;
|
|
63341
63214
|
this.type = 'ideogram';
|
|
63342
|
-
this.id = this.type;
|
|
63343
63215
|
this.height = 16;
|
|
63344
63216
|
this.order = Number.MIN_SAFE_INTEGER;
|
|
63345
63217
|
this.disableButtons = true;
|
|
@@ -63599,7 +63471,7 @@
|
|
|
63599
63471
|
return this;
|
|
63600
63472
|
}
|
|
63601
63473
|
|
|
63602
|
-
supportsWholeGenome() {
|
|
63474
|
+
get supportsWholeGenome() {
|
|
63603
63475
|
return false;
|
|
63604
63476
|
}
|
|
63605
63477
|
|
|
@@ -64477,6 +64349,15 @@
|
|
|
64477
64349
|
this.id = guid$2();
|
|
64478
64350
|
}
|
|
64479
64351
|
|
|
64352
|
+
extend(locus) {
|
|
64353
|
+
const newStart = Math.min(locus.start, this.start);
|
|
64354
|
+
const newEnd = Math.max(locus.end, this.end);
|
|
64355
|
+
const ratio = (newEnd - newStart) / (this.end - this.start);
|
|
64356
|
+
this.start = newStart;
|
|
64357
|
+
this.end = newEnd;
|
|
64358
|
+
this.bpPerPixel *= ratio;
|
|
64359
|
+
}
|
|
64360
|
+
|
|
64480
64361
|
calculateEnd(pixels) {
|
|
64481
64362
|
return this.start + this.bpPerPixel * pixels;
|
|
64482
64363
|
}
|
|
@@ -64561,7 +64442,7 @@
|
|
|
64561
64442
|
const viewChanged = start !== this.start || bpPerPixel !== this.bpPerPixel;
|
|
64562
64443
|
|
|
64563
64444
|
if (viewChanged) {
|
|
64564
|
-
await browser.updateViews(
|
|
64445
|
+
await browser.updateViews(true);
|
|
64565
64446
|
}
|
|
64566
64447
|
}
|
|
64567
64448
|
|
|
@@ -64631,23 +64512,6 @@
|
|
|
64631
64512
|
});
|
|
64632
64513
|
}
|
|
64633
64514
|
|
|
64634
|
-
function adjustReferenceFrame(scaleFactor, referenceFrame, viewportWidth, alignmentStart, alignmentLength) {
|
|
64635
|
-
referenceFrame.bpPerPixel *= scaleFactor;
|
|
64636
|
-
const alignmentEE = alignmentStart + alignmentLength;
|
|
64637
|
-
const alignmentCC = (alignmentStart + alignmentEE) / 2;
|
|
64638
|
-
referenceFrame.start = alignmentCC - referenceFrame.bpPerPixel * (viewportWidth / 2);
|
|
64639
|
-
referenceFrame.end = referenceFrame.start + referenceFrame.bpPerPixel * viewportWidth;
|
|
64640
|
-
referenceFrame.locusSearchString = referenceFrame.getLocusString();
|
|
64641
|
-
}
|
|
64642
|
-
|
|
64643
|
-
function createReferenceFrameWithAlignment(genome, chromosomeName, bpp, viewportWidth, alignmentStart, alignmentLength) {
|
|
64644
|
-
const alignmentEE = alignmentStart + alignmentLength;
|
|
64645
|
-
const alignmentCC = (alignmentStart + alignmentEE) / 2;
|
|
64646
|
-
const ss = alignmentCC - bpp * (viewportWidth / 2);
|
|
64647
|
-
const ee = ss + bpp * viewportWidth;
|
|
64648
|
-
return new ReferenceFrame(genome, chromosomeName, ss, ee, bpp);
|
|
64649
|
-
}
|
|
64650
|
-
|
|
64651
64515
|
const defaultNucleotideColors = {
|
|
64652
64516
|
"A": "rgb( 0, 200, 0)",
|
|
64653
64517
|
"C": "rgb( 0,0,200)",
|
|
@@ -65694,6 +65558,65 @@
|
|
|
65694
65558
|
button.addEventListener('click', () => browser.saveSVGtoFile({}));
|
|
65695
65559
|
};
|
|
65696
65560
|
|
|
65561
|
+
const viewportColumnManager = {
|
|
65562
|
+
createColumns: (columnContainer, count) => {
|
|
65563
|
+
for (let i = 0; i < count; i++) {
|
|
65564
|
+
if (0 === i) {
|
|
65565
|
+
createColumn(columnContainer, 'igv-column');
|
|
65566
|
+
} else {
|
|
65567
|
+
columnContainer.appendChild(div$1({
|
|
65568
|
+
class: 'igv-column-shim'
|
|
65569
|
+
}));
|
|
65570
|
+
createColumn(columnContainer, 'igv-column');
|
|
65571
|
+
}
|
|
65572
|
+
}
|
|
65573
|
+
},
|
|
65574
|
+
removeColumnAtIndex: (i, column) => {
|
|
65575
|
+
const shim = 0 === i ? column.nextElementSibling : column.previousElementSibling;
|
|
65576
|
+
column.remove();
|
|
65577
|
+
shim.remove();
|
|
65578
|
+
},
|
|
65579
|
+
insertAfter: referenceElement => {
|
|
65580
|
+
const shim = div$1({
|
|
65581
|
+
class: 'igv-column-shim'
|
|
65582
|
+
});
|
|
65583
|
+
insertElementAfter(shim, referenceElement);
|
|
65584
|
+
const column = div$1({
|
|
65585
|
+
class: 'igv-column'
|
|
65586
|
+
});
|
|
65587
|
+
insertElementAfter(column, shim);
|
|
65588
|
+
return column;
|
|
65589
|
+
},
|
|
65590
|
+
insertBefore: (referenceElement, count) => {
|
|
65591
|
+
for (let i = 0; i < count; i++) {
|
|
65592
|
+
const column = div$1({
|
|
65593
|
+
class: 'igv-column'
|
|
65594
|
+
});
|
|
65595
|
+
insertElementBefore(column, referenceElement);
|
|
65596
|
+
|
|
65597
|
+
if (count > 1 && i > 0) {
|
|
65598
|
+
const columnShim = div$1({
|
|
65599
|
+
class: 'igv-column-shim'
|
|
65600
|
+
});
|
|
65601
|
+
insertElementBefore(columnShim, column);
|
|
65602
|
+
}
|
|
65603
|
+
}
|
|
65604
|
+
},
|
|
65605
|
+
indexOfColumn: (columnContainer, column) => {
|
|
65606
|
+
const allColumns = columnContainer.querySelectorAll('.igv-column');
|
|
65607
|
+
|
|
65608
|
+
for (let i = 0; i < allColumns.length; i++) {
|
|
65609
|
+
const c = allColumns[i];
|
|
65610
|
+
|
|
65611
|
+
if (c === column) {
|
|
65612
|
+
return i;
|
|
65613
|
+
}
|
|
65614
|
+
}
|
|
65615
|
+
|
|
65616
|
+
return undefined;
|
|
65617
|
+
}
|
|
65618
|
+
};
|
|
65619
|
+
|
|
65697
65620
|
/*
|
|
65698
65621
|
* The MIT License (MIT)
|
|
65699
65622
|
*
|
|
@@ -65944,7 +65867,7 @@
|
|
|
65944
65867
|
}
|
|
65945
65868
|
}
|
|
65946
65869
|
|
|
65947
|
-
supportsWholeGenome() {
|
|
65870
|
+
get supportsWholeGenome() {
|
|
65948
65871
|
return true;
|
|
65949
65872
|
}
|
|
65950
65873
|
|
|
@@ -66476,7 +66399,9 @@
|
|
|
66476
66399
|
// deferred because ideogram and ruler are treated as "tracks", and tracks require a reference frame
|
|
66477
66400
|
|
|
66478
66401
|
if (false !== session.showIdeogram) {
|
|
66479
|
-
|
|
66402
|
+
const ideogramTrack = new IdeogramTrack(this);
|
|
66403
|
+
ideogramTrack.id = 'ideogram';
|
|
66404
|
+
this.trackViews.push(new TrackView(this, this.columnContainer, ideogramTrack));
|
|
66480
66405
|
}
|
|
66481
66406
|
|
|
66482
66407
|
if (false !== session.showRuler) {
|
|
@@ -66526,7 +66451,12 @@
|
|
|
66526
66451
|
}
|
|
66527
66452
|
}
|
|
66528
66453
|
|
|
66529
|
-
await this.loadTrackList(trackConfigurations);
|
|
66454
|
+
await this.loadTrackList(trackConfigurations); // The ruler and ideogram tracks are not explicitly loaded, but needs updated nonetheless.
|
|
66455
|
+
|
|
66456
|
+
for (let rtv of this.trackViews.filter(tv => tv.track.type === 'ruler' || tv.track.type === 'ideogram')) {
|
|
66457
|
+
rtv.updateViews();
|
|
66458
|
+
}
|
|
66459
|
+
|
|
66530
66460
|
this.updateUIWithReferenceFrameList();
|
|
66531
66461
|
}
|
|
66532
66462
|
|
|
@@ -66691,26 +66621,22 @@
|
|
|
66691
66621
|
}
|
|
66692
66622
|
|
|
66693
66623
|
async loadTrackList(configList) {
|
|
66694
|
-
|
|
66695
|
-
const promises = [];
|
|
66696
|
-
|
|
66697
|
-
for (let config of configList) {
|
|
66698
|
-
promises.push(this.loadTrack(config, false));
|
|
66699
|
-
}
|
|
66624
|
+
const promises = [];
|
|
66700
66625
|
|
|
66701
|
-
|
|
66702
|
-
|
|
66703
|
-
|
|
66704
|
-
});
|
|
66626
|
+
for (let config of configList) {
|
|
66627
|
+
promises.push(this.loadTrack(config));
|
|
66628
|
+
}
|
|
66705
66629
|
|
|
66706
|
-
|
|
66707
|
-
|
|
66708
|
-
|
|
66630
|
+
const loadedTracks = await Promise.all(promises);
|
|
66631
|
+
const groupAutoscaleViews = this.trackViews.filter(function (trackView) {
|
|
66632
|
+
return trackView.track.autoscaleGroup;
|
|
66633
|
+
});
|
|
66709
66634
|
|
|
66710
|
-
|
|
66711
|
-
|
|
66712
|
-
await this.resize();
|
|
66635
|
+
if (groupAutoscaleViews.length > 0) {
|
|
66636
|
+
this.updateViews();
|
|
66713
66637
|
}
|
|
66638
|
+
|
|
66639
|
+
return loadedTracks;
|
|
66714
66640
|
}
|
|
66715
66641
|
|
|
66716
66642
|
async loadROI(config) {
|
|
@@ -66724,7 +66650,9 @@
|
|
|
66724
66650
|
}
|
|
66725
66651
|
} else {
|
|
66726
66652
|
this.roi.push(new ROI(config, this.genome));
|
|
66727
|
-
}
|
|
66653
|
+
} // Force reload all views (force = true) to insure ROI features are loaded. Wasteful but this function is
|
|
66654
|
+
// rarely called.
|
|
66655
|
+
|
|
66728
66656
|
|
|
66729
66657
|
await this.updateViews(true);
|
|
66730
66658
|
}
|
|
@@ -66738,7 +66666,7 @@
|
|
|
66738
66666
|
}
|
|
66739
66667
|
|
|
66740
66668
|
for (let tv of this.trackViews) {
|
|
66741
|
-
tv.
|
|
66669
|
+
tv.repaintViews();
|
|
66742
66670
|
}
|
|
66743
66671
|
}
|
|
66744
66672
|
|
|
@@ -66746,7 +66674,7 @@
|
|
|
66746
66674
|
this.roi = [];
|
|
66747
66675
|
|
|
66748
66676
|
for (let tv of this.trackViews) {
|
|
66749
|
-
tv.
|
|
66677
|
+
tv.repaintViews();
|
|
66750
66678
|
}
|
|
66751
66679
|
}
|
|
66752
66680
|
|
|
@@ -66762,25 +66690,13 @@
|
|
|
66762
66690
|
/**
|
|
66763
66691
|
* Return a promise to load a track.
|
|
66764
66692
|
*
|
|
66765
|
-
* Each track is associated with the following DOM elements
|
|
66766
|
-
*
|
|
66767
|
-
* leftHandGutter - div on the left for track controls and legend
|
|
66768
|
-
* contentDiv - a div element wrapping all the track content. Height can be > viewportDiv height
|
|
66769
|
-
* viewportDiv - a div element through which the track is viewed. This might have a vertical scrollbar
|
|
66770
|
-
* canvas - canvas element upon which the track is drawn. Child of contentDiv
|
|
66771
|
-
*
|
|
66772
|
-
* The width of all elements should be equal. Height of the viewportDiv is controlled by the user, but never
|
|
66773
|
-
* greater than the contentDiv height. Height of contentDiv and canvas are equal, and governed by the data
|
|
66774
|
-
* loaded.
|
|
66775
|
-
*
|
|
66776
|
-
*
|
|
66777
66693
|
* @param config
|
|
66778
66694
|
* @param doResize - undefined by default
|
|
66779
66695
|
* @returns {*}
|
|
66780
66696
|
*/
|
|
66781
66697
|
|
|
66782
66698
|
|
|
66783
|
-
async loadTrack(config
|
|
66699
|
+
async loadTrack(config) {
|
|
66784
66700
|
// config might be json
|
|
66785
66701
|
if (isString$3(config)) {
|
|
66786
66702
|
config = JSON.parse(config);
|
|
@@ -66845,11 +66761,6 @@
|
|
|
66845
66761
|
|
|
66846
66762
|
msg += ": " + config.url;
|
|
66847
66763
|
Alert.presentAlert(new Error(msg), undefined);
|
|
66848
|
-
} finally {
|
|
66849
|
-
// TODO: If loadTrack() is called individually - not via loadTrackList() - call this.resize()
|
|
66850
|
-
if (false === doResize) ; else {
|
|
66851
|
-
await this.resize();
|
|
66852
|
-
}
|
|
66853
66764
|
}
|
|
66854
66765
|
}
|
|
66855
66766
|
/**
|
|
@@ -67082,43 +66993,11 @@
|
|
|
67082
66993
|
this.navbarManager.navbarDidResize(this.$navigation.width(), isWGV);
|
|
67083
66994
|
}
|
|
67084
66995
|
|
|
67085
|
-
|
|
67086
|
-
|
|
67087
|
-
|
|
67088
|
-
async resize() {
|
|
67089
|
-
const viewportWidth = this.calculateViewportWidth(this.referenceFrameList.length);
|
|
67090
|
-
|
|
67091
|
-
for (let referenceFrame of this.referenceFrameList) {
|
|
67092
|
-
const index = this.referenceFrameList.indexOf(referenceFrame);
|
|
67093
|
-
const {
|
|
67094
|
-
chr,
|
|
67095
|
-
genome
|
|
67096
|
-
} = referenceFrame;
|
|
67097
|
-
const {
|
|
67098
|
-
bpLength
|
|
67099
|
-
} = genome.getChromosome(referenceFrame.chr);
|
|
67100
|
-
const viewportWidthBP = referenceFrame.toBP(viewportWidth); // viewportWidthBP > bpLength occurs when locus is full chromosome and user widens browser
|
|
67101
|
-
|
|
67102
|
-
if (GenomeUtils.isWholeGenomeView(chr) || viewportWidthBP > bpLength) {
|
|
67103
|
-
// console.log(`${ Date.now() } Recalc referenceFrame(${ index }) bpp. viewport ${ StringUtils.numberFormatter(viewportWidthBP) } > ${ StringUtils.numberFormatter(bpLength) }.`)
|
|
67104
|
-
referenceFrame.bpPerPixel = bpLength / viewportWidth;
|
|
67105
|
-
} else {
|
|
67106
|
-
// console.log(`${ Date.now() } Recalc referenceFrame(${ index }) end.`)
|
|
67107
|
-
referenceFrame.end = referenceFrame.start + referenceFrame.toBP(viewportWidth);
|
|
67108
|
-
}
|
|
67109
|
-
|
|
67110
|
-
for (let {
|
|
67111
|
-
viewports
|
|
67112
|
-
} of this.trackViews) {
|
|
67113
|
-
viewports[index].setWidth(viewportWidth);
|
|
67114
|
-
}
|
|
67115
|
-
}
|
|
67116
|
-
|
|
67117
|
-
await this.updateViews(true);
|
|
67118
|
-
this.updateUIWithReferenceFrameList();
|
|
66996
|
+
resize.call(this);
|
|
66997
|
+
await this.updateViews();
|
|
67119
66998
|
}
|
|
67120
66999
|
|
|
67121
|
-
async updateViews(
|
|
67000
|
+
async updateViews() {
|
|
67122
67001
|
const trackViews = this.trackViews;
|
|
67123
67002
|
this.updateLocusSearchWidget();
|
|
67124
67003
|
|
|
@@ -67129,7 +67008,7 @@
|
|
|
67129
67008
|
|
|
67130
67009
|
if (this.dragObject) {
|
|
67131
67010
|
for (let trackView of trackViews) {
|
|
67132
|
-
await trackView.updateViews(
|
|
67011
|
+
await trackView.updateViews();
|
|
67133
67012
|
}
|
|
67134
67013
|
} else {
|
|
67135
67014
|
// Group autoscale
|
|
@@ -67178,19 +67057,25 @@
|
|
|
67178
67057
|
for (let trackView of groupTrackViews) {
|
|
67179
67058
|
trackView.track.dataRange = dataRange;
|
|
67180
67059
|
trackView.track.autoscale = false;
|
|
67181
|
-
p.push(trackView.updateViews(
|
|
67060
|
+
p.push(trackView.updateViews());
|
|
67182
67061
|
}
|
|
67183
67062
|
|
|
67184
67063
|
await Promise.all(p);
|
|
67185
67064
|
}
|
|
67186
67065
|
}
|
|
67187
67066
|
|
|
67188
|
-
await Promise.all(otherTracks.map(tv => tv.updateViews(
|
|
67067
|
+
await Promise.all(otherTracks.map(tv => tv.updateViews())); // for (let trackView of otherTracks) {
|
|
67189
67068
|
// await trackView.updateViews(force);
|
|
67190
67069
|
// }
|
|
67191
67070
|
}
|
|
67192
67071
|
}
|
|
67193
67072
|
|
|
67073
|
+
repaintViews() {
|
|
67074
|
+
for (let trackView of this.trackViews) {
|
|
67075
|
+
trackView.repaintViews();
|
|
67076
|
+
}
|
|
67077
|
+
}
|
|
67078
|
+
|
|
67194
67079
|
updateLocusSearchWidget() {
|
|
67195
67080
|
const referenceFrameList = this.referenceFrameList; // Update end position of reference frames based on pixel widths. This is hacky, but its been done here
|
|
67196
67081
|
// for a long time, although indirectly.
|
|
@@ -67248,41 +67133,53 @@
|
|
|
67248
67133
|
referenceFrame.zoomWithScaleFactor(this, scaleFactor, viewportWidth, centerBPOrUndefined);
|
|
67249
67134
|
}
|
|
67250
67135
|
}
|
|
67136
|
+
/**
|
|
67137
|
+
* Add a new multi-locus panel for the specified region
|
|
67138
|
+
* @param chr
|
|
67139
|
+
* @param start
|
|
67140
|
+
* @param end
|
|
67141
|
+
* @param referenceFrameLeft - optional, if supplied new panel should be placed to the immediate right
|
|
67142
|
+
*/
|
|
67143
|
+
|
|
67251
67144
|
|
|
67252
|
-
async
|
|
67145
|
+
async addMultiLocusPanel(chr, start, end, referenceFrameLeft) {
|
|
67253
67146
|
// account for reduced viewport width as a result of adding right mate pair panel
|
|
67254
67147
|
const viewportWidth = this.calculateViewportWidth(1 + this.referenceFrameList.length);
|
|
67255
67148
|
const scaleFactor = this.calculateViewportWidth(this.referenceFrameList.length) / this.calculateViewportWidth(1 + this.referenceFrameList.length);
|
|
67256
|
-
adjustReferenceFrame(scaleFactor, referenceFrameLeft, viewportWidth, alignment.start, alignment.lengthOnRef); // create right mate pair reference frame
|
|
67257
67149
|
|
|
67258
|
-
|
|
67259
|
-
|
|
67150
|
+
for (let refFrame of this.referenceFrameList) {
|
|
67151
|
+
refFrame.bpPerPixel *= scaleFactor;
|
|
67152
|
+
}
|
|
67153
|
+
|
|
67154
|
+
const bpp = (end - start) / viewportWidth;
|
|
67155
|
+
const newReferenceFrame = new ReferenceFrame(this.genome, chr, start, end, bpp);
|
|
67156
|
+
const indexLeft = referenceFrameLeft ? this.referenceFrameList.indexOf(referenceFrameLeft) : this.referenceFrameList.length - 1;
|
|
67157
|
+
const indexRight = 1 + indexLeft; // TODO -- this is really ugly
|
|
67260
67158
|
|
|
67261
|
-
const indexLeft = this.referenceFrameList.indexOf(referenceFrameLeft);
|
|
67262
|
-
const indexRight = 1 + this.referenceFrameList.indexOf(referenceFrameLeft);
|
|
67263
67159
|
const {
|
|
67264
67160
|
$viewport
|
|
67265
67161
|
} = this.trackViews[0].viewports[indexLeft];
|
|
67266
67162
|
const viewportColumn = viewportColumnManager.insertAfter($viewport.get(0).parentElement);
|
|
67267
67163
|
|
|
67268
67164
|
if (indexRight === this.referenceFrameList.length) {
|
|
67269
|
-
this.referenceFrameList.push(
|
|
67165
|
+
this.referenceFrameList.push(newReferenceFrame);
|
|
67270
67166
|
|
|
67271
67167
|
for (let trackView of this.trackViews) {
|
|
67272
|
-
const viewport = createViewport(trackView, viewportColumn,
|
|
67168
|
+
const viewport = createViewport(trackView, viewportColumn, newReferenceFrame);
|
|
67273
67169
|
trackView.viewports.push(viewport);
|
|
67274
67170
|
}
|
|
67275
67171
|
} else {
|
|
67276
|
-
this.referenceFrameList.splice(indexRight, 0,
|
|
67172
|
+
this.referenceFrameList.splice(indexRight, 0, newReferenceFrame);
|
|
67277
67173
|
|
|
67278
67174
|
for (let trackView of this.trackViews) {
|
|
67279
|
-
const viewport = createViewport(trackView, viewportColumn,
|
|
67175
|
+
const viewport = createViewport(trackView, viewportColumn, newReferenceFrame);
|
|
67280
67176
|
trackView.viewports.splice(indexRight, 0, viewport);
|
|
67281
67177
|
}
|
|
67282
67178
|
}
|
|
67283
67179
|
|
|
67284
67180
|
this.centerLineList = this.createCenterLineList(this.columnContainer);
|
|
67285
|
-
|
|
67181
|
+
resize.call(this);
|
|
67182
|
+
await this.updateViews(true);
|
|
67286
67183
|
}
|
|
67287
67184
|
|
|
67288
67185
|
async removeMultiLocusPanel(referenceFrame) {
|
|
@@ -67311,8 +67208,15 @@
|
|
|
67311
67208
|
const scaleFactor = this.calculateViewportWidth(1 + this.referenceFrameList.length) / this.calculateViewportWidth(this.referenceFrameList.length);
|
|
67312
67209
|
await this.rescaleForMultiLocus(scaleFactor);
|
|
67313
67210
|
}
|
|
67211
|
+
/**
|
|
67212
|
+
* Goto the locus represented by the selected referenceFrame, discarding all other panels
|
|
67213
|
+
*
|
|
67214
|
+
* @param referenceFrame
|
|
67215
|
+
* @returns {Promise<void>}
|
|
67216
|
+
*/
|
|
67217
|
+
|
|
67314
67218
|
|
|
67315
|
-
async
|
|
67219
|
+
async gotoMultilocusPanel(referenceFrame) {
|
|
67316
67220
|
const referenceFrameIndex = this.referenceFrameList.indexOf(referenceFrame); // Remove columns for unselected panels
|
|
67317
67221
|
|
|
67318
67222
|
this.columnContainer.querySelectorAll('.igv-column').forEach((column, c) => {
|
|
@@ -67360,7 +67264,7 @@
|
|
|
67360
67264
|
|
|
67361
67265
|
this.centerLineList = this.createCenterLineList(this.columnContainer);
|
|
67362
67266
|
this.updateUIWithReferenceFrameList();
|
|
67363
|
-
await this.updateViews(
|
|
67267
|
+
await this.updateViews();
|
|
67364
67268
|
}
|
|
67365
67269
|
/**
|
|
67366
67270
|
* @deprecated This is a deprecated method with no known usages. To be removed in a future release.
|
|
@@ -67613,20 +67517,6 @@
|
|
|
67613
67517
|
const surl = (idx > 0 ? path.substring(0, idx) : path) + "?sessionURL=blob:" + this.compressedSession();
|
|
67614
67518
|
return surl;
|
|
67615
67519
|
}
|
|
67616
|
-
|
|
67617
|
-
currentLoci() {
|
|
67618
|
-
const loci = [];
|
|
67619
|
-
const anyTrackView = this.trackViews[0];
|
|
67620
|
-
|
|
67621
|
-
for (let {
|
|
67622
|
-
referenceFrame
|
|
67623
|
-
} of anyTrackView.viewports) {
|
|
67624
|
-
const locusString = referenceFrame.getLocusString();
|
|
67625
|
-
loci.push(locusString);
|
|
67626
|
-
}
|
|
67627
|
-
|
|
67628
|
-
return loci;
|
|
67629
|
-
}
|
|
67630
67520
|
/**
|
|
67631
67521
|
* Record a mouse click on a specific viewport. This might be the start of a drag operation. Dragging
|
|
67632
67522
|
* (panning) is handled here so that the mouse can move out of a specific viewport (e.g. stray into another
|
|
@@ -67661,10 +67551,22 @@
|
|
|
67661
67551
|
this.fireEvent('trackdragend');
|
|
67662
67552
|
}
|
|
67663
67553
|
}
|
|
67554
|
+
/**
|
|
67555
|
+
* Track drag here refers to vertical dragging to reorder tracks, not horizontal panning.
|
|
67556
|
+
*
|
|
67557
|
+
* @param trackView
|
|
67558
|
+
*/
|
|
67559
|
+
|
|
67664
67560
|
|
|
67665
67561
|
startTrackDrag(trackView) {
|
|
67666
67562
|
this.dragTrack = trackView;
|
|
67667
67563
|
}
|
|
67564
|
+
/**
|
|
67565
|
+
* Track drag here refers to vertical dragging to reorder tracks, not horizontal panning.
|
|
67566
|
+
*
|
|
67567
|
+
* @param dragDestination
|
|
67568
|
+
*/
|
|
67569
|
+
|
|
67668
67570
|
|
|
67669
67571
|
updateTrackDrag(dragDestination) {
|
|
67670
67572
|
if (dragDestination && this.dragTrack) {
|
|
@@ -67738,12 +67640,9 @@
|
|
|
67738
67640
|
}
|
|
67739
67641
|
|
|
67740
67642
|
addWindowResizeHandler() {
|
|
67741
|
-
this.
|
|
67643
|
+
// Create a copy of the prototype "resize" function bound to this instance. Neccessary to support removing.
|
|
67644
|
+
this.boundWindowResizeHandler = resize.bind(this);
|
|
67742
67645
|
window.addEventListener('resize', this.boundWindowResizeHandler);
|
|
67743
|
-
|
|
67744
|
-
function windowResizeHandler() {
|
|
67745
|
-
this.resize();
|
|
67746
|
-
}
|
|
67747
67646
|
}
|
|
67748
67647
|
|
|
67749
67648
|
removeWindowResizeHandler() {
|
|
@@ -67822,7 +67721,7 @@
|
|
|
67822
67721
|
id: this.genome.id,
|
|
67823
67722
|
chromosomes: makeCircViewChromosomes(this.genome)
|
|
67824
67723
|
});
|
|
67825
|
-
this.circularViewVisible = show
|
|
67724
|
+
this.circularViewVisible = show;
|
|
67826
67725
|
}
|
|
67827
67726
|
|
|
67828
67727
|
get circularViewVisible() {
|
|
@@ -67837,6 +67736,48 @@
|
|
|
67837
67736
|
}
|
|
67838
67737
|
|
|
67839
67738
|
}
|
|
67739
|
+
/**
|
|
67740
|
+
* Function called win window is resized, or visibility changed (e.g. "show" from a tab). This is a function rather
|
|
67741
|
+
* than class method because it needs to be copied and bound to specific instances of browser to support listener
|
|
67742
|
+
* removal
|
|
67743
|
+
*
|
|
67744
|
+
* @returns {Promise<void>}
|
|
67745
|
+
*/
|
|
67746
|
+
|
|
67747
|
+
|
|
67748
|
+
async function resize() {
|
|
67749
|
+
const viewportWidth = this.calculateViewportWidth(this.referenceFrameList.length);
|
|
67750
|
+
|
|
67751
|
+
for (let referenceFrame of this.referenceFrameList) {
|
|
67752
|
+
const index = this.referenceFrameList.indexOf(referenceFrame);
|
|
67753
|
+
const {
|
|
67754
|
+
chr,
|
|
67755
|
+
genome
|
|
67756
|
+
} = referenceFrame;
|
|
67757
|
+
const {
|
|
67758
|
+
bpLength
|
|
67759
|
+
} = genome.getChromosome(referenceFrame.chr);
|
|
67760
|
+
const viewportWidthBP = referenceFrame.toBP(viewportWidth); // viewportWidthBP > bpLength occurs when locus is full chromosome and user widens browser
|
|
67761
|
+
|
|
67762
|
+
if (GenomeUtils.isWholeGenomeView(chr) || viewportWidthBP > bpLength) {
|
|
67763
|
+
// console.log(`${ Date.now() } Recalc referenceFrame(${ index }) bpp. viewport ${ StringUtils.numberFormatter(viewportWidthBP) } > ${ StringUtils.numberFormatter(bpLength) }.`)
|
|
67764
|
+
referenceFrame.bpPerPixel = bpLength / viewportWidth;
|
|
67765
|
+
} else {
|
|
67766
|
+
// console.log(`${ Date.now() } Recalc referenceFrame(${ index }) end.`)
|
|
67767
|
+
referenceFrame.end = referenceFrame.start + referenceFrame.toBP(viewportWidth);
|
|
67768
|
+
}
|
|
67769
|
+
|
|
67770
|
+
for (let {
|
|
67771
|
+
viewports
|
|
67772
|
+
} of this.trackViews) {
|
|
67773
|
+
viewports[index].setWidth(viewportWidth);
|
|
67774
|
+
}
|
|
67775
|
+
}
|
|
67776
|
+
|
|
67777
|
+
this.updateUIWithReferenceFrameList(); //TODO -- update view only if needed. Reducing size never needed. Increasing size maybe
|
|
67778
|
+
|
|
67779
|
+
await this.updateViews(true);
|
|
67780
|
+
}
|
|
67840
67781
|
|
|
67841
67782
|
function handleMouseMove(e) {
|
|
67842
67783
|
e.preventDefault();
|