igv 2.11.2 → 2.12.2
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 +1183 -1209
- package/dist/igv.esm.min.js +9 -9
- package/dist/igv.esm.min.js.map +1 -1
- package/dist/igv.js +1179 -1129
- 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);
|
|
@@ -19911,7 +19911,7 @@
|
|
|
19911
19911
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
19912
19912
|
* THE SOFTWARE.
|
|
19913
19913
|
*/
|
|
19914
|
-
const knownFileExtensions = new Set(["narrowpeak", "broadpeak", "regionpeak", "peaks", "bedgraph", "wig", "gff3", "gff", "gtf", "fusionjuncspan", "refflat", "seg", "aed", "bed", "vcf", "bb", "bigbed", "biginteract", "bw", "bigwig", "bam", "tdf", "refgene", "genepred", "genepredext", "bedpe", "bp", "snp", "rmsk", "cram", "gwas", "maf", "mut"]);
|
|
19914
|
+
const knownFileExtensions = new Set(["narrowpeak", "broadpeak", "regionpeak", "peaks", "bedgraph", "wig", "gff3", "gff", "gtf", "fusionjuncspan", "refflat", "seg", "aed", "bed", "vcf", "bb", "bigbed", "biginteract", "biggenepred", "bignarrowpeak", "bw", "bigwig", "bam", "tdf", "refgene", "genepred", "genepredext", "bedpe", "bp", "snp", "rmsk", "cram", "gwas", "maf", "mut"]);
|
|
19915
19915
|
/**
|
|
19916
19916
|
* Return a custom format object with the given name.
|
|
19917
19917
|
* @param name
|
|
@@ -20032,6 +20032,7 @@
|
|
|
20032
20032
|
|
|
20033
20033
|
case "bedpe":
|
|
20034
20034
|
case "bedpe-loop":
|
|
20035
|
+
case "biginteract":
|
|
20035
20036
|
return "interact";
|
|
20036
20037
|
|
|
20037
20038
|
case "bp":
|
|
@@ -20043,7 +20044,8 @@
|
|
|
20043
20044
|
case "bed":
|
|
20044
20045
|
case "bigbed":
|
|
20045
20046
|
case "bb":
|
|
20046
|
-
case "
|
|
20047
|
+
case "biggenepred":
|
|
20048
|
+
case "bignarrowpeak":
|
|
20047
20049
|
return "bedtype";
|
|
20048
20050
|
|
|
20049
20051
|
default:
|
|
@@ -20287,6 +20289,30 @@
|
|
|
20287
20289
|
return window.location.protocol === "https:" || window.location.hostname === "localhost";
|
|
20288
20290
|
}
|
|
20289
20291
|
|
|
20292
|
+
const pairs = [['A', 'T'], ['G', 'C'], ['Y', 'R'], ['W', 'S'], ['K', 'M'], ['D', 'H'], ['B', 'V']];
|
|
20293
|
+
const complements = new Map();
|
|
20294
|
+
|
|
20295
|
+
for (let p of pairs) {
|
|
20296
|
+
const p1 = p[0];
|
|
20297
|
+
const p2 = p[1];
|
|
20298
|
+
complements.set(p1, p2);
|
|
20299
|
+
complements.set(p2, p1);
|
|
20300
|
+
complements.set(p1.toLowerCase(), p2.toLowerCase());
|
|
20301
|
+
complements.set(p2.toLowerCase(), p1.toLowerCase());
|
|
20302
|
+
}
|
|
20303
|
+
|
|
20304
|
+
function reverseComplementSequence(sequence) {
|
|
20305
|
+
let comp = '';
|
|
20306
|
+
let idx = sequence.length;
|
|
20307
|
+
|
|
20308
|
+
while (idx-- > 0) {
|
|
20309
|
+
const base = sequence[idx];
|
|
20310
|
+
comp += complements.has(base) ? complements.get(base) : base;
|
|
20311
|
+
}
|
|
20312
|
+
|
|
20313
|
+
return comp;
|
|
20314
|
+
}
|
|
20315
|
+
|
|
20290
20316
|
/*
|
|
20291
20317
|
* The MIT License (MIT)
|
|
20292
20318
|
*
|
|
@@ -20440,16 +20466,21 @@
|
|
|
20440
20466
|
const viewport = clickState.viewport;
|
|
20441
20467
|
|
|
20442
20468
|
if (viewport.referenceFrame.bpPerPixel <= 1) {
|
|
20469
|
+
const pixelWidth = viewport.getWidth();
|
|
20470
|
+
const bpWindow = pixelWidth * viewport.referenceFrame.bpPerPixel;
|
|
20471
|
+
const chr = viewport.referenceFrame.chr;
|
|
20472
|
+
const start = Math.floor(viewport.referenceFrame.start);
|
|
20473
|
+
const end = Math.ceil(start + bpWindow);
|
|
20443
20474
|
const items = [{
|
|
20444
|
-
label: 'View visible sequence...',
|
|
20475
|
+
label: this.reversed ? 'View visible sequence (reversed)...' : 'View visible sequence...',
|
|
20445
20476
|
click: async () => {
|
|
20446
|
-
|
|
20447
|
-
|
|
20448
|
-
|
|
20449
|
-
|
|
20450
|
-
|
|
20451
|
-
|
|
20452
|
-
Alert.presentAlert(
|
|
20477
|
+
let seq = await this.browser.genome.sequence.getSequence(chr, start, end);
|
|
20478
|
+
|
|
20479
|
+
if (this.reversed) {
|
|
20480
|
+
seq = reverseComplementSequence(seq);
|
|
20481
|
+
}
|
|
20482
|
+
|
|
20483
|
+
Alert.presentAlert(seq);
|
|
20453
20484
|
}
|
|
20454
20485
|
}];
|
|
20455
20486
|
|
|
@@ -20457,13 +20488,18 @@
|
|
|
20457
20488
|
items.push({
|
|
20458
20489
|
label: 'Copy visible sequence',
|
|
20459
20490
|
click: async () => {
|
|
20460
|
-
|
|
20461
|
-
|
|
20462
|
-
|
|
20463
|
-
|
|
20464
|
-
|
|
20465
|
-
|
|
20466
|
-
|
|
20491
|
+
let seq = await this.browser.genome.sequence.getSequence(chr, start, end);
|
|
20492
|
+
|
|
20493
|
+
if (this.reversed) {
|
|
20494
|
+
seq = reverseComplementSequence(seq);
|
|
20495
|
+
}
|
|
20496
|
+
|
|
20497
|
+
try {
|
|
20498
|
+
await navigator.clipboard.writeText(seq);
|
|
20499
|
+
} catch (e) {
|
|
20500
|
+
console.error(e);
|
|
20501
|
+
Alert.presentAlert(`error copying sequence to clipboard ${e}`);
|
|
20502
|
+
}
|
|
20467
20503
|
}
|
|
20468
20504
|
});
|
|
20469
20505
|
}
|
|
@@ -20604,7 +20640,7 @@
|
|
|
20604
20640
|
}
|
|
20605
20641
|
}
|
|
20606
20642
|
|
|
20607
|
-
supportsWholeGenome() {
|
|
20643
|
+
get supportsWholeGenome() {
|
|
20608
20644
|
return false;
|
|
20609
20645
|
}
|
|
20610
20646
|
|
|
@@ -20672,12 +20708,13 @@
|
|
|
20672
20708
|
});
|
|
20673
20709
|
this.$viewport.append(this.$content);
|
|
20674
20710
|
this.$content.height(this.$viewport.height());
|
|
20675
|
-
this.contentDiv = this.$content.get(0);
|
|
20676
|
-
this.$
|
|
20677
|
-
|
|
20678
|
-
this.canvas = this.$canvas.get(0)
|
|
20679
|
-
this.ctx = this.canvas.getContext("2d")
|
|
20680
|
-
|
|
20711
|
+
this.contentDiv = this.$content.get(0); // this.$canvas = $('<canvas>')
|
|
20712
|
+
// this.$content.append(this.$canvas)
|
|
20713
|
+
//
|
|
20714
|
+
// this.canvas = this.$canvas.get(0)
|
|
20715
|
+
// this.ctx = this.canvas.getContext("2d")
|
|
20716
|
+
|
|
20717
|
+
this.$viewport.width(width);
|
|
20681
20718
|
this.initializationHelper();
|
|
20682
20719
|
}
|
|
20683
20720
|
|
|
@@ -20733,14 +20770,13 @@
|
|
|
20733
20770
|
console.log('Viewport - draw(drawConfiguration, features, roiFeatures)');
|
|
20734
20771
|
}
|
|
20735
20772
|
|
|
20736
|
-
checkContentHeight() {
|
|
20773
|
+
checkContentHeight(features) {
|
|
20737
20774
|
let track = this.trackView.track;
|
|
20775
|
+
features = features || this.cachedFeatures;
|
|
20738
20776
|
|
|
20739
20777
|
if ("FILL" === track.displayMode) {
|
|
20740
20778
|
this.setContentHeight(this.$viewport.height());
|
|
20741
20779
|
} else if (typeof track.computePixelHeight === 'function') {
|
|
20742
|
-
let features = this.cachedFeatures;
|
|
20743
|
-
|
|
20744
20780
|
if (features && features.length > 0) {
|
|
20745
20781
|
let requiredContentHeight = track.computePixelHeight(features);
|
|
20746
20782
|
let currentContentHeight = this.$content.height();
|
|
@@ -20760,7 +20796,6 @@
|
|
|
20760
20796
|
// Maximum height of a canvas is ~32,000 pixels on Chrome, possibly smaller on other platforms
|
|
20761
20797
|
contentHeight = Math.min(contentHeight, 32000);
|
|
20762
20798
|
this.$content.height(contentHeight);
|
|
20763
|
-
if (this.tile) this.tile.invalidate = true;
|
|
20764
20799
|
}
|
|
20765
20800
|
|
|
20766
20801
|
isLoading() {
|
|
@@ -20775,8 +20810,6 @@
|
|
|
20775
20810
|
|
|
20776
20811
|
setWidth(width) {
|
|
20777
20812
|
this.$viewport.width(width);
|
|
20778
|
-
this.canvas.style.width = `${width}px`;
|
|
20779
|
-
this.canvas.setAttribute('width', width);
|
|
20780
20813
|
}
|
|
20781
20814
|
|
|
20782
20815
|
getWidth() {
|
|
@@ -20804,7 +20837,6 @@
|
|
|
20804
20837
|
this.popover.dispose();
|
|
20805
20838
|
}
|
|
20806
20839
|
|
|
20807
|
-
this.removeMouseHandlers();
|
|
20808
20840
|
this.$viewport.get(0).remove(); // Null out all properties -- this should not be neccessary, but just in case there is a
|
|
20809
20841
|
// reference to self somewhere we want to free memory.
|
|
20810
20842
|
|
|
@@ -23061,7 +23093,7 @@
|
|
|
23061
23093
|
}
|
|
23062
23094
|
};
|
|
23063
23095
|
|
|
23064
|
-
const _version = "2.
|
|
23096
|
+
const _version = "2.12.2";
|
|
23065
23097
|
|
|
23066
23098
|
function version$1() {
|
|
23067
23099
|
return _version;
|
|
@@ -23204,7 +23236,7 @@
|
|
|
23204
23236
|
class Genome {
|
|
23205
23237
|
constructor(config, sequence, ideograms, aliases) {
|
|
23206
23238
|
this.config = config;
|
|
23207
|
-
this.id = config.id;
|
|
23239
|
+
this.id = config.id || generateGenomeID(config);
|
|
23208
23240
|
this.sequence = sequence;
|
|
23209
23241
|
this.chromosomeNames = sequence.chromosomeNames;
|
|
23210
23242
|
this.chromosomes = sequence.chromosomes; // An object (functions as a dictionary)
|
|
@@ -23411,6 +23443,7 @@
|
|
|
23411
23443
|
}
|
|
23412
23444
|
|
|
23413
23445
|
async getSequence(chr, start, end) {
|
|
23446
|
+
chr = this.getChromosomeName(chr);
|
|
23414
23447
|
return this.sequence.getSequence(chr, start, end);
|
|
23415
23448
|
}
|
|
23416
23449
|
|
|
@@ -23519,6 +23552,18 @@
|
|
|
23519
23552
|
}
|
|
23520
23553
|
}
|
|
23521
23554
|
|
|
23555
|
+
function generateGenomeID(config) {
|
|
23556
|
+
if (config.id !== undefined) {
|
|
23557
|
+
return config.id;
|
|
23558
|
+
} else if (config.fastaURL && isString$3(config.fastaURL)) {
|
|
23559
|
+
return config.fastaURL;
|
|
23560
|
+
} else if (config.fastaURL && config.fastaURL.name) {
|
|
23561
|
+
return config.fastaURL.name;
|
|
23562
|
+
} else {
|
|
23563
|
+
return ("0000" + (Math.random() * Math.pow(36, 4) << 0).toString(36)).slice(-4);
|
|
23564
|
+
}
|
|
23565
|
+
}
|
|
23566
|
+
|
|
23522
23567
|
/**
|
|
23523
23568
|
* Created by dat on 9/16/16.
|
|
23524
23569
|
*/
|
|
@@ -23537,15 +23582,13 @@
|
|
|
23537
23582
|
});
|
|
23538
23583
|
this.$viewport.append(this.$spinner);
|
|
23539
23584
|
this.$spinner.append($$1('<div>'));
|
|
23540
|
-
const
|
|
23541
|
-
track
|
|
23542
|
-
} = this.trackView;
|
|
23585
|
+
const track = this.trackView.track;
|
|
23543
23586
|
|
|
23544
23587
|
if ('sequence' !== track.type) {
|
|
23545
23588
|
this.$zoomInNotice = this.createZoomInNotice(this.$content);
|
|
23546
23589
|
}
|
|
23547
23590
|
|
|
23548
|
-
if (track.name && "sequence" !== track.
|
|
23591
|
+
if (track.name && "sequence" !== track.id) {
|
|
23549
23592
|
this.$trackLabel = $$1('<div class="igv-track-label">');
|
|
23550
23593
|
this.$viewport.append(this.$trackLabel);
|
|
23551
23594
|
this.setTrackLabel(track.name);
|
|
@@ -23559,6 +23602,11 @@
|
|
|
23559
23602
|
this.addMouseHandlers();
|
|
23560
23603
|
}
|
|
23561
23604
|
|
|
23605
|
+
setContentHeight(contentHeight) {
|
|
23606
|
+
super.setContentHeight(contentHeight);
|
|
23607
|
+
if (this.featureCache) this.featureCache.redraw = true;
|
|
23608
|
+
}
|
|
23609
|
+
|
|
23562
23610
|
setTrackLabel(label) {
|
|
23563
23611
|
this.$trackLabel.empty();
|
|
23564
23612
|
this.$trackLabel.html(label);
|
|
@@ -23575,55 +23623,72 @@
|
|
|
23575
23623
|
this.$spinner.hide();
|
|
23576
23624
|
}
|
|
23577
23625
|
}
|
|
23626
|
+
/**
|
|
23627
|
+
* Test to determine if we are zoomed in far enough to see features. Applicable to tracks with visibility windows.
|
|
23628
|
+
*
|
|
23629
|
+
* As a side effect the viewports canvas is removed if zoomed out.
|
|
23630
|
+
*
|
|
23631
|
+
* @returns {boolean} true if we are zoomed in past visibility window, false otherwise
|
|
23632
|
+
*/
|
|
23578
23633
|
|
|
23579
|
-
checkZoomIn() {
|
|
23580
|
-
const showZoomInNotice = () => {
|
|
23581
|
-
const referenceFrame = this.referenceFrame;
|
|
23582
23634
|
|
|
23583
|
-
|
|
23635
|
+
checkZoomIn() {
|
|
23636
|
+
const zoomedOutOfWindow = () => {
|
|
23637
|
+
if (this.referenceFrame.chr.toLowerCase() === "all" && !this.trackView.track.supportsWholeGenome) {
|
|
23584
23638
|
return true;
|
|
23585
23639
|
} else {
|
|
23586
23640
|
const visibilityWindow = this.trackView.track.visibilityWindow;
|
|
23587
|
-
return visibilityWindow !== undefined && visibilityWindow > 0 && referenceFrame.bpPerPixel * this.$viewport.width() > visibilityWindow;
|
|
23641
|
+
return visibilityWindow !== undefined && visibilityWindow > 0 && this.referenceFrame.bpPerPixel * this.$viewport.width() > visibilityWindow;
|
|
23588
23642
|
}
|
|
23589
23643
|
};
|
|
23590
23644
|
|
|
23645
|
+
if (this.trackView.track && "sequence" === this.trackView.track.type && this.referenceFrame.bpPerPixel > 1) {
|
|
23646
|
+
$$1(this.canvas).remove();
|
|
23647
|
+
this.canvas = undefined; //this.featureCache = undefined
|
|
23648
|
+
|
|
23649
|
+
return false;
|
|
23650
|
+
}
|
|
23651
|
+
|
|
23591
23652
|
if (!this.viewIsReady()) {
|
|
23592
23653
|
return false;
|
|
23593
23654
|
}
|
|
23594
23655
|
|
|
23595
|
-
if (
|
|
23596
|
-
|
|
23597
|
-
|
|
23598
|
-
|
|
23599
|
-
|
|
23600
|
-
|
|
23601
|
-
}
|
|
23656
|
+
if (zoomedOutOfWindow()) {
|
|
23657
|
+
// Out of visibility window
|
|
23658
|
+
if (this.canvas) {
|
|
23659
|
+
$$1(this.canvas).remove();
|
|
23660
|
+
this.canvas = undefined; //this.featureCache = undefined
|
|
23661
|
+
}
|
|
23602
23662
|
|
|
23603
|
-
|
|
23663
|
+
if (this.trackView.track.autoHeight) {
|
|
23664
|
+
const minHeight = this.trackView.minHeight || 0;
|
|
23665
|
+
this.setContentHeight(minHeight);
|
|
23666
|
+
}
|
|
23604
23667
|
|
|
23605
|
-
|
|
23606
|
-
|
|
23607
|
-
|
|
23608
|
-
}
|
|
23668
|
+
if (this.$zoomInNotice) {
|
|
23669
|
+
this.$zoomInNotice.show();
|
|
23670
|
+
}
|
|
23609
23671
|
|
|
23610
|
-
|
|
23611
|
-
|
|
23672
|
+
return false;
|
|
23673
|
+
} else {
|
|
23674
|
+
if (this.$zoomInNotice) {
|
|
23612
23675
|
this.$zoomInNotice.hide();
|
|
23613
|
-
return true;
|
|
23614
23676
|
}
|
|
23615
|
-
}
|
|
23616
23677
|
|
|
23617
|
-
|
|
23678
|
+
return true;
|
|
23679
|
+
}
|
|
23618
23680
|
}
|
|
23681
|
+
/**
|
|
23682
|
+
* Adjust the canvas to the current genomic state.
|
|
23683
|
+
*/
|
|
23684
|
+
|
|
23619
23685
|
|
|
23620
23686
|
shift() {
|
|
23621
|
-
const
|
|
23622
|
-
const referenceFrame = self.referenceFrame;
|
|
23687
|
+
const referenceFrame = this.referenceFrame;
|
|
23623
23688
|
|
|
23624
|
-
if (
|
|
23625
|
-
const pixelOffset = Math.round((
|
|
23626
|
-
|
|
23689
|
+
if (this.canvas && this.canvas._data && this.canvas._data.chr === this.referenceFrame.chr && this.canvas._data.bpPerPixel === referenceFrame.bpPerPixel) {
|
|
23690
|
+
const pixelOffset = Math.round((this.canvas._data.startBP - referenceFrame.start) / referenceFrame.bpPerPixel);
|
|
23691
|
+
this.canvas.style.left = pixelOffset + "px";
|
|
23627
23692
|
}
|
|
23628
23693
|
}
|
|
23629
23694
|
|
|
@@ -23649,9 +23714,10 @@
|
|
|
23649
23714
|
this.startSpinner();
|
|
23650
23715
|
|
|
23651
23716
|
try {
|
|
23652
|
-
const
|
|
23717
|
+
const track = this.trackView.track;
|
|
23718
|
+
const features = await this.getFeatures(track, chr, bpStart, bpEnd, referenceFrame.bpPerPixel);
|
|
23653
23719
|
let roiFeatures = [];
|
|
23654
|
-
const roi = mergeArrays(this.browser.roi,
|
|
23720
|
+
const roi = mergeArrays(this.browser.roi, track.roi);
|
|
23655
23721
|
|
|
23656
23722
|
if (roi) {
|
|
23657
23723
|
for (let r of roi) {
|
|
@@ -23663,11 +23729,13 @@
|
|
|
23663
23729
|
}
|
|
23664
23730
|
}
|
|
23665
23731
|
|
|
23666
|
-
|
|
23732
|
+
const mr = track && ("wig" === track.type || "merged" === track.type); // wig tracks are potentially multiresolution (e.g. bigwig)
|
|
23733
|
+
|
|
23734
|
+
this.featureCache = new FeatureCache(chr, bpStart, bpEnd, referenceFrame.bpPerPixel, features, roiFeatures, mr);
|
|
23667
23735
|
this.loading = false;
|
|
23668
23736
|
this.hideMessage();
|
|
23669
23737
|
this.stopSpinner();
|
|
23670
|
-
return this.
|
|
23738
|
+
return this.featureCache;
|
|
23671
23739
|
} catch (error) {
|
|
23672
23740
|
// Track might have been removed during load
|
|
23673
23741
|
if (this.trackView && this.trackView.disposed !== true) {
|
|
@@ -23680,38 +23748,32 @@
|
|
|
23680
23748
|
this.stopSpinner();
|
|
23681
23749
|
}
|
|
23682
23750
|
}
|
|
23751
|
+
/**
|
|
23752
|
+
* Repaint the canvas using the cached features
|
|
23753
|
+
*
|
|
23754
|
+
*/
|
|
23683
23755
|
|
|
23684
|
-
|
|
23685
|
-
|
|
23756
|
+
|
|
23757
|
+
repaint() {
|
|
23758
|
+
if (undefined === this.featureCache) {
|
|
23686
23759
|
return;
|
|
23687
23760
|
}
|
|
23688
23761
|
|
|
23689
23762
|
let {
|
|
23690
23763
|
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
|
|
23764
|
+
roiFeatures
|
|
23765
|
+
} = this.featureCache; //this.tile.bpPerPixel = this.referenceFrame.bpPerPixel
|
|
23766
|
+
// const isWGV = GenomeUtils.isWholeGenomeView(this.browser.referenceFrameList[0].chr)
|
|
23708
23767
|
|
|
23768
|
+
const isWGV = GenomeUtils.isWholeGenomeView(this.referenceFrame.chr); // Canvas dimensions. There is no left-right panning for WGV so canvas width is viewport width.
|
|
23769
|
+
// For deep tracks we paint a canvas == 3*viewportHeight centered on the current vertical scroll position
|
|
23709
23770
|
|
|
23771
|
+
const pixelWidth = isWGV ? this.$viewport.width() : 3 * this.$viewport.width();
|
|
23710
23772
|
const viewportHeight = this.$viewport.height();
|
|
23711
23773
|
const contentHeight = this.getContentHeight();
|
|
23712
23774
|
const minHeight = roiFeatures ? Math.max(contentHeight, viewportHeight) : contentHeight; // Need to fill viewport for ROIs.
|
|
23713
23775
|
|
|
23714
|
-
|
|
23776
|
+
const pixelHeight = Math.min(minHeight, 3 * viewportHeight);
|
|
23715
23777
|
|
|
23716
23778
|
if (0 === pixelWidth || 0 === pixelHeight) {
|
|
23717
23779
|
if (this.canvas) {
|
|
@@ -23721,26 +23783,22 @@
|
|
|
23721
23783
|
return;
|
|
23722
23784
|
}
|
|
23723
23785
|
|
|
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);
|
|
23786
|
+
const canvasTop = Math.max(0, -this.$content.position().top - viewportHeight);
|
|
23787
|
+
const bpPerPixel = this.referenceFrame.bpPerPixel;
|
|
23788
|
+
const startBP = this.referenceFrame.start - (isWGV ? 0 : pixelWidth / 3 * bpPerPixel);
|
|
23789
|
+
const endBP = this.referenceFrame.end + (isWGV ? 0 : pixelWidth / 3 * bpPerPixel);
|
|
23790
|
+
const pixelXOffset = Math.round((startBP - this.referenceFrame.start) / bpPerPixel);
|
|
23735
23791
|
const newCanvas = $$1('<canvas class="igv-canvas">').get(0);
|
|
23736
|
-
const ctx = newCanvas.getContext("2d");
|
|
23737
23792
|
newCanvas.style.width = pixelWidth + "px";
|
|
23738
23793
|
newCanvas.style.height = pixelHeight + "px";
|
|
23794
|
+
newCanvas.style.left = pixelXOffset + "px";
|
|
23795
|
+
newCanvas.style.top = canvasTop + "px"; // Always use high DPI if in "FILL" display mode, otherwise use track setting;
|
|
23796
|
+
|
|
23797
|
+
const devicePixelRatio = "FILL" === this.trackView.track.displayMode || this.trackView.track.supportHiDPI !== false ? window.devicePixelRatio : 1;
|
|
23739
23798
|
newCanvas.width = devicePixelRatio * pixelWidth;
|
|
23740
23799
|
newCanvas.height = devicePixelRatio * pixelHeight;
|
|
23800
|
+
const ctx = newCanvas.getContext("2d");
|
|
23741
23801
|
ctx.scale(devicePixelRatio, devicePixelRatio);
|
|
23742
|
-
newCanvas.style.left = pixelXOffset + "px";
|
|
23743
|
-
newCanvas.style.top = canvasTop + "px";
|
|
23744
23802
|
ctx.translate(0, -canvasTop);
|
|
23745
23803
|
const drawConfiguration = {
|
|
23746
23804
|
context: ctx,
|
|
@@ -23757,20 +23815,32 @@
|
|
|
23757
23815
|
viewportWidth: this.$viewport.width()
|
|
23758
23816
|
};
|
|
23759
23817
|
this.draw(drawConfiguration, features, roiFeatures);
|
|
23760
|
-
this.
|
|
23761
|
-
|
|
23762
|
-
bottom: canvasTop + pixelHeight
|
|
23763
|
-
};
|
|
23818
|
+
this.featureCache.canvasTop = canvasTop;
|
|
23819
|
+
this.featureCache.height = pixelHeight;
|
|
23764
23820
|
|
|
23765
|
-
if (this
|
|
23766
|
-
this
|
|
23821
|
+
if (this.canvas) {
|
|
23822
|
+
$$1(this.canvas).remove();
|
|
23767
23823
|
}
|
|
23768
23824
|
|
|
23769
|
-
|
|
23770
|
-
|
|
23825
|
+
newCanvas._data = {
|
|
23826
|
+
chr: this.featureCache.chr,
|
|
23827
|
+
bpPerPixel,
|
|
23828
|
+
startBP,
|
|
23829
|
+
endBP,
|
|
23830
|
+
pixelHeight,
|
|
23831
|
+
pixelTop: canvasTop
|
|
23832
|
+
};
|
|
23771
23833
|
this.canvas = newCanvas;
|
|
23772
|
-
this.
|
|
23834
|
+
this.$content.append($$1(newCanvas));
|
|
23773
23835
|
}
|
|
23836
|
+
/**
|
|
23837
|
+
* Draw the associated track.
|
|
23838
|
+
*
|
|
23839
|
+
* @param drawConfiguration
|
|
23840
|
+
* @param features
|
|
23841
|
+
* @param roiFeatures
|
|
23842
|
+
*/
|
|
23843
|
+
|
|
23774
23844
|
|
|
23775
23845
|
draw(drawConfiguration, features, roiFeatures) {
|
|
23776
23846
|
// console.log(`${ Date.now() } viewport draw(). track ${ this.trackView.track.type }. content-css-top ${ this.$content.css('top') }. canvas-top ${ drawConfiguration.pixelTop }.`)
|
|
@@ -23785,51 +23855,6 @@
|
|
|
23785
23855
|
r.track.draw(drawConfiguration);
|
|
23786
23856
|
}
|
|
23787
23857
|
}
|
|
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
23858
|
}
|
|
23834
23859
|
|
|
23835
23860
|
containsPosition(chr, position) {
|
|
@@ -23844,15 +23869,17 @@
|
|
|
23844
23869
|
return this.loading;
|
|
23845
23870
|
}
|
|
23846
23871
|
|
|
23847
|
-
|
|
23848
|
-
if (!this.
|
|
23849
|
-
const
|
|
23872
|
+
savePNG() {
|
|
23873
|
+
if (!this.canvas) return;
|
|
23874
|
+
const canvasMetadata = this.featureCache;
|
|
23875
|
+
const canvasTop = canvasMetadata ? canvasMetadata.canvasTop : 0;
|
|
23850
23876
|
const devicePixelRatio = window.devicePixelRatio;
|
|
23851
23877
|
const w = this.$viewport.width() * devicePixelRatio;
|
|
23852
23878
|
const h = this.$viewport.height() * devicePixelRatio;
|
|
23853
23879
|
const x = -$$1(this.canvas).position().left * devicePixelRatio;
|
|
23854
23880
|
const y = (-this.$content.position().top - canvasTop) * devicePixelRatio;
|
|
23855
|
-
const
|
|
23881
|
+
const ctx = this.canvas.getContext("2d");
|
|
23882
|
+
const imageData = ctx.getImageData(x, y, w, h);
|
|
23856
23883
|
const exportCanvas = document.createElement('canvas');
|
|
23857
23884
|
const exportCtx = exportCanvas.getContext('2d');
|
|
23858
23885
|
exportCanvas.width = imageData.width;
|
|
@@ -23968,29 +23995,44 @@
|
|
|
23968
23995
|
viewportWidth: width,
|
|
23969
23996
|
selection: this.selection
|
|
23970
23997
|
};
|
|
23971
|
-
const features = this.
|
|
23972
|
-
const roiFeatures = this.
|
|
23998
|
+
const features = this.featureCache ? this.featureCache.features : [];
|
|
23999
|
+
const roiFeatures = this.featureCache ? this.featureCache.roiFeatures : undefined;
|
|
23973
24000
|
this.draw(config, features, roiFeatures);
|
|
23974
24001
|
context.restore();
|
|
23975
24002
|
}
|
|
23976
24003
|
|
|
23977
|
-
|
|
23978
|
-
return this.
|
|
24004
|
+
get cachedFeatures() {
|
|
24005
|
+
return this.featureCache ? this.featureCache.features : [];
|
|
23979
24006
|
}
|
|
23980
24007
|
|
|
23981
24008
|
async getFeatures(track, chr, start, end, bpPerPixel) {
|
|
23982
|
-
if (this.
|
|
23983
|
-
return this.
|
|
24009
|
+
if (this.featureCache && this.featureCache.containsRange(chr, start, end, bpPerPixel)) {
|
|
24010
|
+
return this.featureCache.features;
|
|
23984
24011
|
} else if (typeof track.getFeatures === "function") {
|
|
23985
24012
|
const features = await track.getFeatures(chr, start, end, bpPerPixel, this);
|
|
23986
|
-
this.
|
|
23987
|
-
this.checkContentHeight();
|
|
24013
|
+
this.checkContentHeight(features);
|
|
23988
24014
|
return features;
|
|
23989
24015
|
} else {
|
|
23990
24016
|
return undefined;
|
|
23991
24017
|
}
|
|
23992
24018
|
}
|
|
23993
24019
|
|
|
24020
|
+
needsRepaint() {
|
|
24021
|
+
if (!this.canvas) return true;
|
|
24022
|
+
const data = this.canvas._data;
|
|
24023
|
+
return !data || this.referenceFrame.start < data.startBP || this.referenceFrame.end > data.endBP || this.referenceFrame.chr !== data.chr || this.referenceFrame.bpPerPixel != data.bpPerPixel;
|
|
24024
|
+
}
|
|
24025
|
+
|
|
24026
|
+
needsReload() {
|
|
24027
|
+
if (!this.featureCache) return true;
|
|
24028
|
+
const referenceFrame = this.referenceFrame;
|
|
24029
|
+
const chr = this.referenceFrame.chr;
|
|
24030
|
+
const start = referenceFrame.start;
|
|
24031
|
+
const end = start + referenceFrame.toBP($$1(this.contentDiv).width());
|
|
24032
|
+
const bpPerPixel = referenceFrame.bpPerPixel;
|
|
24033
|
+
return !this.featureCache.containsRange(chr, start, end, bpPerPixel);
|
|
24034
|
+
}
|
|
24035
|
+
|
|
23994
24036
|
createZoomInNotice($parent) {
|
|
23995
24037
|
const $container = $$1('<div>', {
|
|
23996
24038
|
class: 'igv-zoom-in-notice-container'
|
|
@@ -24008,36 +24050,42 @@
|
|
|
24008
24050
|
}
|
|
24009
24051
|
|
|
24010
24052
|
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));
|
|
24053
|
+
const viewport = this.$viewport.get(0);
|
|
24054
|
+
this.addViewportContextMenuHandler(viewport);
|
|
24017
24055
|
|
|
24018
|
-
|
|
24019
|
-
this.
|
|
24020
|
-
|
|
24021
|
-
|
|
24056
|
+
const md = event => {
|
|
24057
|
+
this.enableClick = true;
|
|
24058
|
+
this.browser.mouseDownOnViewport(event, this);
|
|
24059
|
+
pageCoordinates$1(event);
|
|
24060
|
+
};
|
|
24022
24061
|
|
|
24023
|
-
|
|
24024
|
-
|
|
24025
|
-
|
|
24026
|
-
|
|
24027
|
-
|
|
24028
|
-
|
|
24029
|
-
|
|
24062
|
+
viewport.addEventListener('mousedown', md);
|
|
24063
|
+
viewport.addEventListener('touchstart', md);
|
|
24064
|
+
|
|
24065
|
+
const mu = event => {
|
|
24066
|
+
// Any mouse up cancels drag and scrolling
|
|
24067
|
+
if (this.browser.dragObject || this.browser.isScrolling) {
|
|
24068
|
+
this.browser.cancelTrackPan(); // event.preventDefault();
|
|
24069
|
+
// event.stopPropagation();
|
|
24070
|
+
|
|
24071
|
+
this.enableClick = false; // Until next mouse down
|
|
24072
|
+
} else {
|
|
24073
|
+
this.browser.cancelTrackPan();
|
|
24074
|
+
this.browser.endTrackDrag();
|
|
24075
|
+
}
|
|
24076
|
+
};
|
|
24077
|
+
|
|
24078
|
+
viewport.addEventListener('mouseup', mu);
|
|
24079
|
+
viewport.addEventListener('touchend', mu);
|
|
24080
|
+
this.addViewportClickHandler(this.$viewport.get(0));
|
|
24030
24081
|
|
|
24031
24082
|
if (this.trackView.track.name && "sequence" !== this.trackView.track.config.type) {
|
|
24032
|
-
this.
|
|
24083
|
+
this.addTrackLabelClickHandler(this.$trackLabel.get(0));
|
|
24033
24084
|
}
|
|
24034
24085
|
}
|
|
24035
24086
|
|
|
24036
24087
|
addViewportContextMenuHandler(viewport) {
|
|
24037
|
-
|
|
24038
|
-
viewport.addEventListener('contextmenu', this.boundContextMenuHandler);
|
|
24039
|
-
|
|
24040
|
-
function contextMenuHandler(event) {
|
|
24088
|
+
viewport.addEventListener('contextmenu', event => {
|
|
24041
24089
|
// Ignore if we are doing a drag. This can happen with touch events.
|
|
24042
24090
|
if (this.browser.dragObject) {
|
|
24043
24091
|
return false;
|
|
@@ -24077,55 +24125,12 @@
|
|
|
24077
24125
|
click: () => this.saveSVG()
|
|
24078
24126
|
});
|
|
24079
24127
|
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);
|
|
24128
|
+
});
|
|
24121
24129
|
}
|
|
24122
24130
|
|
|
24123
24131
|
addViewportClickHandler(viewport) {
|
|
24124
|
-
|
|
24125
|
-
|
|
24126
|
-
|
|
24127
|
-
function clickHandler(event) {
|
|
24128
|
-
if (this.enableClick) {
|
|
24132
|
+
viewport.addEventListener('click', event => {
|
|
24133
|
+
if (this.enableClick && this.canvas) {
|
|
24129
24134
|
if (3 === event.which || event.ctrlKey) {
|
|
24130
24135
|
return;
|
|
24131
24136
|
} // Close any currently open popups
|
|
@@ -24199,18 +24204,11 @@
|
|
|
24199
24204
|
|
|
24200
24205
|
lastClickTime = time;
|
|
24201
24206
|
}
|
|
24202
|
-
}
|
|
24203
|
-
}
|
|
24204
|
-
|
|
24205
|
-
removeViewportClickHandler(viewport) {
|
|
24206
|
-
viewport.removeEventListener('click', this.boundClickHandler);
|
|
24207
|
+
});
|
|
24207
24208
|
}
|
|
24208
24209
|
|
|
24209
24210
|
addTrackLabelClickHandler(trackLabel) {
|
|
24210
|
-
|
|
24211
|
-
trackLabel.addEventListener('click', this.boundTrackLabelClickHandler);
|
|
24212
|
-
|
|
24213
|
-
function clickHandler(event) {
|
|
24211
|
+
trackLabel.addEventListener('click', event => {
|
|
24214
24212
|
event.stopPropagation();
|
|
24215
24213
|
const {
|
|
24216
24214
|
track
|
|
@@ -24231,44 +24229,16 @@
|
|
|
24231
24229
|
this.popover = new Popover(this.browser.columnContainer, track.name || '');
|
|
24232
24230
|
this.popover.presentContentWithEvent(event, str);
|
|
24233
24231
|
}
|
|
24234
|
-
}
|
|
24235
|
-
}
|
|
24236
|
-
|
|
24237
|
-
removeTrackLabelClickHandler(trackLabel) {
|
|
24238
|
-
trackLabel.removeEventListener('click', this.boundTrackLabelClickHandler);
|
|
24232
|
+
});
|
|
24239
24233
|
}
|
|
24240
24234
|
|
|
24241
24235
|
}
|
|
24242
24236
|
|
|
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
24237
|
function createClickState(event, viewport) {
|
|
24263
24238
|
const referenceFrame = viewport.referenceFrame;
|
|
24264
24239
|
const viewportCoords = translateMouseCoordinates$1(event, viewport.contentDiv);
|
|
24265
24240
|
const canvasCoords = translateMouseCoordinates$1(event, viewport.canvas);
|
|
24266
24241
|
const genomicLocation = referenceFrame.start + referenceFrame.toBP(viewportCoords.x);
|
|
24267
|
-
|
|
24268
|
-
if (undefined === genomicLocation || null === viewport.tile) {
|
|
24269
|
-
return undefined;
|
|
24270
|
-
}
|
|
24271
|
-
|
|
24272
24242
|
return {
|
|
24273
24243
|
event,
|
|
24274
24244
|
viewport,
|
|
@@ -24322,22 +24292,28 @@
|
|
|
24322
24292
|
return rows.join('');
|
|
24323
24293
|
}
|
|
24324
24294
|
|
|
24325
|
-
|
|
24326
|
-
|
|
24327
|
-
|
|
24328
|
-
|
|
24329
|
-
|
|
24330
|
-
|
|
24331
|
-
|
|
24332
|
-
|
|
24295
|
+
class FeatureCache {
|
|
24296
|
+
constructor(chr, tileStart, tileEnd, bpPerPixel, features, roiFeatures, multiresolution) {
|
|
24297
|
+
this.chr = chr;
|
|
24298
|
+
this.startBP = tileStart;
|
|
24299
|
+
this.endBP = tileEnd;
|
|
24300
|
+
this.bpPerPixel = bpPerPixel;
|
|
24301
|
+
this.features = features;
|
|
24302
|
+
this.roiFeatures = roiFeatures;
|
|
24303
|
+
this.multiresolution = multiresolution;
|
|
24304
|
+
}
|
|
24333
24305
|
|
|
24334
|
-
|
|
24335
|
-
|
|
24336
|
-
|
|
24306
|
+
containsRange(chr, start, end, bpPerPixel) {
|
|
24307
|
+
// For multi-resolution tracks allow for a 2X change in bpPerPixel
|
|
24308
|
+
const r = this.multiresolution ? this.bpPerPixel / bpPerPixel : 1;
|
|
24309
|
+
return start >= this.startBP && end <= this.endBP && chr === this.chr && r > 0.5 && r < 2;
|
|
24310
|
+
}
|
|
24337
24311
|
|
|
24338
|
-
|
|
24339
|
-
|
|
24340
|
-
|
|
24312
|
+
overlapsRange(chr, start, end) {
|
|
24313
|
+
return this.chr === chr && end >= this.startBP && start <= this.endBP;
|
|
24314
|
+
}
|
|
24315
|
+
|
|
24316
|
+
}
|
|
24341
24317
|
/**
|
|
24342
24318
|
* Merge 2 arrays. a and/or b can be undefined. If both are undefined, return undefined
|
|
24343
24319
|
* @param a An array or undefined
|
|
@@ -24476,10 +24452,12 @@
|
|
|
24476
24452
|
end: bp(this.rulerViewport.referenceFrame, left + width)
|
|
24477
24453
|
};
|
|
24478
24454
|
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.
|
|
24455
|
+
const newStart = Math.round(extent.start);
|
|
24456
|
+
const newEnd = Math.round(extent.end);
|
|
24457
|
+
this.rulerViewport.referenceFrame.bpPerPixel = (newEnd - newStart) / this.rulerViewport.contentDiv.clientWidth;
|
|
24458
|
+
this.rulerViewport.referenceFrame.start = newStart;
|
|
24459
|
+
this.rulerViewport.referenceFrame.end = newEnd;
|
|
24460
|
+
this.rulerViewport.browser.updateViews();
|
|
24483
24461
|
}
|
|
24484
24462
|
}
|
|
24485
24463
|
}
|
|
@@ -25481,6 +25459,18 @@
|
|
|
25481
25459
|
return true; // By definition
|
|
25482
25460
|
}
|
|
25483
25461
|
|
|
25462
|
+
isMateMapped() {
|
|
25463
|
+
return true; // By definition
|
|
25464
|
+
}
|
|
25465
|
+
|
|
25466
|
+
isProperPair() {
|
|
25467
|
+
return this.firstAlignment.isProperPair();
|
|
25468
|
+
}
|
|
25469
|
+
|
|
25470
|
+
get fragmentLength() {
|
|
25471
|
+
return Math.abs(this.firstAlignment.fragmentLength);
|
|
25472
|
+
}
|
|
25473
|
+
|
|
25484
25474
|
firstOfPairStrand() {
|
|
25485
25475
|
if (this.firstAlignment.isFirstOfPair()) {
|
|
25486
25476
|
return this.firstAlignment.strand;
|
|
@@ -25491,6 +25481,10 @@
|
|
|
25491
25481
|
}
|
|
25492
25482
|
}
|
|
25493
25483
|
|
|
25484
|
+
hasTag(str) {
|
|
25485
|
+
return this.firstAlignment.hasTag(str) || this.secondAlignment && this.secondAlignment.hasTag(str);
|
|
25486
|
+
}
|
|
25487
|
+
|
|
25494
25488
|
}
|
|
25495
25489
|
|
|
25496
25490
|
/*
|
|
@@ -25847,7 +25841,15 @@
|
|
|
25847
25841
|
*/
|
|
25848
25842
|
|
|
25849
25843
|
class AlignmentContainer {
|
|
25850
|
-
|
|
25844
|
+
// this.config.samplingWindowSize, this.config.samplingDepth,
|
|
25845
|
+
// this.config.pairsSupported, this.config.alleleFreqThreshold)
|
|
25846
|
+
constructor(chr, start, end, _ref) {
|
|
25847
|
+
let {
|
|
25848
|
+
samplingWindowSize,
|
|
25849
|
+
samplingDepth,
|
|
25850
|
+
pairsSupported,
|
|
25851
|
+
alleleFreqThreshold
|
|
25852
|
+
} = _ref;
|
|
25851
25853
|
this.chr = chr;
|
|
25852
25854
|
this.start = Math.floor(start);
|
|
25853
25855
|
this.end = Math.ceil(end);
|
|
@@ -25870,17 +25872,10 @@
|
|
|
25870
25872
|
// TODO -- pass this in
|
|
25871
25873
|
return alignment.isMapped() && !alignment.isFailsVendorQualityCheck();
|
|
25872
25874
|
};
|
|
25873
|
-
|
|
25874
|
-
this.pairedEndStats = new PairedEndStats();
|
|
25875
25875
|
}
|
|
25876
25876
|
|
|
25877
25877
|
push(alignment) {
|
|
25878
25878
|
if (this.filter(alignment) === false) return;
|
|
25879
|
-
|
|
25880
|
-
if (alignment.isPaired()) {
|
|
25881
|
-
this.pairedEndStats.push(alignment);
|
|
25882
|
-
}
|
|
25883
|
-
|
|
25884
25879
|
this.coverageMap.incCounts(alignment); // Count coverage before any downsampling
|
|
25885
25880
|
|
|
25886
25881
|
if (this.pairsSupported && this.downsampledReads.has(alignment.readName)) {
|
|
@@ -25909,7 +25904,6 @@
|
|
|
25909
25904
|
});
|
|
25910
25905
|
this.pairsCache = undefined;
|
|
25911
25906
|
this.downsampledReads = undefined;
|
|
25912
|
-
this.pairedEndStats.compute();
|
|
25913
25907
|
}
|
|
25914
25908
|
|
|
25915
25909
|
contains(chr, start, end) {
|
|
@@ -26204,62 +26198,6 @@
|
|
|
26204
26198
|
|
|
26205
26199
|
}
|
|
26206
26200
|
|
|
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
26201
|
class SupplementaryAlignment {
|
|
26264
26202
|
constructor(rec) {
|
|
26265
26203
|
const tokens = rec.split(',');
|
|
@@ -27022,7 +26960,7 @@
|
|
|
27022
26960
|
const lseq = readInt(ba, offset + 20);
|
|
27023
26961
|
const mateChrIdx = readInt(ba, offset + 24);
|
|
27024
26962
|
const matePos = readInt(ba, offset + 28);
|
|
27025
|
-
const
|
|
26963
|
+
const fragmentLength = readInt(ba, offset + 32);
|
|
27026
26964
|
let readName = [];
|
|
27027
26965
|
|
|
27028
26966
|
for (let j = 0; j < nl - 1; ++j) {
|
|
@@ -27063,7 +27001,7 @@
|
|
|
27063
27001
|
alignment.readName = readName;
|
|
27064
27002
|
alignment.cigar = cigar;
|
|
27065
27003
|
alignment.lengthOnRef = lengthOnRef;
|
|
27066
|
-
alignment.fragmentLength =
|
|
27004
|
+
alignment.fragmentLength = fragmentLength;
|
|
27067
27005
|
alignment.mq = mq;
|
|
27068
27006
|
BamUtils.bam_tag2cigar(ba, blockEnd, p, lseq, alignment, cigarArray);
|
|
27069
27007
|
alignment.end = alignment.start + alignment.lengthOnRef;
|
|
@@ -27463,7 +27401,7 @@
|
|
|
27463
27401
|
const header = this.header;
|
|
27464
27402
|
const queryChr = header.chrAliasTable.hasOwnProperty(chr) ? header.chrAliasTable[chr] : chr;
|
|
27465
27403
|
const qAlignments = this.alignmentCache.queryFeatures(queryChr, bpStart, bpEnd);
|
|
27466
|
-
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.
|
|
27404
|
+
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config);
|
|
27467
27405
|
|
|
27468
27406
|
for (let a of qAlignments) {
|
|
27469
27407
|
alignmentContainer.push(a);
|
|
@@ -27490,13 +27428,13 @@
|
|
|
27490
27428
|
const alignments = [];
|
|
27491
27429
|
this.header = BamUtils.decodeBamHeader(data);
|
|
27492
27430
|
BamUtils.decodeBamRecords(data, this.header.size, alignments, this.header.chrNames);
|
|
27493
|
-
this.alignmentCache = new FeatureCache(alignments, this.genome);
|
|
27431
|
+
this.alignmentCache = new FeatureCache$1(alignments, this.genome);
|
|
27494
27432
|
}
|
|
27495
27433
|
|
|
27496
27434
|
fetchAlignments(chr, bpStart, bpEnd) {
|
|
27497
27435
|
const queryChr = this.header.chrAliasTable.hasOwnProperty(chr) ? this.header.chrAliasTable[chr] : chr;
|
|
27498
27436
|
const features = this.alignmentCache.queryFeatures(queryChr, bpStart, bpEnd);
|
|
27499
|
-
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.
|
|
27437
|
+
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config);
|
|
27500
27438
|
|
|
27501
27439
|
for (let feature of features) {
|
|
27502
27440
|
alignmentContainer.push(feature);
|
|
@@ -28496,7 +28434,7 @@
|
|
|
28496
28434
|
const chrToIndex = await this.getChrIndex();
|
|
28497
28435
|
const queryChr = this.chrAliasTable.hasOwnProperty(chr) ? this.chrAliasTable[chr] : chr;
|
|
28498
28436
|
const chrId = chrToIndex[queryChr];
|
|
28499
|
-
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config
|
|
28437
|
+
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config);
|
|
28500
28438
|
|
|
28501
28439
|
if (chrId === undefined) {
|
|
28502
28440
|
return alignmentContainer;
|
|
@@ -28645,7 +28583,7 @@
|
|
|
28645
28583
|
|
|
28646
28584
|
async readAlignments(chr, start, end) {
|
|
28647
28585
|
if (!this.bamReaders.hasOwnProperty(chr)) {
|
|
28648
|
-
return new AlignmentContainer(chr, start, end);
|
|
28586
|
+
return new AlignmentContainer(chr, start, end, this.config);
|
|
28649
28587
|
} else {
|
|
28650
28588
|
let reader = this.bamReaders[chr];
|
|
28651
28589
|
const a = await reader.readAlignments(chr, start, end);
|
|
@@ -28717,7 +28655,7 @@
|
|
|
28717
28655
|
return igvxhr.loadString(url, buildOptions(self.config)).then(function (sam) {
|
|
28718
28656
|
var alignmentContainer;
|
|
28719
28657
|
header.chrToIndex[queryChr];
|
|
28720
|
-
alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, self.
|
|
28658
|
+
alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, self.config);
|
|
28721
28659
|
BamUtils.decodeSamRecords(sam, alignmentContainer, queryChr, bpStart, bpEnd, self.filter);
|
|
28722
28660
|
return alignmentContainer;
|
|
28723
28661
|
});
|
|
@@ -28927,7 +28865,7 @@
|
|
|
28927
28865
|
|
|
28928
28866
|
const ba = unbgzf(compressedData.buffer);
|
|
28929
28867
|
const chrIdx = this.header.chrToIndex[chr];
|
|
28930
|
-
const alignmentContainer = new AlignmentContainer(chr, start, end, this.
|
|
28868
|
+
const alignmentContainer = new AlignmentContainer(chr, start, end, this.config);
|
|
28931
28869
|
BamUtils.decodeBamRecords(ba, this.header.size, alignmentContainer, this.header.chrNames, chrIdx, start, end);
|
|
28932
28870
|
alignmentContainer.finish();
|
|
28933
28871
|
return alignmentContainer;
|
|
@@ -44465,7 +44403,7 @@
|
|
|
44465
44403
|
const header = await this.getHeader();
|
|
44466
44404
|
const queryChr = header.chrAliasTable.hasOwnProperty(chr) ? header.chrAliasTable[chr] : chr;
|
|
44467
44405
|
const chrIdx = header.chrToIndex[queryChr];
|
|
44468
|
-
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.
|
|
44406
|
+
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config);
|
|
44469
44407
|
|
|
44470
44408
|
if (chrIdx === undefined) {
|
|
44471
44409
|
return alignmentContainer;
|
|
@@ -45074,7 +45012,7 @@
|
|
|
45074
45012
|
"pageSize": "10000"
|
|
45075
45013
|
},
|
|
45076
45014
|
decode: decodeGa4ghReads,
|
|
45077
|
-
results: new AlignmentContainer(chr, bpStart, bpEnd, self.
|
|
45015
|
+
results: new AlignmentContainer(chr, bpStart, bpEnd, self.config)
|
|
45078
45016
|
});
|
|
45079
45017
|
});
|
|
45080
45018
|
|
|
@@ -45381,7 +45319,6 @@
|
|
|
45381
45319
|
const genome = browser.genome;
|
|
45382
45320
|
this.config = config;
|
|
45383
45321
|
this.genome = genome;
|
|
45384
|
-
this.alignmentContainer = undefined;
|
|
45385
45322
|
|
|
45386
45323
|
if (isDataURL(config.url)) {
|
|
45387
45324
|
if ("cram" === config.format) {
|
|
@@ -45432,58 +45369,44 @@
|
|
|
45432
45369
|
}
|
|
45433
45370
|
|
|
45434
45371
|
setViewAsPairs(bool) {
|
|
45435
|
-
|
|
45436
|
-
if (this.viewAsPairs !== bool) {
|
|
45437
|
-
this.viewAsPairs = bool; // if (this.alignmentContainer) {
|
|
45438
|
-
// this.alignmentContainer.setViewAsPairs(bool);
|
|
45439
|
-
// }
|
|
45440
|
-
}
|
|
45372
|
+
this.viewAsPairs = bool;
|
|
45441
45373
|
}
|
|
45442
45374
|
|
|
45443
45375
|
setShowSoftClips(bool) {
|
|
45444
|
-
|
|
45445
|
-
this.showSoftClips = bool;
|
|
45446
|
-
}
|
|
45376
|
+
this.showSoftClips = bool;
|
|
45447
45377
|
}
|
|
45448
45378
|
|
|
45449
45379
|
async getAlignments(chr, bpStart, bpEnd) {
|
|
45450
45380
|
const genome = this.genome;
|
|
45451
45381
|
const showSoftClips = this.showSoftClips;
|
|
45382
|
+
const alignmentContainer = await this.bamReader.readAlignments(chr, bpStart, bpEnd);
|
|
45383
|
+
let alignments = alignmentContainer.alignments;
|
|
45452
45384
|
|
|
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
|
|
45385
|
+
if (!this.viewAsPairs) {
|
|
45386
|
+
alignments = unpairAlignments([{
|
|
45387
|
+
alignments: alignments
|
|
45388
|
+
}]);
|
|
45389
|
+
}
|
|
45468
45390
|
|
|
45469
|
-
|
|
45391
|
+
const hasAlignments = alignments.length > 0;
|
|
45392
|
+
alignmentContainer.packedAlignmentRows = packAlignmentRows(alignments, alignmentContainer.start, alignmentContainer.end, showSoftClips);
|
|
45393
|
+
this.alignmentContainer = alignmentContainer;
|
|
45470
45394
|
|
|
45471
|
-
|
|
45472
|
-
|
|
45395
|
+
if (hasAlignments) {
|
|
45396
|
+
const sequence = await genome.sequence.getSequence(chr, alignmentContainer.start, alignmentContainer.end);
|
|
45473
45397
|
|
|
45474
|
-
|
|
45475
|
-
|
|
45398
|
+
if (sequence) {
|
|
45399
|
+
alignmentContainer.coverageMap.refSeq = sequence; // TODO -- fix this
|
|
45476
45400
|
|
|
45477
|
-
|
|
45401
|
+
alignmentContainer.sequence = sequence; // TODO -- fix this
|
|
45478
45402
|
|
|
45479
|
-
|
|
45480
|
-
|
|
45481
|
-
|
|
45482
|
-
}
|
|
45403
|
+
return alignmentContainer;
|
|
45404
|
+
} else {
|
|
45405
|
+
console.error("No sequence for: " + chr + ":" + alignmentContainer.start + "-" + alignmentContainer.end);
|
|
45483
45406
|
}
|
|
45484
|
-
|
|
45485
|
-
return alignmentContainer;
|
|
45486
45407
|
}
|
|
45408
|
+
|
|
45409
|
+
return alignmentContainer;
|
|
45487
45410
|
}
|
|
45488
45411
|
|
|
45489
45412
|
}
|
|
@@ -45512,6 +45435,10 @@
|
|
|
45512
45435
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
45513
45436
|
* THE SOFTWARE.
|
|
45514
45437
|
*/
|
|
45438
|
+
|
|
45439
|
+
const fixColor = colorString => {
|
|
45440
|
+
return colorString.indexOf(",") > 0 && !colorString.startsWith("rgb") ? `rgb(${colorString})` : colorString;
|
|
45441
|
+
};
|
|
45515
45442
|
/**
|
|
45516
45443
|
* A collection of properties and methods shared by all (or most) track types.
|
|
45517
45444
|
*
|
|
@@ -45520,6 +45447,7 @@
|
|
|
45520
45447
|
* @constructor
|
|
45521
45448
|
*/
|
|
45522
45449
|
|
|
45450
|
+
|
|
45523
45451
|
class TrackBase {
|
|
45524
45452
|
constructor(config, browser) {
|
|
45525
45453
|
this.browser = browser;
|
|
@@ -45553,8 +45481,8 @@
|
|
|
45553
45481
|
|
|
45554
45482
|
this.id = this.config.id === undefined ? this.name : this.config.id;
|
|
45555
45483
|
this.order = config.order;
|
|
45556
|
-
this.color = config.color;
|
|
45557
|
-
this.altColor = config.altColor;
|
|
45484
|
+
if (config.color) this.color = fixColor(config.color);
|
|
45485
|
+
if (config.altColor) this.altColor = fixColor(config.altColor);
|
|
45558
45486
|
|
|
45559
45487
|
if ("civic-ws" === config.sourceType) {
|
|
45560
45488
|
// Ugly proxy for specialized track type
|
|
@@ -45667,7 +45595,7 @@
|
|
|
45667
45595
|
return state;
|
|
45668
45596
|
}
|
|
45669
45597
|
|
|
45670
|
-
supportsWholeGenome() {
|
|
45598
|
+
get supportsWholeGenome() {
|
|
45671
45599
|
return false;
|
|
45672
45600
|
}
|
|
45673
45601
|
/**
|
|
@@ -45828,7 +45756,7 @@
|
|
|
45828
45756
|
clickedFeatures(clickState, features) {
|
|
45829
45757
|
// We use the cached features rather than method to avoid async load. If the
|
|
45830
45758
|
// 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.
|
|
45759
|
+
if (!features) features = clickState.viewport.cachedFeatures;
|
|
45832
45760
|
|
|
45833
45761
|
if (!features || features.length === 0) {
|
|
45834
45762
|
return [];
|
|
@@ -47758,7 +47686,7 @@
|
|
|
47758
47686
|
return this.tracks.find(t => name === t.name);
|
|
47759
47687
|
}
|
|
47760
47688
|
|
|
47761
|
-
|
|
47689
|
+
getChordSet(name) {
|
|
47762
47690
|
return this.chordSets.find(cs => name === cs.name);
|
|
47763
47691
|
}
|
|
47764
47692
|
|
|
@@ -47868,9 +47796,9 @@
|
|
|
47868
47796
|
buttonContainer.appendChild(this.showControlsButton);
|
|
47869
47797
|
this.showControlsButton.innerText = 'none' === this.controlPanel.style.display ? 'Show Controls' : 'Hide Controls';
|
|
47870
47798
|
this.showControlsButton.addEventListener('click', event => {
|
|
47871
|
-
const
|
|
47799
|
+
const panelRows = this.controlPanel.querySelectorAll('div');
|
|
47872
47800
|
|
|
47873
|
-
if (
|
|
47801
|
+
if (panelRows.length > 0) {
|
|
47874
47802
|
if ('none' === this.controlPanel.style.display) {
|
|
47875
47803
|
this.controlPanel.style.display = 'flex';
|
|
47876
47804
|
event.target.innerText = 'Hide Controls';
|
|
@@ -47950,10 +47878,10 @@
|
|
|
47950
47878
|
hideShowButton.innerText = true === chordSet.visible ? 'Hide' : 'Show';
|
|
47951
47879
|
hideShowButton.addEventListener('click', event => {
|
|
47952
47880
|
if (true === chordSet.visible) {
|
|
47953
|
-
this.
|
|
47881
|
+
this.hideChordSet(chordSet.name);
|
|
47954
47882
|
event.target.innerText = "Show";
|
|
47955
47883
|
} else {
|
|
47956
|
-
this.
|
|
47884
|
+
this.showChordSet(chordSet.name);
|
|
47957
47885
|
event.target.innerText = "Hide";
|
|
47958
47886
|
}
|
|
47959
47887
|
}); // The alpha range slider. Create this here so we can reference it from the color picker
|
|
@@ -47980,7 +47908,7 @@
|
|
|
47980
47908
|
rgbaString
|
|
47981
47909
|
}) => {
|
|
47982
47910
|
colorPickerButton.style.backgroundColor = setAlpha(rgbaString, 1);
|
|
47983
|
-
this.
|
|
47911
|
+
this.setColor(chordSet.name, rgbaString);
|
|
47984
47912
|
alphaSlider.value = alphaToValue(getAlpha(chordSet.color));
|
|
47985
47913
|
}
|
|
47986
47914
|
};
|
|
@@ -47998,7 +47926,7 @@
|
|
|
47998
47926
|
|
|
47999
47927
|
alphaSlider.oninput = () => {
|
|
48000
47928
|
const v = valueToAlpha(alphaSlider.value);
|
|
48001
|
-
this.
|
|
47929
|
+
this.setColor(chordSet.name, setAlpha(chordSet.color, v));
|
|
48002
47930
|
picker.setColor(chordSet.color);
|
|
48003
47931
|
};
|
|
48004
47932
|
|
|
@@ -48017,12 +47945,14 @@
|
|
|
48017
47945
|
|
|
48018
47946
|
|
|
48019
47947
|
setAssembly(igvGenome) {
|
|
48020
|
-
|
|
47948
|
+
const id = this.genomeId || guid();
|
|
47949
|
+
|
|
47950
|
+
if (this.genomeId === id) {
|
|
48021
47951
|
return;
|
|
48022
47952
|
}
|
|
48023
47953
|
|
|
48024
47954
|
this.chordManager.clearChords();
|
|
48025
|
-
this.genomeId =
|
|
47955
|
+
this.genomeId = id;
|
|
48026
47956
|
this.chrNames = new Set(igvGenome.chromosomes.map(chr => shortChrName$1(chr.name)));
|
|
48027
47957
|
const regions = [];
|
|
48028
47958
|
const colors = [];
|
|
@@ -48041,7 +47971,7 @@
|
|
|
48041
47971
|
this.assembly = {
|
|
48042
47972
|
name: igvGenome.name,
|
|
48043
47973
|
sequence: {
|
|
48044
|
-
trackId:
|
|
47974
|
+
trackId: id,
|
|
48045
47975
|
type: 'ReferenceSequenceTrack',
|
|
48046
47976
|
adapter: {
|
|
48047
47977
|
type: 'FromConfigSequenceAdapter',
|
|
@@ -48079,7 +48009,7 @@
|
|
|
48079
48009
|
|
|
48080
48010
|
|
|
48081
48011
|
addChords(newChords, options = {}) {
|
|
48082
|
-
const tmp = options.
|
|
48012
|
+
const tmp = options.name || options.track || "*";
|
|
48083
48013
|
const trackName = tmp.split(' ')[0].replaceAll("%20", " ");
|
|
48084
48014
|
const chordSetName = tmp.replaceAll("%20", " ");
|
|
48085
48015
|
const chordSet = {
|
|
@@ -48129,20 +48059,6 @@
|
|
|
48129
48059
|
clearSelection() {
|
|
48130
48060
|
this.viewState.pluginManager.rootModel.session.clearSelection();
|
|
48131
48061
|
}
|
|
48132
|
-
|
|
48133
|
-
getFeature(featureId) {
|
|
48134
|
-
// TODO -- broken
|
|
48135
|
-
// const display = this.viewState.pluginManager.rootModel.session.view.tracks[0].displays[0]
|
|
48136
|
-
// const feature = display.data.features.get(featureId)
|
|
48137
|
-
// return feature;
|
|
48138
|
-
const features = [...this.viewState.config.tracks[0].adapter.features.value];
|
|
48139
|
-
|
|
48140
|
-
for (let f of features) {
|
|
48141
|
-
if (featureId === f.uniqueId) {
|
|
48142
|
-
return f;
|
|
48143
|
-
}
|
|
48144
|
-
}
|
|
48145
|
-
}
|
|
48146
48062
|
/**
|
|
48147
48063
|
* Deprecated, use "visible" property
|
|
48148
48064
|
*/
|
|
@@ -48168,25 +48084,25 @@
|
|
|
48168
48084
|
this.parent.style.display = isVisible ? 'block' : 'none';
|
|
48169
48085
|
}
|
|
48170
48086
|
|
|
48171
|
-
|
|
48172
|
-
let
|
|
48087
|
+
hideChordSet(trackName) {
|
|
48088
|
+
let cs = this.getChordSet(trackName);
|
|
48173
48089
|
|
|
48174
|
-
if (
|
|
48175
|
-
|
|
48090
|
+
if (cs) {
|
|
48091
|
+
cs.visible = false;
|
|
48176
48092
|
this.render();
|
|
48177
48093
|
} else {
|
|
48178
48094
|
console.warn(`No track with name: ${name}`);
|
|
48179
48095
|
}
|
|
48180
48096
|
}
|
|
48181
48097
|
|
|
48182
|
-
|
|
48183
|
-
let
|
|
48098
|
+
showChordSet(name) {
|
|
48099
|
+
let cs = this.getChordSet(name);
|
|
48184
48100
|
|
|
48185
|
-
if (
|
|
48186
|
-
|
|
48101
|
+
if (cs) {
|
|
48102
|
+
cs.visible = true;
|
|
48187
48103
|
this.render();
|
|
48188
48104
|
} else {
|
|
48189
|
-
console.warn(`No track with name: ${
|
|
48105
|
+
console.warn(`No track with name: ${name}`);
|
|
48190
48106
|
}
|
|
48191
48107
|
} // showTrack(trackID) {
|
|
48192
48108
|
// let idx = this.tracks.findIndex(t => trackID === t.id)
|
|
@@ -48213,12 +48129,12 @@
|
|
|
48213
48129
|
this.render();
|
|
48214
48130
|
}
|
|
48215
48131
|
|
|
48216
|
-
|
|
48217
|
-
return this.groupByTrack ? this.chordManager.getTrack(name) : this.chordManager.
|
|
48132
|
+
getChordSet(name) {
|
|
48133
|
+
return this.groupByTrack ? this.chordManager.getTrack(name) : this.chordManager.getChordSet(name);
|
|
48218
48134
|
}
|
|
48219
48135
|
|
|
48220
|
-
|
|
48221
|
-
const t = this.
|
|
48136
|
+
setColor(name, color) {
|
|
48137
|
+
const t = this.getChordSet(name);
|
|
48222
48138
|
|
|
48223
48139
|
if (t) {
|
|
48224
48140
|
t.color = color;
|
|
@@ -48319,7 +48235,7 @@
|
|
|
48319
48235
|
}
|
|
48320
48236
|
|
|
48321
48237
|
function embedCSS$1() {
|
|
48322
|
-
const css = '.igv-circview-container {\n z-index: 2048;\n
|
|
48238
|
+
const css = '.igv-circview-container {\n z-index: 2048;\n width: fit-content;\n height: fit-content;\n box-sizing: content-box;\n color: dimgray;\n font-family: \"Open Sans\", sans-serif;\n font-size: 12px;\n background-color: white;\n border-color: dimgray;\n border-style: solid;\n border-width: thin;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n}\n\n.igv-circview-toolbar {\n position: relative;\n width: 100%;\n height: 32px;\n background-color: lightgrey;\n border-bottom-style: solid;\n border-bottom-color: dimgray;\n border-bottom-width: thin;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n}\n\n.igv-circview-toolbar-button-container {\n height: 100%;\n width: fit-content;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-circview-toolbar-button-container > div {\n margin: 4px;\n}\n\n.igv-circview-track-panel {\n z-index: 1024;\n position: absolute;\n top: 33px;\n left: 0;\n width: 100%;\n height: fit-content;\n border-bottom-style: solid;\n border-bottom-color: dimgray;\n border-bottom-width: thin;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n}\n.igv-circview-track-panel > div {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-circview-track-panel > div > div {\n margin: 4px;\n}\n\n.igv-circview-swatch-button {\n cursor: pointer;\n padding: 5px;\n width: 8px;\n height: 8px;\n border: 1px solid #8d8b8b;\n border-radius: 16px;\n}\n\n.igv-circview-button {\n cursor: pointer;\n padding: 5px;\n color: #444;\n vertical-align: middle;\n text-align: center;\n font-family: \"Open Sans\", sans-serif;\n font-size: 12px;\n border: 1px solid #8d8b8b;\n border-radius: 4px;\n background: #efefef;\n box-shadow: 0 0 5px -1px rgba(0, 0, 0, 0.2);\n}\n\n.igv-circview-button:hover {\n background: #efefef;\n box-shadow: 0 0 5px -1px rgba(0, 0, 0, 0.6);\n}\n\n.igv-circview-button:active {\n color: #007bff;\n box-shadow: 0 0 5px -1px rgba(0, 0, 0, 0.6);\n}\n\n/*# sourceMappingURL=circular-view.css.map */\n';
|
|
48323
48239
|
const style = document.createElement('style');
|
|
48324
48240
|
style.setAttribute('type', 'text/css');
|
|
48325
48241
|
style.innerHTML = css;
|
|
@@ -48359,20 +48275,36 @@
|
|
|
48359
48275
|
const chords = [];
|
|
48360
48276
|
|
|
48361
48277
|
for (let a of alignments) {
|
|
48362
|
-
|
|
48363
|
-
|
|
48364
|
-
|
|
48365
|
-
|
|
48366
|
-
|
|
48367
|
-
|
|
48368
|
-
|
|
48369
|
-
|
|
48370
|
-
|
|
48371
|
-
|
|
48372
|
-
|
|
48373
|
-
|
|
48374
|
-
}
|
|
48375
|
-
}
|
|
48278
|
+
if (a.paired) {
|
|
48279
|
+
if (a.firstAlignment && a.secondAlignment) {
|
|
48280
|
+
chords.push({
|
|
48281
|
+
uniqueId: a.readName,
|
|
48282
|
+
refName: shortChrName(a.firstAlignment.chr),
|
|
48283
|
+
start: a.firstAlignment.start,
|
|
48284
|
+
end: a.firstAlignment.end,
|
|
48285
|
+
mate: {
|
|
48286
|
+
refName: shortChrName(a.secondAlignment.chr),
|
|
48287
|
+
start: a.secondAlignment.start,
|
|
48288
|
+
end: a.secondAlignment.end
|
|
48289
|
+
}
|
|
48290
|
+
});
|
|
48291
|
+
}
|
|
48292
|
+
} else {
|
|
48293
|
+
const mate = a.mate;
|
|
48294
|
+
|
|
48295
|
+
if (mate && mate.chr && mate.position) {
|
|
48296
|
+
chords.push({
|
|
48297
|
+
uniqueId: a.readName,
|
|
48298
|
+
refName: shortChrName(a.chr),
|
|
48299
|
+
start: a.start,
|
|
48300
|
+
end: a.end,
|
|
48301
|
+
mate: {
|
|
48302
|
+
refName: shortChrName(mate.chr),
|
|
48303
|
+
start: mate.position - 1,
|
|
48304
|
+
end: mate.position
|
|
48305
|
+
}
|
|
48306
|
+
});
|
|
48307
|
+
}
|
|
48376
48308
|
}
|
|
48377
48309
|
}
|
|
48378
48310
|
|
|
@@ -48380,9 +48312,7 @@
|
|
|
48380
48312
|
};
|
|
48381
48313
|
|
|
48382
48314
|
const makeSupplementalAlignmentChords = alignments => {
|
|
48383
|
-
const
|
|
48384
|
-
|
|
48385
|
-
for (let a of alignments) {
|
|
48315
|
+
const makeChords = a => {
|
|
48386
48316
|
const sa = a.tags()['SA'];
|
|
48387
48317
|
const supAl = createSupplementaryAlignments(sa);
|
|
48388
48318
|
let n = 0;
|
|
@@ -48402,6 +48332,20 @@
|
|
|
48402
48332
|
});
|
|
48403
48333
|
}
|
|
48404
48334
|
}
|
|
48335
|
+
};
|
|
48336
|
+
|
|
48337
|
+
const chords = [];
|
|
48338
|
+
|
|
48339
|
+
for (let a of alignments) {
|
|
48340
|
+
if (a.paired) {
|
|
48341
|
+
makeChords(a.firstAlignment);
|
|
48342
|
+
|
|
48343
|
+
if (a.secondAlignment) {
|
|
48344
|
+
makeChords(a.secondAlignment);
|
|
48345
|
+
}
|
|
48346
|
+
} else {
|
|
48347
|
+
makeChords(a);
|
|
48348
|
+
}
|
|
48405
48349
|
}
|
|
48406
48350
|
|
|
48407
48351
|
return chords;
|
|
@@ -48467,52 +48411,125 @@
|
|
|
48467
48411
|
return regions;
|
|
48468
48412
|
}
|
|
48469
48413
|
|
|
48414
|
+
function sendChords(chords, track, refFrame, alpha) {
|
|
48415
|
+
const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? track.color : getChrColor(refFrame.chr), alpha);
|
|
48416
|
+
const trackColor = IGVColor.addAlpha(track.color || 'rgb(0,0,255)', alpha); // name the chord set to include locus and filtering information
|
|
48417
|
+
|
|
48418
|
+
const encodedName = track.name.replaceAll(' ', '%20');
|
|
48419
|
+
const chordSetName = "all" === refFrame.chr ? encodedName : `${encodedName} ${refFrame.chr}:${refFrame.start}-${refFrame.end}`;
|
|
48420
|
+
track.browser.circularView.addChords(chords, {
|
|
48421
|
+
track: chordSetName,
|
|
48422
|
+
color: chordSetColor,
|
|
48423
|
+
trackColor: trackColor
|
|
48424
|
+
}); // show circular view if hidden
|
|
48425
|
+
|
|
48426
|
+
if (!track.browser.circularViewVisible) track.browser.circularViewVisible = true;
|
|
48427
|
+
}
|
|
48428
|
+
|
|
48470
48429
|
function createCircularView(el, browser) {
|
|
48471
48430
|
const circularView = new CircularView(el, {
|
|
48472
48431
|
onChordClick: (feature, chordTrack, pluginManager) => {
|
|
48473
48432
|
const f1 = feature.data;
|
|
48474
48433
|
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);
|
|
48434
|
+
addFrameForFeature(f1);
|
|
48435
|
+
addFrameForFeature(f2);
|
|
48436
|
+
|
|
48437
|
+
function addFrameForFeature(feature) {
|
|
48438
|
+
feature.chr = browser.genome.getChromosomeName(feature.refName);
|
|
48439
|
+
let frameFound = false;
|
|
48440
|
+
|
|
48441
|
+
for (let referenceFrame of browser.referenceFrameList) {
|
|
48442
|
+
const l = Locus.fromLocusString(referenceFrame.getLocusString());
|
|
48443
|
+
|
|
48444
|
+
if (l.contains(feature)) {
|
|
48445
|
+
frameFound = true;
|
|
48446
|
+
break;
|
|
48447
|
+
} else if (l.overlaps(feature)) {
|
|
48448
|
+
referenceFrame.extend(feature);
|
|
48449
|
+
frameFound = true;
|
|
48450
|
+
break;
|
|
48499
48451
|
}
|
|
48500
48452
|
}
|
|
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
48453
|
|
|
48509
|
-
|
|
48510
|
-
|
|
48454
|
+
if (!frameFound) {
|
|
48455
|
+
const flanking = 2000;
|
|
48456
|
+
const center = (feature.start + feature.end) / 2;
|
|
48457
|
+
browser.addMultiLocusPanel(feature.chr, center - flanking, center + flanking);
|
|
48458
|
+
}
|
|
48459
|
+
}
|
|
48511
48460
|
}
|
|
48512
48461
|
});
|
|
48513
48462
|
return circularView;
|
|
48514
48463
|
}
|
|
48515
48464
|
|
|
48465
|
+
class PairedEndStats {
|
|
48466
|
+
constructor(alignments, _ref) {
|
|
48467
|
+
let {
|
|
48468
|
+
minTLENPercentile,
|
|
48469
|
+
maxTLENPercentile
|
|
48470
|
+
} = _ref;
|
|
48471
|
+
this.totalCount = 0;
|
|
48472
|
+
this.frCount = 0;
|
|
48473
|
+
this.rfCount = 0;
|
|
48474
|
+
this.ffCount = 0;
|
|
48475
|
+
this.sumF = 0;
|
|
48476
|
+
this.sumF2 = 0;
|
|
48477
|
+
this.lp = minTLENPercentile === undefined ? 0.1 : minTLENPercentile;
|
|
48478
|
+
this.up = maxTLENPercentile === undefined ? 99.5 : maxTLENPercentile;
|
|
48479
|
+
this.isizes = [];
|
|
48480
|
+
this.compute(alignments);
|
|
48481
|
+
}
|
|
48482
|
+
|
|
48483
|
+
compute(alignments) {
|
|
48484
|
+
for (let alignment of alignments) {
|
|
48485
|
+
if (alignment.isProperPair()) {
|
|
48486
|
+
var tlen = Math.abs(alignment.fragmentLength);
|
|
48487
|
+
this.sumF += tlen;
|
|
48488
|
+
this.sumF2 += tlen * tlen;
|
|
48489
|
+
this.isizes.push(tlen);
|
|
48490
|
+
var po = alignment.pairOrientation;
|
|
48491
|
+
|
|
48492
|
+
if (typeof po === "string" && po.length === 4) {
|
|
48493
|
+
var tmp = '' + po.charAt(0) + po.charAt(2);
|
|
48494
|
+
|
|
48495
|
+
switch (tmp) {
|
|
48496
|
+
case 'FF':
|
|
48497
|
+
case 'RR':
|
|
48498
|
+
this.ffCount++;
|
|
48499
|
+
break;
|
|
48500
|
+
|
|
48501
|
+
case "FR":
|
|
48502
|
+
this.frCount++;
|
|
48503
|
+
break;
|
|
48504
|
+
|
|
48505
|
+
case "RF":
|
|
48506
|
+
this.rfCount++;
|
|
48507
|
+
}
|
|
48508
|
+
}
|
|
48509
|
+
|
|
48510
|
+
this.totalCount++;
|
|
48511
|
+
}
|
|
48512
|
+
}
|
|
48513
|
+
|
|
48514
|
+
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";
|
|
48515
|
+
this.minTLEN = this.lp === 0 ? 0 : percentile(this.isizes, this.lp);
|
|
48516
|
+
this.maxTLEN = percentile(this.isizes, this.up); // var fMean = this.sumF / this.totalCount
|
|
48517
|
+
// var stdDev = Math.sqrt((this.totalCount * this.sumF2 - this.sumF * this.sumF) / (this.totalCount * this.totalCount))
|
|
48518
|
+
// this.minTLEN = fMean - 3 * stdDev
|
|
48519
|
+
// this.maxTLEN = fMean + 3 * stdDev
|
|
48520
|
+
}
|
|
48521
|
+
|
|
48522
|
+
}
|
|
48523
|
+
|
|
48524
|
+
function percentile(array, p) {
|
|
48525
|
+
if (array.length === 0) return undefined;
|
|
48526
|
+
var k = Math.floor(array.length * (p / 100));
|
|
48527
|
+
array.sort(function (a, b) {
|
|
48528
|
+
return a - b;
|
|
48529
|
+
});
|
|
48530
|
+
return array[k];
|
|
48531
|
+
}
|
|
48532
|
+
|
|
48516
48533
|
/*
|
|
48517
48534
|
* The MIT License (MIT)
|
|
48518
48535
|
*
|
|
@@ -48572,10 +48589,7 @@
|
|
|
48572
48589
|
this.showInsertions = false !== config.showInsertions;
|
|
48573
48590
|
this.showMismatches = false !== config.showMismatches;
|
|
48574
48591
|
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
|
|
48592
|
+
this.coverageColor = config.coverageColor; // The sort object can be an array in the case of multi-locus view, however if multiple sort positions
|
|
48579
48593
|
// are present for a given reference frame the last one will take precedence
|
|
48580
48594
|
|
|
48581
48595
|
if (config.sort) {
|
|
@@ -48603,12 +48617,22 @@
|
|
|
48603
48617
|
return this._height;
|
|
48604
48618
|
}
|
|
48605
48619
|
|
|
48620
|
+
get minTemplateLength() {
|
|
48621
|
+
const configMinTLEN = this.config.minTLEN !== undefined ? this.config.minTLEN : this.config.minFragmentLength;
|
|
48622
|
+
return configMinTLEN !== undefined ? configMinTLEN : this._pairedEndStats ? this._pairedEndStats.minTLEN : 0;
|
|
48623
|
+
}
|
|
48624
|
+
|
|
48625
|
+
get maxTemplateLength() {
|
|
48626
|
+
const configMaxTLEN = this.config.maxTLEN !== undefined ? this.config.maxTLEN : this.config.maxFragmentLength;
|
|
48627
|
+
return configMaxTLEN !== undefined ? configMaxTLEN : this._pairedEndStats ? this._pairedEndStats.maxTLEN : 1000;
|
|
48628
|
+
}
|
|
48629
|
+
|
|
48606
48630
|
sort(options) {
|
|
48607
48631
|
options = this.assignSort(options);
|
|
48608
48632
|
|
|
48609
48633
|
for (let vp of this.trackView.viewports) {
|
|
48610
48634
|
if (vp.containsPosition(options.chr, options.position)) {
|
|
48611
|
-
const alignmentContainer = vp.
|
|
48635
|
+
const alignmentContainer = vp.cachedFeatures;
|
|
48612
48636
|
|
|
48613
48637
|
if (alignmentContainer) {
|
|
48614
48638
|
sortAlignmentRows(options, alignmentContainer);
|
|
@@ -48643,16 +48667,16 @@
|
|
|
48643
48667
|
async getFeatures(chr, bpStart, bpEnd, bpPerPixel, viewport) {
|
|
48644
48668
|
const alignmentContainer = await this.featureSource.getAlignments(chr, bpStart, bpEnd);
|
|
48645
48669
|
|
|
48646
|
-
if (alignmentContainer.
|
|
48647
|
-
|
|
48648
|
-
this.minFragmentLength = alignmentContainer.pairedEndStats.lowerFragmentLength;
|
|
48649
|
-
}
|
|
48670
|
+
if (alignmentContainer.paired && !this._pairedEndStats && !this.config.maxFragmentLength) {
|
|
48671
|
+
const pairedEndStats = new PairedEndStats(alignmentContainer.alignments, this.config);
|
|
48650
48672
|
|
|
48651
|
-
if (
|
|
48652
|
-
this.
|
|
48673
|
+
if (pairedEndStats.totalCount > 99) {
|
|
48674
|
+
this._pairedEndStats = pairedEndStats;
|
|
48653
48675
|
}
|
|
48654
48676
|
}
|
|
48655
48677
|
|
|
48678
|
+
alignmentContainer.alignments = undefined; // Don't need to hold onto these anymore
|
|
48679
|
+
|
|
48656
48680
|
const sort = this.sortObject;
|
|
48657
48681
|
|
|
48658
48682
|
if (sort) {
|
|
@@ -48757,7 +48781,7 @@
|
|
|
48757
48781
|
label: 'pair orientation'
|
|
48758
48782
|
});
|
|
48759
48783
|
colorByMenuItems.push({
|
|
48760
|
-
key: '
|
|
48784
|
+
key: 'tlen',
|
|
48761
48785
|
label: 'insert size (TLEN)'
|
|
48762
48786
|
});
|
|
48763
48787
|
colorByMenuItems.push({
|
|
@@ -48868,36 +48892,19 @@
|
|
|
48868
48892
|
this.trackView.repaintViews();
|
|
48869
48893
|
}
|
|
48870
48894
|
});
|
|
48871
|
-
} //
|
|
48895
|
+
} // Add chords to JBrowse circular view, if present
|
|
48872
48896
|
|
|
48873
48897
|
|
|
48874
|
-
if (this.browser.circularView &&
|
|
48898
|
+
if (this.browser.circularView && (this.alignmentTrack.hasPairs || this.alignmentTrack.hasSupplemental)) {
|
|
48875
48899
|
menuItems.push('<hr/>');
|
|
48876
48900
|
|
|
48877
48901
|
if (this.alignmentTrack.hasPairs) {
|
|
48878
48902
|
menuItems.push({
|
|
48879
48903
|
label: 'Add discordant pairs to circular view',
|
|
48880
48904
|
click: () => {
|
|
48881
|
-
const maxFragmentLength = this.maxFragmentLength;
|
|
48882
|
-
const inView = [];
|
|
48883
|
-
|
|
48884
48905
|
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
|
-
}
|
|
48906
|
+
this.addPairedChordsForViewport(viewport);
|
|
48892
48907
|
}
|
|
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
48908
|
}
|
|
48902
48909
|
});
|
|
48903
48910
|
}
|
|
@@ -48906,25 +48913,9 @@
|
|
|
48906
48913
|
menuItems.push({
|
|
48907
48914
|
label: 'Add split reads to circular view',
|
|
48908
48915
|
click: () => {
|
|
48909
|
-
const inView = [];
|
|
48910
|
-
|
|
48911
48916
|
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
|
-
}
|
|
48917
|
+
this.addSplitChordsForViewport(viewport);
|
|
48920
48918
|
}
|
|
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
48919
|
}
|
|
48929
48920
|
});
|
|
48930
48921
|
}
|
|
@@ -49043,7 +49034,7 @@
|
|
|
49043
49034
|
}
|
|
49044
49035
|
|
|
49045
49036
|
getCachedAlignmentContainers() {
|
|
49046
|
-
return this.trackView.viewports.map(vp => vp.
|
|
49037
|
+
return this.trackView.viewports.map(vp => vp.cachedFeatures);
|
|
49047
49038
|
}
|
|
49048
49039
|
|
|
49049
49040
|
get dataRange() {
|
|
@@ -49069,6 +49060,64 @@
|
|
|
49069
49060
|
set autoscale(autoscale) {
|
|
49070
49061
|
this.coverageTrack.autoscale = autoscale;
|
|
49071
49062
|
}
|
|
49063
|
+
/**
|
|
49064
|
+
* Add chords to the circular view for the given viewport, represented by its reference frame
|
|
49065
|
+
* @param refFrame
|
|
49066
|
+
*/
|
|
49067
|
+
|
|
49068
|
+
|
|
49069
|
+
addPairedChordsForViewport(viewport) {
|
|
49070
|
+
const maxTemplateLength = this.maxTemplateLength;
|
|
49071
|
+
const inView = [];
|
|
49072
|
+
const refFrame = viewport.referenceFrame;
|
|
49073
|
+
|
|
49074
|
+
for (let a of viewport.cachedFeatures.allAlignments()) {
|
|
49075
|
+
if (a.end >= refFrame.start && a.start <= refFrame.end) {
|
|
49076
|
+
if (a.paired) {
|
|
49077
|
+
if (a.end - a.start > maxTemplateLength) {
|
|
49078
|
+
inView.push(a);
|
|
49079
|
+
}
|
|
49080
|
+
} else {
|
|
49081
|
+
if (a.mate && a.mate.chr && (a.mate.chr !== a.chr || Math.max(a.fragmentLength) > maxTemplateLength)) {
|
|
49082
|
+
inView.push(a);
|
|
49083
|
+
}
|
|
49084
|
+
}
|
|
49085
|
+
}
|
|
49086
|
+
}
|
|
49087
|
+
|
|
49088
|
+
const chords = makePairedAlignmentChords(inView);
|
|
49089
|
+
sendChords(chords, this, refFrame, 0.02); // const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? this.color : getChrColor(refFrame.chr), 0.02)
|
|
49090
|
+
// const trackColor = IGVColor.addAlpha(this.color || 'rgb(0,0,255)', 0.02)
|
|
49091
|
+
//
|
|
49092
|
+
// // name the chord set to include track name and locus
|
|
49093
|
+
// const encodedName = this.name.replaceAll(' ', '%20')
|
|
49094
|
+
// const chordSetName = "all" === refFrame.chr ? encodedName :
|
|
49095
|
+
// `${encodedName} (${refFrame.chr}:${refFrame.start}-${refFrame.end}`
|
|
49096
|
+
// this.browser.circularView.addChords(chords, {name: chordSetName, color: chordSetColor, trackColor: trackColor})
|
|
49097
|
+
}
|
|
49098
|
+
|
|
49099
|
+
addSplitChordsForViewport(viewport) {
|
|
49100
|
+
const inView = [];
|
|
49101
|
+
const refFrame = viewport.referenceFrame;
|
|
49102
|
+
|
|
49103
|
+
for (let a of viewport.cachedFeatures.allAlignments()) {
|
|
49104
|
+
const sa = a.hasTag('SA');
|
|
49105
|
+
|
|
49106
|
+
if (a.end >= refFrame.start && a.start <= refFrame.end && sa) {
|
|
49107
|
+
inView.push(a);
|
|
49108
|
+
}
|
|
49109
|
+
}
|
|
49110
|
+
|
|
49111
|
+
const chords = makeSupplementalAlignmentChords(inView);
|
|
49112
|
+
sendChords(chords, this, refFrame, 0.02); // const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? this.color : getChrColor(refFrame.chr), 0.02)
|
|
49113
|
+
// const trackColor = IGVColor.addAlpha(this.color || 'rgb(0,0,255)', 0.02)
|
|
49114
|
+
//
|
|
49115
|
+
// // name the chord set to include track name and locus
|
|
49116
|
+
// const encodedName = this.name.replaceAll(' ', '%20')
|
|
49117
|
+
// const chordSetName = "all" === refFrame.chr ? encodedName :
|
|
49118
|
+
// `${encodedName} (${refFrame.chr}:${refFrame.start}-${refFrame.end}`
|
|
49119
|
+
// this.browser.circularView.addChords(chords, {name: chordSetName, color: chordSetColor, trackColor: trackColor})
|
|
49120
|
+
}
|
|
49072
49121
|
|
|
49073
49122
|
}
|
|
49074
49123
|
|
|
@@ -49179,7 +49228,7 @@
|
|
|
49179
49228
|
}
|
|
49180
49229
|
|
|
49181
49230
|
getClickedObject(clickState) {
|
|
49182
|
-
let features = clickState.viewport.
|
|
49231
|
+
let features = clickState.viewport.cachedFeatures;
|
|
49183
49232
|
if (!features || features.length === 0) return;
|
|
49184
49233
|
const genomicLocation = Math.floor(clickState.genomicLocation);
|
|
49185
49234
|
const coverageMap = features.coverageMap;
|
|
@@ -49265,14 +49314,14 @@
|
|
|
49265
49314
|
this.deletionColor = config.deletionColor || "black";
|
|
49266
49315
|
this.skippedColor = config.skippedColor || "rgb(150, 170, 170)";
|
|
49267
49316
|
this.pairConnectorColor = config.pairConnectorColor;
|
|
49268
|
-
this.
|
|
49269
|
-
this.
|
|
49317
|
+
this.smallTLENColor = config.smallTLENColor || config.smallFragmentLengthColor || "rgb(0, 0, 150)";
|
|
49318
|
+
this.largeTLENColor = config.largeTLENColor || config.largeFragmentLengthColor || "rgb(200, 0, 0)";
|
|
49270
49319
|
this.pairOrientation = config.pairOrienation || 'fr';
|
|
49271
49320
|
this.pairColors = {};
|
|
49272
49321
|
this.pairColors["RL"] = config.rlColor || "rgb(0, 150, 0)";
|
|
49273
49322
|
this.pairColors["RR"] = config.rrColor || "rgb(20, 50, 200)";
|
|
49274
49323
|
this.pairColors["LL"] = config.llColor || "rgb(0, 150, 150)";
|
|
49275
|
-
this.colorBy = config.colorBy || "
|
|
49324
|
+
this.colorBy = config.colorBy || "unexpectedPair";
|
|
49276
49325
|
this.colorByTag = config.colorByTag ? config.colorByTag.toUpperCase() : undefined;
|
|
49277
49326
|
this.bamColorTag = config.bamColorTag === undefined ? "YC" : config.bamColorTag;
|
|
49278
49327
|
this.hideSmallIndels = config.hideSmallIndels;
|
|
@@ -49371,7 +49420,7 @@
|
|
|
49371
49420
|
for (let alignment of alignmentRow.alignments) {
|
|
49372
49421
|
this.hasPairs = this.hasPairs || alignment.isPaired();
|
|
49373
49422
|
|
|
49374
|
-
if (this.browser.circularView
|
|
49423
|
+
if (this.browser.circularView) {
|
|
49375
49424
|
// This is an expensive check, only do it if needed
|
|
49376
49425
|
this.hasSupplemental = this.hasSupplemental || alignment.hasTag('SA');
|
|
49377
49426
|
}
|
|
@@ -49627,7 +49676,7 @@
|
|
|
49627
49676
|
direction: direction
|
|
49628
49677
|
};
|
|
49629
49678
|
this.parent.sortObject = newSortObject;
|
|
49630
|
-
sortAlignmentRows(newSortObject, viewport.
|
|
49679
|
+
sortAlignmentRows(newSortObject, viewport.cachedFeatures);
|
|
49631
49680
|
viewport.repaint();
|
|
49632
49681
|
};
|
|
49633
49682
|
|
|
@@ -49679,7 +49728,7 @@
|
|
|
49679
49728
|
};
|
|
49680
49729
|
this.sortByTag = tag;
|
|
49681
49730
|
this.parent.sortObject = newSortObject;
|
|
49682
|
-
sortAlignmentRows(newSortObject, viewport.
|
|
49731
|
+
sortAlignmentRows(newSortObject, viewport.cachedFeatures);
|
|
49683
49732
|
viewport.repaint();
|
|
49684
49733
|
}
|
|
49685
49734
|
}
|
|
@@ -49703,8 +49752,12 @@
|
|
|
49703
49752
|
const referenceFrame = clickState.viewport.referenceFrame;
|
|
49704
49753
|
|
|
49705
49754
|
if (this.browser.genome.getChromosome(clickedAlignment.mate.chr)) {
|
|
49706
|
-
this.highlightedAlignmentReadNamed = clickedAlignment.readName;
|
|
49707
|
-
|
|
49755
|
+
this.highlightedAlignmentReadNamed = clickedAlignment.readName; //this.browser.presentMultiLocusPanel(clickedAlignment, referenceFrame)
|
|
49756
|
+
|
|
49757
|
+
const bpWidth = referenceFrame.end - referenceFrame.start;
|
|
49758
|
+
const frameStart = clickedAlignment.mate.position - bpWidth / 2;
|
|
49759
|
+
const frameEnd = clickedAlignment.mate.position + bpWidth / 2;
|
|
49760
|
+
this.browser.addMultiLocusPanel(clickedAlignment.mate.chr, frameStart, frameEnd, referenceFrame);
|
|
49708
49761
|
} else {
|
|
49709
49762
|
Alert.presentAlert(`Reference does not contain chromosome: ${clickedAlignment.mate.chr}`);
|
|
49710
49763
|
}
|
|
@@ -49717,9 +49770,7 @@
|
|
|
49717
49770
|
list.push({
|
|
49718
49771
|
label: 'View read sequence',
|
|
49719
49772
|
click: () => {
|
|
49720
|
-
const
|
|
49721
|
-
if (!alignment) return;
|
|
49722
|
-
const seqstring = alignment.seq; //.map(b => String.fromCharCode(b)).join("");
|
|
49773
|
+
const seqstring = clickedAlignment.seq; //.map(b => String.fromCharCode(b)).join("");
|
|
49723
49774
|
|
|
49724
49775
|
if (!seqstring || "*" === seqstring) {
|
|
49725
49776
|
Alert.presentAlert("Read sequence: *");
|
|
@@ -49732,12 +49783,16 @@
|
|
|
49732
49783
|
if (isSecureContext()) {
|
|
49733
49784
|
list.push({
|
|
49734
49785
|
label: 'Copy read sequence',
|
|
49735
|
-
click: () => {
|
|
49736
|
-
const
|
|
49737
|
-
if (!alignment) return;
|
|
49738
|
-
const seqstring = alignment.seq; //.map(b => String.fromCharCode(b)).join("");
|
|
49786
|
+
click: async () => {
|
|
49787
|
+
const seq = clickedAlignment.seq; //.map(b => String.fromCharCode(b)).join("");
|
|
49739
49788
|
|
|
49740
|
-
|
|
49789
|
+
try {
|
|
49790
|
+
//console.log(`seq: ${seq}`)
|
|
49791
|
+
await navigator.clipboard.writeText(seq);
|
|
49792
|
+
} catch (e) {
|
|
49793
|
+
console.error(e);
|
|
49794
|
+
Alert.presentAlert(`error copying sequence to clipboard ${e}`);
|
|
49795
|
+
}
|
|
49741
49796
|
}
|
|
49742
49797
|
});
|
|
49743
49798
|
}
|
|
@@ -49747,25 +49802,12 @@
|
|
|
49747
49802
|
} // Experimental JBrowse feature
|
|
49748
49803
|
|
|
49749
49804
|
|
|
49750
|
-
if (this.browser.circularView &&
|
|
49805
|
+
if (this.browser.circularView && (this.hasPairs || this.hasSupplemental)) {
|
|
49751
49806
|
if (this.hasPairs) {
|
|
49752
49807
|
list.push({
|
|
49753
49808
|
label: 'Add discordant pairs to circular view',
|
|
49754
49809
|
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
|
-
});
|
|
49810
|
+
this.parent.addPairedChordsForViewport(viewport);
|
|
49769
49811
|
}
|
|
49770
49812
|
});
|
|
49771
49813
|
}
|
|
@@ -49774,23 +49816,7 @@
|
|
|
49774
49816
|
list.push({
|
|
49775
49817
|
label: 'Add split reads to circular view',
|
|
49776
49818
|
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
|
-
});
|
|
49819
|
+
this.parent.addSplitChordsForViewport(viewport);
|
|
49794
49820
|
}
|
|
49795
49821
|
});
|
|
49796
49822
|
}
|
|
@@ -49806,7 +49832,7 @@
|
|
|
49806
49832
|
const y = clickState.y;
|
|
49807
49833
|
const genomicLocation = clickState.genomicLocation;
|
|
49808
49834
|
const showSoftClips = this.parent.showSoftClips;
|
|
49809
|
-
let features = viewport.
|
|
49835
|
+
let features = viewport.cachedFeatures;
|
|
49810
49836
|
if (!features || features.length === 0) return;
|
|
49811
49837
|
let packedAlignmentRows = features.packedAlignmentRows;
|
|
49812
49838
|
let downsampledIntervals = features.downsampledIntervals;
|
|
@@ -49880,11 +49906,15 @@
|
|
|
49880
49906
|
case "unexpectedPair":
|
|
49881
49907
|
case "pairOrientation":
|
|
49882
49908
|
if (this.pairOrientation && alignment.pairOrientation) {
|
|
49883
|
-
|
|
49909
|
+
const oTypes = orientationTypes[this.pairOrientation];
|
|
49884
49910
|
|
|
49885
49911
|
if (oTypes) {
|
|
49886
|
-
|
|
49887
|
-
|
|
49912
|
+
const pairColor = this.pairColors[oTypes[alignment.pairOrientation]];
|
|
49913
|
+
|
|
49914
|
+
if (pairColor) {
|
|
49915
|
+
color = pairColor;
|
|
49916
|
+
break;
|
|
49917
|
+
}
|
|
49888
49918
|
}
|
|
49889
49919
|
}
|
|
49890
49920
|
|
|
@@ -49892,13 +49922,16 @@
|
|
|
49892
49922
|
break;
|
|
49893
49923
|
}
|
|
49894
49924
|
|
|
49925
|
+
case "tlen":
|
|
49895
49926
|
case "fragmentLength":
|
|
49896
|
-
if (alignment.mate && alignment.isMateMapped()
|
|
49897
|
-
|
|
49898
|
-
|
|
49899
|
-
|
|
49900
|
-
|
|
49901
|
-
|
|
49927
|
+
if (alignment.mate && alignment.isMateMapped()) {
|
|
49928
|
+
if (alignment.mate.chr !== alignment.chr) {
|
|
49929
|
+
color = getChrColor(alignment.mate.chr);
|
|
49930
|
+
} else if (this.parent.minTemplateLength && Math.abs(alignment.fragmentLength) < this.parent.minTemplateLength) {
|
|
49931
|
+
color = this.smallTLENColor;
|
|
49932
|
+
} else if (this.parent.maxTemplateLength && Math.abs(alignment.fragmentLength) > this.parent.maxTemplateLength) {
|
|
49933
|
+
color = this.largeTLENColor;
|
|
49934
|
+
}
|
|
49902
49935
|
}
|
|
49903
49936
|
|
|
49904
49937
|
break;
|
|
@@ -49940,10 +49973,7 @@
|
|
|
49940
49973
|
alignmentContainer.packedAlignmentRows.sort(function (rowA, rowB) {
|
|
49941
49974
|
const i = rowA.score > rowB.score ? 1 : rowA.score < rowB.score ? -1 : 0;
|
|
49942
49975
|
return true === direction ? i : -i;
|
|
49943
|
-
});
|
|
49944
|
-
// for(let r of alignmentContainer.packedAlignmentRows) {
|
|
49945
|
-
// console.log(r.score);
|
|
49946
|
-
// }
|
|
49976
|
+
});
|
|
49947
49977
|
}
|
|
49948
49978
|
|
|
49949
49979
|
function shadedBaseColor(qual, baseColor) {
|
|
@@ -50098,7 +50128,7 @@
|
|
|
50098
50128
|
});
|
|
50099
50129
|
this.$viewport.append(this.$rulerLabel);
|
|
50100
50130
|
this.$rulerLabel.click(async () => {
|
|
50101
|
-
await this.browser.
|
|
50131
|
+
await this.browser.gotoMultilocusPanel(this.referenceFrame); // const removals = this.browser.referenceFrameList.filter(r => this.referenceFrame !== r)
|
|
50102
50132
|
// for (let referenceFrame of removals) {
|
|
50103
50133
|
// await this.browser.removeMultiLocusPanel(referenceFrame)
|
|
50104
50134
|
// }
|
|
@@ -50204,7 +50234,10 @@
|
|
|
50204
50234
|
currentViewport = this;
|
|
50205
50235
|
this.$tooltip.show();
|
|
50206
50236
|
} else if (currentViewport.guid !== this.guid) {
|
|
50207
|
-
currentViewport.$tooltip
|
|
50237
|
+
if (currentViewport.$tooltip) {
|
|
50238
|
+
currentViewport.$tooltip.hide();
|
|
50239
|
+
}
|
|
50240
|
+
|
|
50208
50241
|
this.$tooltip.show();
|
|
50209
50242
|
currentViewport = this;
|
|
50210
50243
|
} else {
|
|
@@ -50238,7 +50271,9 @@
|
|
|
50238
50271
|
}); // hide tooltip when movement stops
|
|
50239
50272
|
|
|
50240
50273
|
clearTimeout(timer);
|
|
50241
|
-
timer = setTimeout(() =>
|
|
50274
|
+
timer = setTimeout(() => {
|
|
50275
|
+
if (this.$tooltip) this.$tooltip.hide();
|
|
50276
|
+
}, toolTipTimeout);
|
|
50242
50277
|
}
|
|
50243
50278
|
}
|
|
50244
50279
|
|
|
@@ -50253,65 +50288,6 @@
|
|
|
50253
50288
|
|
|
50254
50289
|
}
|
|
50255
50290
|
|
|
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
50291
|
/*
|
|
50316
50292
|
* The MIT License (MIT)
|
|
50317
50293
|
*
|
|
@@ -50343,60 +50319,30 @@
|
|
|
50343
50319
|
}
|
|
50344
50320
|
|
|
50345
50321
|
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;
|
|
50322
|
+
this.canvas = document.createElement('canvas');
|
|
50323
|
+
this.canvas.className = 'igv-ideogram-canvas';
|
|
50324
|
+
this.$content.append($$1(this.canvas));
|
|
50325
|
+
this.ideogram_ctx = this.canvas.getContext('2d');
|
|
50355
50326
|
this.addMouseHandlers();
|
|
50356
50327
|
}
|
|
50357
50328
|
|
|
50358
50329
|
addMouseHandlers() {
|
|
50359
|
-
this.addBrowserObserver();
|
|
50360
50330
|
this.addViewportClickHandler(this.$viewport.get(0));
|
|
50361
50331
|
}
|
|
50362
50332
|
|
|
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
50333
|
addViewportClickHandler(viewport) {
|
|
50334
|
+
this.boundClickHandler = clickHandler.bind(this);
|
|
50335
|
+
viewport.addEventListener('click', this.boundClickHandler);
|
|
50336
|
+
|
|
50388
50337
|
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
50338
|
const {
|
|
50393
50339
|
xNormalized,
|
|
50394
50340
|
width
|
|
50395
50341
|
} = translateMouseCoordinates$1(event, this.ideogram_ctx.canvas);
|
|
50396
50342
|
const {
|
|
50397
50343
|
bpLength
|
|
50398
|
-
} = this.browser.genome.getChromosome(referenceFrame.chr);
|
|
50399
|
-
const locusLength = referenceFrame.bpPerPixel * width;
|
|
50344
|
+
} = this.browser.genome.getChromosome(this.referenceFrame.chr);
|
|
50345
|
+
const locusLength = this.referenceFrame.bpPerPixel * width;
|
|
50400
50346
|
const chrCoveragePercentage = locusLength / bpLength;
|
|
50401
50347
|
let xPercentage = xNormalized;
|
|
50402
50348
|
|
|
@@ -50410,18 +50356,11 @@
|
|
|
50410
50356
|
|
|
50411
50357
|
const ss = Math.round((xPercentage - chrCoveragePercentage / 2.0) * bpLength);
|
|
50412
50358
|
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);
|
|
50359
|
+
this.referenceFrame.start = ss;
|
|
50360
|
+
this.referenceFrame.end = ee;
|
|
50361
|
+
this.referenceFrame.bpPerPixel = (ee - ss) / width;
|
|
50362
|
+
this.browser.updateViews(this.referenceFrame, this.browser.trackViews, true);
|
|
50417
50363
|
}
|
|
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
50364
|
}
|
|
50426
50365
|
|
|
50427
50366
|
setWidth(width) {
|
|
@@ -50439,14 +50378,22 @@
|
|
|
50439
50378
|
context.restore();
|
|
50440
50379
|
}
|
|
50441
50380
|
|
|
50442
|
-
|
|
50443
|
-
this
|
|
50444
|
-
|
|
50381
|
+
repaint() {
|
|
50382
|
+
this.draw({
|
|
50383
|
+
referenceFrame: this.referenceFrame
|
|
50384
|
+
});
|
|
50385
|
+
}
|
|
50386
|
+
|
|
50387
|
+
draw(_ref) {
|
|
50388
|
+
let {
|
|
50389
|
+
referenceFrame
|
|
50390
|
+
} = _ref;
|
|
50391
|
+
IGVGraphics.configureHighDPICanvas(this.ideogram_ctx, this.$viewport.width(), this.$viewport.height());
|
|
50445
50392
|
this.trackView.track.draw({
|
|
50446
|
-
context,
|
|
50393
|
+
context: this.ideogram_ctx,
|
|
50447
50394
|
referenceFrame,
|
|
50448
|
-
pixelWidth,
|
|
50449
|
-
pixelHeight
|
|
50395
|
+
pixelWidth: this.$viewport.width(),
|
|
50396
|
+
pixelHeight: this.$viewport.height()
|
|
50450
50397
|
});
|
|
50451
50398
|
}
|
|
50452
50399
|
|
|
@@ -50484,7 +50431,7 @@
|
|
|
50484
50431
|
function createViewport(trackView, column, referenceFrame, width) {
|
|
50485
50432
|
if ('ruler' === trackView.track.type) {
|
|
50486
50433
|
return new RulerViewport(trackView, column, referenceFrame, width);
|
|
50487
|
-
} else if ('ideogram' === trackView.track.
|
|
50434
|
+
} else if ('ideogram' === trackView.track.id) {
|
|
50488
50435
|
return new IdeogramViewport(trackView, column, referenceFrame, width);
|
|
50489
50436
|
} else {
|
|
50490
50437
|
return new TrackViewport(trackView, column, referenceFrame, width);
|
|
@@ -50659,7 +50606,7 @@
|
|
|
50659
50606
|
sampleNameViewport.setWidth(this.browser.sampleNameViewportWidth);
|
|
50660
50607
|
}
|
|
50661
50608
|
|
|
50662
|
-
this.browser.
|
|
50609
|
+
this.browser.layoutChange();
|
|
50663
50610
|
}
|
|
50664
50611
|
};
|
|
50665
50612
|
this.browser.inputDialog.present(config, event);
|
|
@@ -51011,7 +50958,6 @@
|
|
|
51011
50958
|
|
|
51012
50959
|
class TrackView {
|
|
51013
50960
|
constructor(browser, columnContainer, track) {
|
|
51014
|
-
this.namespace = `trackview-${guid$2()}`;
|
|
51015
50961
|
this.browser = browser;
|
|
51016
50962
|
this.track = track;
|
|
51017
50963
|
track.trackView = this;
|
|
@@ -51037,7 +50983,7 @@
|
|
|
51037
50983
|
|
|
51038
50984
|
addDOMToColumnContainer(browser, columnContainer, referenceFrameList) {
|
|
51039
50985
|
// Axis
|
|
51040
|
-
this.axis = this.createAxis(browser, this.track); //
|
|
50986
|
+
this.axis = this.createAxis(browser, this.track); // Create a viewport for each reference frame
|
|
51041
50987
|
|
|
51042
50988
|
this.viewports = [];
|
|
51043
50989
|
const viewportWidth = browser.calculateViewportWidth(referenceFrameList.length);
|
|
@@ -51104,7 +51050,6 @@
|
|
|
51104
51050
|
this.axis.remove(); // Track Viewports
|
|
51105
51051
|
|
|
51106
51052
|
for (let viewport of this.viewports) {
|
|
51107
|
-
viewport.removeMouseHandlers();
|
|
51108
51053
|
viewport.$viewport.remove();
|
|
51109
51054
|
} // SampleName Viewport
|
|
51110
51055
|
|
|
@@ -51222,7 +51167,7 @@
|
|
|
51222
51167
|
$viewport.height(newHeight);
|
|
51223
51168
|
}
|
|
51224
51169
|
|
|
51225
|
-
this.sampleNameViewport.viewport.style.height = `${newHeight}px`; // If the track does not manage its own content height set it here
|
|
51170
|
+
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
51171
|
|
|
51227
51172
|
if (typeof this.track.computePixelHeight !== "function") {
|
|
51228
51173
|
for (let vp of this.viewports) {
|
|
@@ -51274,14 +51219,6 @@
|
|
|
51274
51219
|
if (viewport.isLoading()) return true;
|
|
51275
51220
|
}
|
|
51276
51221
|
}
|
|
51277
|
-
|
|
51278
|
-
resize(viewportWidth) {
|
|
51279
|
-
for (let viewport of this.viewports) {
|
|
51280
|
-
viewport.setWidth(viewportWidth);
|
|
51281
|
-
}
|
|
51282
|
-
|
|
51283
|
-
this.updateViews(true);
|
|
51284
|
-
}
|
|
51285
51222
|
/**
|
|
51286
51223
|
* Repaint all viewports without loading any new data. Use this for events that change visual aspect of data,
|
|
51287
51224
|
* e.g. color, sort order, etc, but do not change the genomic state.
|
|
@@ -51290,7 +51227,9 @@
|
|
|
51290
51227
|
|
|
51291
51228
|
repaintViews() {
|
|
51292
51229
|
for (let viewport of this.viewports) {
|
|
51293
|
-
viewport.
|
|
51230
|
+
if (viewport.isVisible()) {
|
|
51231
|
+
viewport.repaint();
|
|
51232
|
+
}
|
|
51294
51233
|
}
|
|
51295
51234
|
|
|
51296
51235
|
if (typeof this.track.paintAxis === 'function') {
|
|
@@ -51312,41 +51251,60 @@
|
|
|
51312
51251
|
setTrackLabelName(name) {
|
|
51313
51252
|
this.viewports.forEach(viewport => viewport.setTrackLabel(name));
|
|
51314
51253
|
}
|
|
51254
|
+
/**
|
|
51255
|
+
* Called in response to a window resize event, change in # of multilocus panels, or other event that changes
|
|
51256
|
+
* the width of the track view.
|
|
51257
|
+
*
|
|
51258
|
+
* @param viewportWidth The width of each viewport in this track view.
|
|
51259
|
+
*/
|
|
51260
|
+
|
|
51261
|
+
|
|
51262
|
+
resize(viewportWidth) {
|
|
51263
|
+
for (let viewport of this.viewports) {
|
|
51264
|
+
viewport.setWidth(viewportWidth);
|
|
51265
|
+
}
|
|
51266
|
+
}
|
|
51315
51267
|
/**
|
|
51316
51268
|
* Update viewports to reflect current genomic state, possibly loading additional data.
|
|
51269
|
+
*
|
|
51270
|
+
* @param force - if true, force a repaint even if no new data is loaded
|
|
51271
|
+
* @returns {Promise<void>}
|
|
51317
51272
|
*/
|
|
51318
51273
|
|
|
51319
51274
|
|
|
51320
|
-
async updateViews(
|
|
51275
|
+
async updateViews() {
|
|
51321
51276
|
if (!(this.browser && this.browser.referenceFrameList)) return;
|
|
51322
51277
|
const visibleViewports = this.viewports.filter(viewport => viewport.isVisible()); // Shift viewports left/right to current genomic state (pans canvas)
|
|
51323
51278
|
|
|
51324
|
-
visibleViewports.forEach(viewport => viewport.shift());
|
|
51325
|
-
const isDragging = this.browser.dragObject;
|
|
51279
|
+
visibleViewports.forEach(viewport => viewport.shift()); // If dragging (panning) return
|
|
51326
51280
|
|
|
51327
|
-
if (
|
|
51281
|
+
if (this.browser.dragObject) {
|
|
51328
51282
|
return;
|
|
51329
|
-
} //
|
|
51283
|
+
} // Get viewports to repaint
|
|
51330
51284
|
|
|
51331
51285
|
|
|
51332
|
-
|
|
51286
|
+
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
|
|
51287
|
+
|
|
51288
|
+
viewportsToRepaint = viewportsToRepaint.filter(viewport => viewport.checkZoomIn()); // Get viewports that require a data load
|
|
51289
|
+
|
|
51290
|
+
const viewportsToReload = viewportsToRepaint.filter(viewport => viewport.needsReload()); // Trigger viewport to load features needed to cover current genomic range
|
|
51333
51291
|
// NOTE: these must be loaded synchronously, do not user Promise.all, not all file readers are thread safe
|
|
51334
51292
|
|
|
51335
|
-
for (let viewport of
|
|
51293
|
+
for (let viewport of viewportsToReload) {
|
|
51336
51294
|
await viewport.loadFeatures();
|
|
51337
51295
|
}
|
|
51338
51296
|
|
|
51339
51297
|
if (this.disposed) return; // Track was removed during load
|
|
51340
|
-
//
|
|
51298
|
+
// Special case for variant tracks in multilocus view. The # of rows to allocate to the variant (site)
|
|
51341
51299
|
// section depends on data from all the views. We only need to adjust this however if any data was loaded
|
|
51342
51300
|
// (i.e. reloadableViewports.length > 0)
|
|
51343
51301
|
|
|
51344
|
-
if (this.track && typeof this.track.variantRowCount === 'function' &&
|
|
51302
|
+
if (this.track && typeof this.track.variantRowCount === 'function' && viewportsToReload.length > 0) {
|
|
51345
51303
|
let maxRow = 0;
|
|
51346
51304
|
|
|
51347
51305
|
for (let viewport of this.viewports) {
|
|
51348
|
-
if (viewport.
|
|
51349
|
-
maxRow = Math.max(maxRow, viewport.
|
|
51306
|
+
if (viewport.featureCache && viewport.featureCache.features) {
|
|
51307
|
+
maxRow = Math.max(maxRow, viewport.featureCache.features.reduce((a, f) => Math.max(a, f.row || 0), 0));
|
|
51350
51308
|
}
|
|
51351
51309
|
}
|
|
51352
51310
|
|
|
@@ -51369,14 +51327,14 @@
|
|
|
51369
51327
|
const start = referenceFrame.start;
|
|
51370
51328
|
const end = start + referenceFrame.toBP($$1(visibleViewport.contentDiv).width());
|
|
51371
51329
|
|
|
51372
|
-
if (visibleViewport.
|
|
51373
|
-
if (typeof visibleViewport.
|
|
51374
|
-
const max = visibleViewport.
|
|
51330
|
+
if (visibleViewport.featureCache && visibleViewport.featureCache.features) {
|
|
51331
|
+
if (typeof visibleViewport.featureCache.features.getMax === 'function') {
|
|
51332
|
+
const max = visibleViewport.featureCache.features.getMax(start, end);
|
|
51375
51333
|
allFeatures.push({
|
|
51376
51334
|
value: max
|
|
51377
51335
|
});
|
|
51378
51336
|
} else {
|
|
51379
|
-
allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(visibleViewport.
|
|
51337
|
+
allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(visibleViewport.featureCache.features, start, end));
|
|
51380
51338
|
}
|
|
51381
51339
|
}
|
|
51382
51340
|
}
|
|
@@ -51386,17 +51344,10 @@
|
|
|
51386
51344
|
} else {
|
|
51387
51345
|
this.track.dataRange = doAutoscale(allFeatures);
|
|
51388
51346
|
}
|
|
51389
|
-
}
|
|
51390
|
-
|
|
51347
|
+
}
|
|
51391
51348
|
|
|
51392
|
-
|
|
51393
|
-
|
|
51394
|
-
visibleViewport.repaint();
|
|
51395
|
-
}
|
|
51396
|
-
} else {
|
|
51397
|
-
for (let vp of reloadableViewports) {
|
|
51398
|
-
vp.repaint();
|
|
51399
|
-
}
|
|
51349
|
+
for (let vp of viewportsToRepaint) {
|
|
51350
|
+
vp.repaint();
|
|
51400
51351
|
}
|
|
51401
51352
|
|
|
51402
51353
|
this.adjustTrackHeight(); // Repaint sample names last
|
|
@@ -51419,36 +51370,34 @@
|
|
|
51419
51370
|
}
|
|
51420
51371
|
}
|
|
51421
51372
|
/**
|
|
51422
|
-
* Return a promise to get all in-view features. Used for group autoscaling.
|
|
51373
|
+
* Return a promise to get all in-view features across all viewports. Used for group autoscaling.
|
|
51423
51374
|
*/
|
|
51424
51375
|
|
|
51425
51376
|
|
|
51426
|
-
async getInViewFeatures(
|
|
51377
|
+
async getInViewFeatures() {
|
|
51427
51378
|
if (!(this.browser && this.browser.referenceFrameList)) {
|
|
51428
51379
|
return [];
|
|
51429
|
-
}
|
|
51430
|
-
|
|
51380
|
+
}
|
|
51431
51381
|
|
|
51432
|
-
const rpV = this.viewportsToReload(force);
|
|
51433
|
-
const promises = rpV.map(function (vp) {
|
|
51434
|
-
return vp.loadFeatures();
|
|
51435
|
-
});
|
|
51436
|
-
await Promise.all(promises);
|
|
51437
51382
|
let allFeatures = [];
|
|
51438
51383
|
|
|
51439
51384
|
for (let vp of this.viewports) {
|
|
51440
|
-
if (vp.
|
|
51385
|
+
if (vp.needsReload()) {
|
|
51386
|
+
await vp.loadFeatures();
|
|
51387
|
+
}
|
|
51388
|
+
|
|
51389
|
+
if (vp.featureCache && vp.featureCache.features) {
|
|
51441
51390
|
const referenceFrame = vp.referenceFrame;
|
|
51442
51391
|
const start = referenceFrame.start;
|
|
51443
51392
|
const end = start + referenceFrame.toBP($$1(vp.contentDiv).width());
|
|
51444
51393
|
|
|
51445
|
-
if (typeof vp.
|
|
51446
|
-
const max = vp.
|
|
51394
|
+
if (typeof vp.featureCache.features.getMax === 'function') {
|
|
51395
|
+
const max = vp.featureCache.features.getMax(start, end);
|
|
51447
51396
|
allFeatures.push({
|
|
51448
51397
|
value: max
|
|
51449
51398
|
});
|
|
51450
51399
|
} else {
|
|
51451
|
-
allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(vp.
|
|
51400
|
+
allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(vp.featureCache.features, start, end));
|
|
51452
51401
|
}
|
|
51453
51402
|
}
|
|
51454
51403
|
}
|
|
@@ -51490,27 +51439,6 @@
|
|
|
51490
51439
|
}
|
|
51491
51440
|
}
|
|
51492
51441
|
|
|
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
51442
|
createTrackScrollbar(browser) {
|
|
51515
51443
|
const outerScroll = div$1();
|
|
51516
51444
|
browser.columnContainer.querySelector('.igv-scrollbar-column').appendChild(outerScroll);
|
|
@@ -51604,7 +51532,7 @@
|
|
|
51604
51532
|
}
|
|
51605
51533
|
|
|
51606
51534
|
addTrackDragMouseHandlers(browser) {
|
|
51607
|
-
if ('ideogram' === this.track.
|
|
51535
|
+
if ('ideogram' === this.track.id || 'ruler' === this.track.id) ; else {
|
|
51608
51536
|
let currentDragHandle = undefined; // Mouse Down
|
|
51609
51537
|
|
|
51610
51538
|
this.boundTrackDragMouseDownHandler = trackDragMouseDownHandler.bind(this);
|
|
@@ -51668,7 +51596,7 @@
|
|
|
51668
51596
|
}
|
|
51669
51597
|
|
|
51670
51598
|
removeTrackDragMouseHandlers() {
|
|
51671
|
-
if ('ideogram' === this.track.
|
|
51599
|
+
if ('ideogram' === this.track.id || 'ruler' === this.track.id) ; else {
|
|
51672
51600
|
this.dragHandle.removeEventListener('mousedown', this.boundTrackDragMouseDownHandler);
|
|
51673
51601
|
document.removeEventListener('mouseup', this.boundDocumentTrackDragMouseUpHandler);
|
|
51674
51602
|
this.dragHandle.removeEventListener('mouseup', this.boundTrackDragMouseEnterHandler);
|
|
@@ -52563,7 +52491,7 @@
|
|
|
52563
52491
|
});
|
|
52564
52492
|
}
|
|
52565
52493
|
|
|
52566
|
-
findUTRs(exons, feature.cdStart, feature.cdEnd);
|
|
52494
|
+
findUTRs$1(exons, feature.cdStart, feature.cdEnd);
|
|
52567
52495
|
feature.exons = exons;
|
|
52568
52496
|
} // Optional extra columns
|
|
52569
52497
|
|
|
@@ -52668,7 +52596,7 @@
|
|
|
52668
52596
|
});
|
|
52669
52597
|
}
|
|
52670
52598
|
|
|
52671
|
-
findUTRs(exons, cdStart, cdEnd);
|
|
52599
|
+
findUTRs$1(exons, cdStart, cdEnd);
|
|
52672
52600
|
feature.exons = exons;
|
|
52673
52601
|
return feature;
|
|
52674
52602
|
}
|
|
@@ -52710,7 +52638,7 @@
|
|
|
52710
52638
|
});
|
|
52711
52639
|
}
|
|
52712
52640
|
|
|
52713
|
-
findUTRs(exons, cdStart, cdEnd);
|
|
52641
|
+
findUTRs$1(exons, cdStart, cdEnd);
|
|
52714
52642
|
feature.exons = exons;
|
|
52715
52643
|
return feature;
|
|
52716
52644
|
}
|
|
@@ -52751,12 +52679,12 @@
|
|
|
52751
52679
|
});
|
|
52752
52680
|
}
|
|
52753
52681
|
|
|
52754
|
-
findUTRs(exons, cdStart, cdEnd);
|
|
52682
|
+
findUTRs$1(exons, cdStart, cdEnd);
|
|
52755
52683
|
feature.exons = exons;
|
|
52756
52684
|
return feature;
|
|
52757
52685
|
}
|
|
52758
52686
|
|
|
52759
|
-
function findUTRs(exons, cdStart, cdEnd) {
|
|
52687
|
+
function findUTRs$1(exons, cdStart, cdEnd) {
|
|
52760
52688
|
for (let exon of exons) {
|
|
52761
52689
|
const end = exon.end;
|
|
52762
52690
|
const start = exon.start;
|
|
@@ -56315,7 +56243,7 @@
|
|
|
56315
56243
|
this.genome = genome;
|
|
56316
56244
|
this.sourceType = config.sourceType === undefined ? "file" : config.sourceType;
|
|
56317
56245
|
this.maxWGCount = config.maxWGCount || DEFAULT_MAX_WG_COUNT;
|
|
56318
|
-
const queryableFormats = new Set(["bigwig", "bw", "bigbed", "bb", "biginteract", "tdf"]);
|
|
56246
|
+
const queryableFormats = new Set(["bigwig", "bw", "bigbed", "bb", "biginteract", "biggenepred", "bignarrowpeak", "tdf"]);
|
|
56319
56247
|
|
|
56320
56248
|
if (config.features && Array.isArray(config.features)) {
|
|
56321
56249
|
// Explicit array of features
|
|
@@ -56327,7 +56255,7 @@
|
|
|
56327
56255
|
}
|
|
56328
56256
|
|
|
56329
56257
|
this.queryable = false;
|
|
56330
|
-
this.featureCache = new FeatureCache(features, genome);
|
|
56258
|
+
this.featureCache = new FeatureCache$1(features, genome);
|
|
56331
56259
|
} else if (config.reader) {
|
|
56332
56260
|
// Explicit reader implementation
|
|
56333
56261
|
this.reader = config.reader;
|
|
@@ -56498,13 +56426,13 @@
|
|
|
56498
56426
|
} // Note - replacing previous cache with new one. genomicInterval is optional (might be undefined => includes all features)
|
|
56499
56427
|
|
|
56500
56428
|
|
|
56501
|
-
this.featureCache = new FeatureCache(features, this.genome, genomicInterval); // If track is marked "searchable"< cache features by name -- use this with caution, memory intensive
|
|
56429
|
+
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
56430
|
|
|
56503
56431
|
if (this.config.searchable || this.config.searchableFields) {
|
|
56504
56432
|
this.addFeaturesToDB(features);
|
|
56505
56433
|
}
|
|
56506
56434
|
} else {
|
|
56507
|
-
this.featureCache = new FeatureCache([], genomicInterval); // Empty cache
|
|
56435
|
+
this.featureCache = new FeatureCache$1([], genomicInterval); // Empty cache
|
|
56508
56436
|
}
|
|
56509
56437
|
}
|
|
56510
56438
|
|
|
@@ -56726,10 +56654,8 @@
|
|
|
56726
56654
|
|
|
56727
56655
|
}
|
|
56728
56656
|
|
|
56729
|
-
//table chromatinInteract
|
|
56730
|
-
|
|
56731
56657
|
function getDecoder(definedFieldCount, fieldCount, autoSql, format) {
|
|
56732
|
-
if (autoSql && 'chromatinInteract' === autoSql.table ||
|
|
56658
|
+
if ("biginteract" === format || autoSql && 'chromatinInteract' === autoSql.table || 'interact' === autoSql.table) {
|
|
56733
56659
|
return decodeInteract;
|
|
56734
56660
|
} else {
|
|
56735
56661
|
const standardFieldCount = definedFieldCount - 3;
|
|
@@ -56776,6 +56702,7 @@
|
|
|
56776
56702
|
});
|
|
56777
56703
|
}
|
|
56778
56704
|
|
|
56705
|
+
findUTRs(exons, feature.cdStart, feature.cdEnd);
|
|
56779
56706
|
feature.exons = exons;
|
|
56780
56707
|
}
|
|
56781
56708
|
|
|
@@ -56792,7 +56719,29 @@
|
|
|
56792
56719
|
}
|
|
56793
56720
|
}
|
|
56794
56721
|
};
|
|
56795
|
-
}
|
|
56722
|
+
} //table chromatinInteract
|
|
56723
|
+
// "Chromatin interaction between two regions"
|
|
56724
|
+
// (
|
|
56725
|
+
// string chrom; "Chromosome (or contig, scaffold, etc.). For interchromosomal, use 2 records"
|
|
56726
|
+
// uint chromStart; "Start position of lower region. For interchromosomal, set to chromStart of this region"
|
|
56727
|
+
// uint chromEnd; "End position of upper region. For interchromosomal, set to chromEnd of this region"
|
|
56728
|
+
// string name; "Name of item, for display"
|
|
56729
|
+
// uint score; "Score from 0-1000"
|
|
56730
|
+
// double value; "Strength of interaction or other data value. Typically basis for score"
|
|
56731
|
+
// string exp; "Experiment name (metadata for filtering). Use . if not applicable"
|
|
56732
|
+
// string color; "Item color. Specified as r,g,b or hexadecimal #RRGGBB or html color name, as in //www.w3.org/TR/css3-color/#html4."
|
|
56733
|
+
// string region1Chrom; "Chromosome of lower region. For non-directional interchromosomal, chrom of this region."
|
|
56734
|
+
// uint region1Start; "Start position of lower/this region"
|
|
56735
|
+
// uint region1End; "End position in chromosome of lower/this region"
|
|
56736
|
+
// string region1Name; "Identifier of lower/this region"
|
|
56737
|
+
// string region1Strand; "Orientation of lower/this region: + or -. Use . if not applicable"
|
|
56738
|
+
// string region2Chrom; "Chromosome of upper region. For non-directional interchromosomal, chrom of other region"
|
|
56739
|
+
// uint region2Start; "Start position in chromosome of upper/this region"
|
|
56740
|
+
// uint region2End; "End position in chromosome of upper/this region"
|
|
56741
|
+
// string region2Name; "Identifier of upper/this region"
|
|
56742
|
+
// string region2Strand; "Orientation of upper/this region: + or -. Use . if not applicable"
|
|
56743
|
+
// )
|
|
56744
|
+
|
|
56796
56745
|
|
|
56797
56746
|
function decodeInteract(feature, tokens) {
|
|
56798
56747
|
feature.chr1 = tokens[5];
|
|
@@ -56809,6 +56758,25 @@
|
|
|
56809
56758
|
}
|
|
56810
56759
|
}
|
|
56811
56760
|
|
|
56761
|
+
function findUTRs(exons, cdStart, cdEnd) {
|
|
56762
|
+
for (let exon of exons) {
|
|
56763
|
+
const end = exon.end;
|
|
56764
|
+
const start = exon.start;
|
|
56765
|
+
|
|
56766
|
+
if (end < cdStart || start > cdEnd) {
|
|
56767
|
+
exon.utr = true;
|
|
56768
|
+
} else {
|
|
56769
|
+
if (cdStart >= start && cdStart <= end) {
|
|
56770
|
+
exon.cdStart = cdStart;
|
|
56771
|
+
}
|
|
56772
|
+
|
|
56773
|
+
if (cdEnd >= start && cdEnd <= end) {
|
|
56774
|
+
exon.cdEnd = cdEnd;
|
|
56775
|
+
}
|
|
56776
|
+
}
|
|
56777
|
+
}
|
|
56778
|
+
}
|
|
56779
|
+
|
|
56812
56780
|
function scoreShade(score, color) {
|
|
56813
56781
|
const alpha = Math.min(1, 0.11 + 0.89 * (score / 779));
|
|
56814
56782
|
return alpha.toString();
|
|
@@ -58254,7 +58222,7 @@
|
|
|
58254
58222
|
return features;
|
|
58255
58223
|
}
|
|
58256
58224
|
|
|
58257
|
-
supportsWholeGenome() {
|
|
58225
|
+
get supportsWholeGenome() {
|
|
58258
58226
|
return true;
|
|
58259
58227
|
}
|
|
58260
58228
|
|
|
@@ -58359,11 +58327,12 @@
|
|
|
58359
58327
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
58360
58328
|
* THE SOFTWARE.
|
|
58361
58329
|
*/
|
|
58330
|
+
const bbFormats = new Set(['bigwig', 'bw', 'bigbed', 'bb', 'biginteract', 'biggenepred', 'bignarrowpeak']);
|
|
58362
58331
|
|
|
58363
58332
|
function FeatureSource(config, genome) {
|
|
58364
58333
|
const format = config.format ? config.format.toLowerCase() : undefined;
|
|
58365
58334
|
|
|
58366
|
-
if (
|
|
58335
|
+
if (bbFormats.has(format)) {
|
|
58367
58336
|
return new BWSource(config, genome);
|
|
58368
58337
|
} else if ("tdf" === format) {
|
|
58369
58338
|
return new TDFSource(config, genome);
|
|
@@ -58372,30 +58341,6 @@
|
|
|
58372
58341
|
}
|
|
58373
58342
|
}
|
|
58374
58343
|
|
|
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
58344
|
const GtexUtils = {
|
|
58400
58345
|
getTissueInfo: function (datasetId, baseURL) {
|
|
58401
58346
|
datasetId = datasetId || 'gtex_v8';
|
|
@@ -58636,8 +58581,8 @@
|
|
|
58636
58581
|
const xleft = centerX - textBox.width / 2;
|
|
58637
58582
|
const xright = centerX + textBox.width / 2;
|
|
58638
58583
|
|
|
58639
|
-
if (options.labelAllFeatures || xleft > options.
|
|
58640
|
-
options.
|
|
58584
|
+
if (options.labelAllFeatures || xleft > options.rowLastLabelX[feature.row] || gtexSelection) {
|
|
58585
|
+
options.rowLastLabelX[feature.row] = xright;
|
|
58641
58586
|
IGVGraphics.fillText(ctx, name, centerX, labelY, geneFontStyle, transform);
|
|
58642
58587
|
}
|
|
58643
58588
|
} finally {
|
|
@@ -58918,7 +58863,7 @@
|
|
|
58918
58863
|
return this;
|
|
58919
58864
|
}
|
|
58920
58865
|
|
|
58921
|
-
supportsWholeGenome() {
|
|
58866
|
+
get supportsWholeGenome() {
|
|
58922
58867
|
return (this.config.indexed === false || !this.config.indexURL) && this.config.supportsWholeGenome !== false;
|
|
58923
58868
|
}
|
|
58924
58869
|
|
|
@@ -58977,27 +58922,31 @@
|
|
|
58977
58922
|
if (featureList) {
|
|
58978
58923
|
const rowFeatureCount = [];
|
|
58979
58924
|
options.rowLastX = [];
|
|
58925
|
+
options.rowLastLabelX = [];
|
|
58980
58926
|
|
|
58981
58927
|
for (let feature of featureList) {
|
|
58982
|
-
|
|
58928
|
+
if (feature.start > bpStart && feature.end < bpEnd) {
|
|
58929
|
+
const row = this.displayMode === "COLLAPSED" ? 0 : feature.row || 0;
|
|
58983
58930
|
|
|
58984
|
-
|
|
58985
|
-
|
|
58986
|
-
|
|
58987
|
-
|
|
58988
|
-
|
|
58931
|
+
if (rowFeatureCount[row] === undefined) {
|
|
58932
|
+
rowFeatureCount[row] = 1;
|
|
58933
|
+
} else {
|
|
58934
|
+
rowFeatureCount[row]++;
|
|
58935
|
+
}
|
|
58989
58936
|
|
|
58990
|
-
|
|
58937
|
+
options.rowLastX[row] = -Number.MAX_SAFE_INTEGER;
|
|
58938
|
+
options.rowLastLabelX[row] = -Number.MAX_SAFE_INTEGER;
|
|
58939
|
+
}
|
|
58991
58940
|
}
|
|
58992
58941
|
|
|
58942
|
+
const pixelsPerFeature = pixelWidth / Math.max(...rowFeatureCount);
|
|
58993
58943
|
let lastPxEnd = [];
|
|
58994
58944
|
|
|
58995
58945
|
for (let feature of featureList) {
|
|
58996
58946
|
if (feature.end < bpStart) continue;
|
|
58997
58947
|
if (feature.start > bpEnd) break;
|
|
58998
58948
|
const row = this.displayMode === 'COLLAPSED' ? 0 : feature.row;
|
|
58999
|
-
|
|
59000
|
-
options.drawLabel = options.labelAllFeatures || featureDensity > 10;
|
|
58949
|
+
options.drawLabel = options.labelAllFeatures || pixelsPerFeature > 10;
|
|
59001
58950
|
const pxEnd = Math.ceil((feature.end - bpStart) / bpPerPixel);
|
|
59002
58951
|
const last = lastPxEnd[row];
|
|
59003
58952
|
|
|
@@ -59071,15 +59020,9 @@
|
|
|
59071
59020
|
for (let fd of featureData) {
|
|
59072
59021
|
data.push(fd);
|
|
59073
59022
|
|
|
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
|
-
}
|
|
59023
|
+
if (infoURL && fd.name && fd.name.toLowerCase() === "name" && fd.value && isString$3(fd.value) && !fd.value.startsWith("<")) {
|
|
59024
|
+
const href = infoURL.replace("$$", feature.name);
|
|
59025
|
+
fd.value = `<a target=_blank href=${href}>${fd.value}</a>`;
|
|
59083
59026
|
}
|
|
59084
59027
|
} //Array.prototype.push.apply(data, featureData);
|
|
59085
59028
|
// If we have clicked over an exon number it.
|
|
@@ -59153,17 +59096,31 @@
|
|
|
59153
59096
|
}
|
|
59154
59097
|
|
|
59155
59098
|
contextMenuItemList(clickState) {
|
|
59156
|
-
|
|
59157
|
-
const features = this.clickedFeatures(clickState);
|
|
59099
|
+
const features = this.clickedFeatures(clickState);
|
|
59158
59100
|
|
|
59159
|
-
|
|
59160
|
-
|
|
59161
|
-
|
|
59101
|
+
if (features.length > 1) {
|
|
59102
|
+
features.sort((a, b) => b.end - b.start - (a.end - a.start));
|
|
59103
|
+
}
|
|
59104
|
+
|
|
59105
|
+
const f = features[0]; // The shortest clicked feature
|
|
59106
|
+
|
|
59107
|
+
if (f.end - f.start <= 1000000) {
|
|
59108
|
+
const list = [{
|
|
59109
|
+
label: 'View feature sequence',
|
|
59110
|
+
click: async () => {
|
|
59111
|
+
let seq = await this.browser.genome.getSequence(f.chr, f.start, f.end);
|
|
59162
59112
|
|
|
59163
|
-
|
|
59113
|
+
if (f.strand === '-') {
|
|
59114
|
+
seq = reverseComplementSequence(seq);
|
|
59115
|
+
}
|
|
59116
|
+
|
|
59117
|
+
if (!seq) seq = "Unknown sequence";
|
|
59118
|
+
Alert.presentAlert(seq);
|
|
59119
|
+
}
|
|
59120
|
+
}];
|
|
59164
59121
|
|
|
59165
|
-
if (
|
|
59166
|
-
|
|
59122
|
+
if (isSecureContext() && navigator.clipboard !== undefined) {
|
|
59123
|
+
list.push({
|
|
59167
59124
|
label: 'Copy feature sequence',
|
|
59168
59125
|
click: async () => {
|
|
59169
59126
|
let seq = await this.browser.genome.getSequence(f.chr, f.start, f.end);
|
|
@@ -59172,14 +59129,21 @@
|
|
|
59172
59129
|
seq = reverseComplementSequence(seq);
|
|
59173
59130
|
}
|
|
59174
59131
|
|
|
59175
|
-
|
|
59132
|
+
try {
|
|
59133
|
+
await navigator.clipboard.writeText(seq);
|
|
59134
|
+
} catch (e) {
|
|
59135
|
+
console.error(e);
|
|
59136
|
+
Alert.presentAlert(`error copying sequence to clipboard ${e}`);
|
|
59137
|
+
}
|
|
59176
59138
|
}
|
|
59177
|
-
}
|
|
59139
|
+
});
|
|
59178
59140
|
}
|
|
59179
|
-
} // Either not a secure context (i.e. http: protocol), or feature is too long
|
|
59180
59141
|
|
|
59181
|
-
|
|
59182
|
-
|
|
59142
|
+
list.push('<hr/>');
|
|
59143
|
+
return list;
|
|
59144
|
+
} else {
|
|
59145
|
+
return undefined;
|
|
59146
|
+
}
|
|
59183
59147
|
}
|
|
59184
59148
|
|
|
59185
59149
|
description() {
|
|
@@ -59275,14 +59239,12 @@
|
|
|
59275
59239
|
this.featureType = 'numeric';
|
|
59276
59240
|
this.paintAxis = paintAxis;
|
|
59277
59241
|
const format = config.format ? config.format.toLowerCase() : config.format;
|
|
59242
|
+
this.flipAxis = config.flipAxis ? config.flipAxis : false;
|
|
59243
|
+
this.logScale = config.logScale ? config.logScale : false;
|
|
59278
59244
|
|
|
59279
59245
|
if ("bigwig" === format) {
|
|
59280
|
-
this.flipAxis = config.flipAxis ? config.flipAxis : false;
|
|
59281
|
-
this.logScale = config.logScale ? config.logScale : false;
|
|
59282
59246
|
this.featureSource = new BWSource(config, this.browser.genome);
|
|
59283
59247
|
} else if ("tdf" === format) {
|
|
59284
|
-
this.flipAxis = config.flipAxis ? config.flipAxis : false;
|
|
59285
|
-
this.logScale = config.logScale ? config.logScale : false;
|
|
59286
59248
|
this.featureSource = new TDFSource(config, this.browser.genome);
|
|
59287
59249
|
} else {
|
|
59288
59250
|
this.featureSource = FeatureSource(config, this.browser.genome);
|
|
@@ -59520,7 +59482,7 @@
|
|
|
59520
59482
|
}
|
|
59521
59483
|
}
|
|
59522
59484
|
|
|
59523
|
-
supportsWholeGenome() {
|
|
59485
|
+
get supportsWholeGenome() {
|
|
59524
59486
|
return !this.config.indexURL && this.config.supportsWholeGenome !== false;
|
|
59525
59487
|
}
|
|
59526
59488
|
/**
|
|
@@ -60059,7 +60021,7 @@
|
|
|
60059
60021
|
|
|
60060
60022
|
const sortHandler = sort => {
|
|
60061
60023
|
const viewport = clickState.viewport;
|
|
60062
|
-
const features = viewport.
|
|
60024
|
+
const features = viewport.cachedFeatures;
|
|
60063
60025
|
this.sortSamples(sort.chr, sort.start, sort.end, sort.direction, features);
|
|
60064
60026
|
};
|
|
60065
60027
|
|
|
@@ -60079,7 +60041,7 @@
|
|
|
60079
60041
|
}];
|
|
60080
60042
|
}
|
|
60081
60043
|
|
|
60082
|
-
supportsWholeGenome() {
|
|
60044
|
+
get supportsWholeGenome() {
|
|
60083
60045
|
return (this.config.indexed === false || !this.config.indexURL) && this.config.supportsWholeGenome !== false;
|
|
60084
60046
|
}
|
|
60085
60047
|
|
|
@@ -60165,10 +60127,16 @@
|
|
|
60165
60127
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
60166
60128
|
* THE SOFTWARE.
|
|
60167
60129
|
*/
|
|
60130
|
+
/**
|
|
60131
|
+
* Represents 2 or more wig tracks overlaid on a common viewport.
|
|
60132
|
+
*/
|
|
60168
60133
|
|
|
60169
60134
|
class MergedTrack extends TrackBase {
|
|
60170
60135
|
constructor(config, browser) {
|
|
60171
60136
|
super(config, browser);
|
|
60137
|
+
this.type = "merged";
|
|
60138
|
+
this.featureType = 'numeric';
|
|
60139
|
+
this.paintAxis = paintAxis;
|
|
60172
60140
|
}
|
|
60173
60141
|
|
|
60174
60142
|
init(config) {
|
|
@@ -60179,21 +60147,6 @@
|
|
|
60179
60147
|
super.init(config);
|
|
60180
60148
|
}
|
|
60181
60149
|
|
|
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
60150
|
async postInit() {
|
|
60198
60151
|
this.tracks = [];
|
|
60199
60152
|
const p = [];
|
|
@@ -60215,45 +60168,83 @@
|
|
|
60215
60168
|
}
|
|
60216
60169
|
}
|
|
60217
60170
|
|
|
60218
|
-
this.
|
|
60171
|
+
this.flipAxis = this.config.flipAxis ? this.config.flipAxis : false;
|
|
60172
|
+
this.logScale = this.config.logScale ? this.config.logScale : false;
|
|
60173
|
+
this.autoscale = this.config.autoscale || this.config.max === undefined;
|
|
60174
|
+
|
|
60175
|
+
if (!this.autoscale) {
|
|
60176
|
+
this.dataRange = {
|
|
60177
|
+
min: this.config.min || 0,
|
|
60178
|
+
max: this.config.max
|
|
60179
|
+
};
|
|
60180
|
+
}
|
|
60181
|
+
|
|
60182
|
+
for (let t of this.tracks) {
|
|
60183
|
+
t.autoscale = false;
|
|
60184
|
+
t.dataRange = this.dataRange;
|
|
60185
|
+
}
|
|
60186
|
+
|
|
60187
|
+
this.height = this.config.height || 50;
|
|
60219
60188
|
return Promise.all(p);
|
|
60220
60189
|
}
|
|
60221
60190
|
|
|
60191
|
+
get height() {
|
|
60192
|
+
return this._height;
|
|
60193
|
+
}
|
|
60194
|
+
|
|
60195
|
+
set height(h) {
|
|
60196
|
+
this._height = h;
|
|
60197
|
+
|
|
60198
|
+
if (this.tracks) {
|
|
60199
|
+
for (let t of this.tracks) {
|
|
60200
|
+
t.height = h;
|
|
60201
|
+
t.config.height = h;
|
|
60202
|
+
}
|
|
60203
|
+
}
|
|
60204
|
+
}
|
|
60205
|
+
|
|
60206
|
+
menuItemList() {
|
|
60207
|
+
let items = [];
|
|
60208
|
+
|
|
60209
|
+
if (this.flipAxis !== undefined) {
|
|
60210
|
+
items.push({
|
|
60211
|
+
label: "Flip y-axis",
|
|
60212
|
+
click: () => {
|
|
60213
|
+
this.flipAxis = !this.flipAxis;
|
|
60214
|
+
this.trackView.repaintViews();
|
|
60215
|
+
}
|
|
60216
|
+
});
|
|
60217
|
+
}
|
|
60218
|
+
|
|
60219
|
+
items = items.concat(MenuUtils.numericDataMenuItems(this.trackView));
|
|
60220
|
+
return items;
|
|
60221
|
+
}
|
|
60222
|
+
|
|
60222
60223
|
async getFeatures(chr, bpStart, bpEnd, bpPerPixel) {
|
|
60223
60224
|
const promises = this.tracks.map(t => t.getFeatures(chr, bpStart, bpEnd, bpPerPixel));
|
|
60224
60225
|
return Promise.all(promises);
|
|
60225
60226
|
}
|
|
60226
60227
|
|
|
60227
60228
|
draw(options) {
|
|
60228
|
-
|
|
60229
|
-
mergedFeatures = options.features; // Array of feature arrays, 1 for each track
|
|
60229
|
+
const mergedFeatures = options.features; // Array of feature arrays, 1 for each track
|
|
60230
60230
|
|
|
60231
|
-
|
|
60231
|
+
if (this.autoscale) {
|
|
60232
|
+
this.dataRange = autoscale(options.referenceFrame.chr, mergedFeatures);
|
|
60233
|
+
}
|
|
60232
60234
|
|
|
60233
|
-
for (i = 0, len = this.tracks.length; i < len; i++) {
|
|
60234
|
-
trackOptions = Object.assign({}, options);
|
|
60235
|
+
for (let i = 0, len = this.tracks.length; i < len; i++) {
|
|
60236
|
+
const trackOptions = Object.assign({}, options);
|
|
60235
60237
|
trackOptions.features = mergedFeatures[i];
|
|
60236
|
-
this.tracks[i].dataRange = dataRange;
|
|
60238
|
+
this.tracks[i].dataRange = this.dataRange;
|
|
60239
|
+
this.tracks[i].flipAxis = this.flipAxis;
|
|
60240
|
+
this.tracks[i].logScale = this.logScale;
|
|
60241
|
+
this.tracks[i].graphType = this.graphType;
|
|
60237
60242
|
this.tracks[i].draw(trackOptions);
|
|
60238
60243
|
}
|
|
60239
60244
|
}
|
|
60240
60245
|
|
|
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
60246
|
popupData(clickState, features) {
|
|
60256
|
-
const featuresArray = features || clickState.viewport.
|
|
60247
|
+
const featuresArray = features || clickState.viewport.cachedFeatures;
|
|
60257
60248
|
|
|
60258
60249
|
if (featuresArray && featuresArray.length === this.tracks.length) {
|
|
60259
60250
|
// Array of feature arrays, 1 for each track
|
|
@@ -60270,40 +60261,24 @@
|
|
|
60270
60261
|
}
|
|
60271
60262
|
}
|
|
60272
60263
|
|
|
60273
|
-
supportsWholeGenome() {
|
|
60274
|
-
|
|
60275
|
-
return b;
|
|
60264
|
+
get supportsWholeGenome() {
|
|
60265
|
+
return this.tracks.every(track => track.supportsWholeGenome());
|
|
60276
60266
|
}
|
|
60277
60267
|
|
|
60278
60268
|
}
|
|
60279
60269
|
|
|
60280
60270
|
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 {
|
|
60271
|
+
let min = 0;
|
|
60272
|
+
let max = -Number.MAX_VALUE;
|
|
60298
60273
|
|
|
60299
|
-
|
|
60300
|
-
|
|
60274
|
+
for (let features of featureArrays) {
|
|
60275
|
+
for (let f of features) {
|
|
60301
60276
|
if (typeof f.value !== 'undefined' && !Number.isNaN(f.value)) {
|
|
60302
60277
|
min = Math.min(min, f.value);
|
|
60303
60278
|
max = Math.max(max, f.value);
|
|
60304
60279
|
}
|
|
60305
|
-
}
|
|
60306
|
-
}
|
|
60280
|
+
}
|
|
60281
|
+
}
|
|
60307
60282
|
|
|
60308
60283
|
return {
|
|
60309
60284
|
min: min,
|
|
@@ -60424,7 +60399,7 @@
|
|
|
60424
60399
|
return this;
|
|
60425
60400
|
}
|
|
60426
60401
|
|
|
60427
|
-
supportsWholeGenome() {
|
|
60402
|
+
get supportsWholeGenome() {
|
|
60428
60403
|
return true;
|
|
60429
60404
|
}
|
|
60430
60405
|
|
|
@@ -60845,7 +60820,7 @@
|
|
|
60845
60820
|
items = items.concat(MenuUtils.numericDataMenuItems(this.trackView));
|
|
60846
60821
|
}
|
|
60847
60822
|
|
|
60848
|
-
if (this.browser.circularView
|
|
60823
|
+
if (this.browser.circularView) {
|
|
60849
60824
|
items.push('<hr/>');
|
|
60850
60825
|
items.push({
|
|
60851
60826
|
label: 'Add interactions to circular view',
|
|
@@ -60862,7 +60837,7 @@
|
|
|
60862
60837
|
|
|
60863
60838
|
contextMenuItemList(clickState) {
|
|
60864
60839
|
// Experimental JBrowse feature
|
|
60865
|
-
if (this.browser.circularView
|
|
60840
|
+
if (this.browser.circularView) {
|
|
60866
60841
|
const viewport = clickState.viewport;
|
|
60867
60842
|
const list = [];
|
|
60868
60843
|
list.push({
|
|
@@ -60887,20 +60862,20 @@
|
|
|
60887
60862
|
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
60863
|
|
|
60889
60864
|
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
|
-
})
|
|
60865
|
+
if (inView.length === 0) return;
|
|
60866
|
+
const chords = makeBedPEChords(inView);
|
|
60867
|
+
sendChords(chords, this, refFrame, 0.5); //
|
|
60868
|
+
//
|
|
60869
|
+
// // for filtered set, distinguishing the chromosomes is more critical than tracks
|
|
60870
|
+
// const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? this.color : getChrColor(refFrame.chr), 0.5)
|
|
60871
|
+
// const trackColor = IGVColor.addAlpha(this.color, 0.5)
|
|
60872
|
+
//
|
|
60873
|
+
// // name the chord set to include locus and filtering information
|
|
60874
|
+
// const encodedName = this.name.replaceAll(' ', '%20')
|
|
60875
|
+
// const chordSetName = "all" === refFrame.chr ?
|
|
60876
|
+
// encodedName :
|
|
60877
|
+
// `${encodedName} (${refFrame.chr}:${refFrame.start}-${refFrame.end} ; range:${this.dataRange.min}-${this.dataRange.max})`
|
|
60878
|
+
// this.browser.circularView.addChords(chords, {track: chordSetName, color: chordSetColor, trackColor: trackColor})
|
|
60904
60879
|
}
|
|
60905
60880
|
|
|
60906
60881
|
doAutoscale(features) {
|
|
@@ -60987,7 +60962,7 @@
|
|
|
60987
60962
|
clickedFeatures(clickState, features) {
|
|
60988
60963
|
// We use the cached features rather than method to avoid async load. If the
|
|
60989
60964
|
// 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.
|
|
60965
|
+
const featureList = features || clickState.viewport.cachedFeatures;
|
|
60991
60966
|
const candidates = [];
|
|
60992
60967
|
|
|
60993
60968
|
if (featureList) {
|
|
@@ -61358,7 +61333,7 @@
|
|
|
61358
61333
|
return this;
|
|
61359
61334
|
}
|
|
61360
61335
|
|
|
61361
|
-
supportsWholeGenome() {
|
|
61336
|
+
get supportsWholeGenome() {
|
|
61362
61337
|
return this.config.indexed === false || this.config.supportsWholeGenome === true;
|
|
61363
61338
|
}
|
|
61364
61339
|
|
|
@@ -61573,7 +61548,7 @@
|
|
|
61573
61548
|
variantColor = "gray";
|
|
61574
61549
|
}
|
|
61575
61550
|
} else if (this._color) {
|
|
61576
|
-
variantColor =
|
|
61551
|
+
variantColor = this.color;
|
|
61577
61552
|
} else if ("NONVARIANT" === v.type) {
|
|
61578
61553
|
variantColor = this.nonRefColor;
|
|
61579
61554
|
} else if ("MIXED" === v.type) {
|
|
@@ -61585,6 +61560,10 @@
|
|
|
61585
61560
|
return variantColor;
|
|
61586
61561
|
}
|
|
61587
61562
|
|
|
61563
|
+
get color() {
|
|
61564
|
+
return this._color ? typeof this._color === "function" ? this._color(v) : this._color : this.defaultColor;
|
|
61565
|
+
}
|
|
61566
|
+
|
|
61588
61567
|
clickedFeatures(clickState, features) {
|
|
61589
61568
|
let featureList = super.clickedFeatures(clickState, features);
|
|
61590
61569
|
const vGap = this.displayMode === 'EXPANDED' ? this.expandedVGap : this.squishedVGap;
|
|
@@ -61845,29 +61824,15 @@
|
|
|
61845
61824
|
} // Experimental JBrowse circular view integration
|
|
61846
61825
|
|
|
61847
61826
|
|
|
61848
|
-
if (this.browser.circularView
|
|
61827
|
+
if (this.browser.circularView) {
|
|
61849
61828
|
menuItems.push('<hr>');
|
|
61850
61829
|
menuItems.push({
|
|
61851
61830
|
label: 'Add SVs to circular view',
|
|
61852
61831
|
click: () => {
|
|
61853
|
-
const inView = [];
|
|
61854
61832
|
|
|
61855
61833
|
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
|
-
}
|
|
61834
|
+
this.sendChordsForViewport(viewport);
|
|
61863
61835
|
}
|
|
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
61836
|
}
|
|
61872
61837
|
});
|
|
61873
61838
|
}
|
|
@@ -61877,26 +61842,26 @@
|
|
|
61877
61842
|
|
|
61878
61843
|
contextMenuItemList(clickState) {
|
|
61879
61844
|
// Experimental JBrowse circular view integration
|
|
61880
|
-
if (this.browser.circularView
|
|
61845
|
+
if (this.browser.circularView) {
|
|
61881
61846
|
const viewport = clickState.viewport;
|
|
61882
61847
|
const list = [];
|
|
61883
61848
|
list.push({
|
|
61884
61849
|
label: 'Add SVs to Circular View',
|
|
61885
61850
|
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
|
-
});
|
|
61851
|
+
this.sendChordsForViewport(viewport);
|
|
61894
61852
|
}
|
|
61895
61853
|
});
|
|
61896
61854
|
list.push('<hr/>');
|
|
61897
61855
|
return list;
|
|
61898
61856
|
}
|
|
61899
61857
|
}
|
|
61858
|
+
|
|
61859
|
+
sendChordsForViewport(viewport) {
|
|
61860
|
+
const refFrame = viewport.referenceFrame;
|
|
61861
|
+
const inView = "all" === refFrame.chr ? this.featureSource.getAllFeatures() : this.featureSource.featureCache.queryFeatures(refFrame.chr, refFrame.start, refFrame.end);
|
|
61862
|
+
const chords = makeVCFChords(inView);
|
|
61863
|
+
sendChords(chords, this, refFrame, 0.5);
|
|
61864
|
+
}
|
|
61900
61865
|
/**
|
|
61901
61866
|
* Create a "color by" checkbox menu item, optionally initially checked
|
|
61902
61867
|
* @param menuItem
|
|
@@ -62199,7 +62164,7 @@
|
|
|
62199
62164
|
|
|
62200
62165
|
|
|
62201
62166
|
popupData(clickState) {
|
|
62202
|
-
let features = clickState.viewport.
|
|
62167
|
+
let features = clickState.viewport.cachedFeatures;
|
|
62203
62168
|
if (!features || features.length === 0) return [];
|
|
62204
62169
|
const tolerance = 3;
|
|
62205
62170
|
const tissue = this.name;
|
|
@@ -62407,7 +62372,7 @@
|
|
|
62407
62372
|
return this;
|
|
62408
62373
|
}
|
|
62409
62374
|
|
|
62410
|
-
supportsWholeGenome() {
|
|
62375
|
+
get supportsWholeGenome() {
|
|
62411
62376
|
return true;
|
|
62412
62377
|
}
|
|
62413
62378
|
|
|
@@ -62545,7 +62510,7 @@
|
|
|
62545
62510
|
popupData(clickState) {
|
|
62546
62511
|
let data = [];
|
|
62547
62512
|
const track = clickState.viewport.trackView.track;
|
|
62548
|
-
const features = clickState.viewport.
|
|
62513
|
+
const features = clickState.viewport.cachedFeatures;
|
|
62549
62514
|
|
|
62550
62515
|
if (features) {
|
|
62551
62516
|
let count = 0;
|
|
@@ -62973,7 +62938,7 @@
|
|
|
62973
62938
|
return items;
|
|
62974
62939
|
}
|
|
62975
62940
|
|
|
62976
|
-
supportsWholeGenome() {
|
|
62941
|
+
get supportsWholeGenome() {
|
|
62977
62942
|
return false;
|
|
62978
62943
|
}
|
|
62979
62944
|
|
|
@@ -63234,7 +63199,7 @@
|
|
|
63234
63199
|
if (!this.featureCache) {
|
|
63235
63200
|
const options = buildOptions(this.config);
|
|
63236
63201
|
const data = await igvxhr.loadString(this.config.url, options);
|
|
63237
|
-
this.featureCache = new FeatureCache(parseBP(data), genome);
|
|
63202
|
+
this.featureCache = new FeatureCache$1(parseBP(data), genome);
|
|
63238
63203
|
return this.featureCache.queryFeatures(chr, start, end);
|
|
63239
63204
|
} else {
|
|
63240
63205
|
return this.featureCache.queryFeatures(chr, start, end);
|
|
@@ -63334,12 +63299,15 @@
|
|
|
63334
63299
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
63335
63300
|
* THE SOFTWARE.
|
|
63336
63301
|
*/
|
|
63302
|
+
/**
|
|
63303
|
+
* Class represents an ideogram of a chromsome cytobands. It is used for the header of a track panel.
|
|
63304
|
+
*
|
|
63305
|
+
*/
|
|
63337
63306
|
|
|
63338
63307
|
class IdeogramTrack {
|
|
63339
63308
|
constructor(browser) {
|
|
63340
63309
|
this.browser = browser;
|
|
63341
63310
|
this.type = 'ideogram';
|
|
63342
|
-
this.id = this.type;
|
|
63343
63311
|
this.height = 16;
|
|
63344
63312
|
this.order = Number.MIN_SAFE_INTEGER;
|
|
63345
63313
|
this.disableButtons = true;
|
|
@@ -63599,7 +63567,7 @@
|
|
|
63599
63567
|
return this;
|
|
63600
63568
|
}
|
|
63601
63569
|
|
|
63602
|
-
supportsWholeGenome() {
|
|
63570
|
+
get supportsWholeGenome() {
|
|
63603
63571
|
return false;
|
|
63604
63572
|
}
|
|
63605
63573
|
|
|
@@ -64477,6 +64445,15 @@
|
|
|
64477
64445
|
this.id = guid$2();
|
|
64478
64446
|
}
|
|
64479
64447
|
|
|
64448
|
+
extend(locus) {
|
|
64449
|
+
const newStart = Math.min(locus.start, this.start);
|
|
64450
|
+
const newEnd = Math.max(locus.end, this.end);
|
|
64451
|
+
const ratio = (newEnd - newStart) / (this.end - this.start);
|
|
64452
|
+
this.start = newStart;
|
|
64453
|
+
this.end = newEnd;
|
|
64454
|
+
this.bpPerPixel *= ratio;
|
|
64455
|
+
}
|
|
64456
|
+
|
|
64480
64457
|
calculateEnd(pixels) {
|
|
64481
64458
|
return this.start + this.bpPerPixel * pixels;
|
|
64482
64459
|
}
|
|
@@ -64561,7 +64538,7 @@
|
|
|
64561
64538
|
const viewChanged = start !== this.start || bpPerPixel !== this.bpPerPixel;
|
|
64562
64539
|
|
|
64563
64540
|
if (viewChanged) {
|
|
64564
|
-
await browser.updateViews(
|
|
64541
|
+
await browser.updateViews(true);
|
|
64565
64542
|
}
|
|
64566
64543
|
}
|
|
64567
64544
|
|
|
@@ -64631,23 +64608,6 @@
|
|
|
64631
64608
|
});
|
|
64632
64609
|
}
|
|
64633
64610
|
|
|
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
64611
|
const defaultNucleotideColors = {
|
|
64652
64612
|
"A": "rgb( 0, 200, 0)",
|
|
64653
64613
|
"C": "rgb( 0,0,200)",
|
|
@@ -65489,7 +65449,7 @@
|
|
|
65489
65449
|
}
|
|
65490
65450
|
}
|
|
65491
65451
|
|
|
65492
|
-
browser.
|
|
65452
|
+
browser.layoutChange();
|
|
65493
65453
|
});
|
|
65494
65454
|
}
|
|
65495
65455
|
|
|
@@ -65694,6 +65654,65 @@
|
|
|
65694
65654
|
button.addEventListener('click', () => browser.saveSVGtoFile({}));
|
|
65695
65655
|
};
|
|
65696
65656
|
|
|
65657
|
+
const viewportColumnManager = {
|
|
65658
|
+
createColumns: (columnContainer, count) => {
|
|
65659
|
+
for (let i = 0; i < count; i++) {
|
|
65660
|
+
if (0 === i) {
|
|
65661
|
+
createColumn(columnContainer, 'igv-column');
|
|
65662
|
+
} else {
|
|
65663
|
+
columnContainer.appendChild(div$1({
|
|
65664
|
+
class: 'igv-column-shim'
|
|
65665
|
+
}));
|
|
65666
|
+
createColumn(columnContainer, 'igv-column');
|
|
65667
|
+
}
|
|
65668
|
+
}
|
|
65669
|
+
},
|
|
65670
|
+
removeColumnAtIndex: (i, column) => {
|
|
65671
|
+
const shim = 0 === i ? column.nextElementSibling : column.previousElementSibling;
|
|
65672
|
+
column.remove();
|
|
65673
|
+
shim.remove();
|
|
65674
|
+
},
|
|
65675
|
+
insertAfter: referenceElement => {
|
|
65676
|
+
const shim = div$1({
|
|
65677
|
+
class: 'igv-column-shim'
|
|
65678
|
+
});
|
|
65679
|
+
insertElementAfter(shim, referenceElement);
|
|
65680
|
+
const column = div$1({
|
|
65681
|
+
class: 'igv-column'
|
|
65682
|
+
});
|
|
65683
|
+
insertElementAfter(column, shim);
|
|
65684
|
+
return column;
|
|
65685
|
+
},
|
|
65686
|
+
insertBefore: (referenceElement, count) => {
|
|
65687
|
+
for (let i = 0; i < count; i++) {
|
|
65688
|
+
const column = div$1({
|
|
65689
|
+
class: 'igv-column'
|
|
65690
|
+
});
|
|
65691
|
+
insertElementBefore(column, referenceElement);
|
|
65692
|
+
|
|
65693
|
+
if (count > 1 && i > 0) {
|
|
65694
|
+
const columnShim = div$1({
|
|
65695
|
+
class: 'igv-column-shim'
|
|
65696
|
+
});
|
|
65697
|
+
insertElementBefore(columnShim, column);
|
|
65698
|
+
}
|
|
65699
|
+
}
|
|
65700
|
+
},
|
|
65701
|
+
indexOfColumn: (columnContainer, column) => {
|
|
65702
|
+
const allColumns = columnContainer.querySelectorAll('.igv-column');
|
|
65703
|
+
|
|
65704
|
+
for (let i = 0; i < allColumns.length; i++) {
|
|
65705
|
+
const c = allColumns[i];
|
|
65706
|
+
|
|
65707
|
+
if (c === column) {
|
|
65708
|
+
return i;
|
|
65709
|
+
}
|
|
65710
|
+
}
|
|
65711
|
+
|
|
65712
|
+
return undefined;
|
|
65713
|
+
}
|
|
65714
|
+
};
|
|
65715
|
+
|
|
65697
65716
|
/*
|
|
65698
65717
|
* The MIT License (MIT)
|
|
65699
65718
|
*
|
|
@@ -65944,7 +65963,7 @@
|
|
|
65944
65963
|
}
|
|
65945
65964
|
}
|
|
65946
65965
|
|
|
65947
|
-
supportsWholeGenome() {
|
|
65966
|
+
get supportsWholeGenome() {
|
|
65948
65967
|
return true;
|
|
65949
65968
|
}
|
|
65950
65969
|
|
|
@@ -66476,7 +66495,9 @@
|
|
|
66476
66495
|
// deferred because ideogram and ruler are treated as "tracks", and tracks require a reference frame
|
|
66477
66496
|
|
|
66478
66497
|
if (false !== session.showIdeogram) {
|
|
66479
|
-
|
|
66498
|
+
const ideogramTrack = new IdeogramTrack(this);
|
|
66499
|
+
ideogramTrack.id = 'ideogram';
|
|
66500
|
+
this.trackViews.push(new TrackView(this, this.columnContainer, ideogramTrack));
|
|
66480
66501
|
}
|
|
66481
66502
|
|
|
66482
66503
|
if (false !== session.showRuler) {
|
|
@@ -66526,7 +66547,12 @@
|
|
|
66526
66547
|
}
|
|
66527
66548
|
}
|
|
66528
66549
|
|
|
66529
|
-
await this.loadTrackList(trackConfigurations);
|
|
66550
|
+
await this.loadTrackList(trackConfigurations); // The ruler and ideogram tracks are not explicitly loaded, but needs updated nonetheless.
|
|
66551
|
+
|
|
66552
|
+
for (let rtv of this.trackViews.filter(tv => tv.track.type === 'ruler' || tv.track.type === 'ideogram')) {
|
|
66553
|
+
rtv.updateViews();
|
|
66554
|
+
}
|
|
66555
|
+
|
|
66530
66556
|
this.updateUIWithReferenceFrameList();
|
|
66531
66557
|
}
|
|
66532
66558
|
|
|
@@ -66691,26 +66717,22 @@
|
|
|
66691
66717
|
}
|
|
66692
66718
|
|
|
66693
66719
|
async loadTrackList(configList) {
|
|
66694
|
-
|
|
66695
|
-
const promises = [];
|
|
66696
|
-
|
|
66697
|
-
for (let config of configList) {
|
|
66698
|
-
promises.push(this.loadTrack(config, false));
|
|
66699
|
-
}
|
|
66720
|
+
const promises = [];
|
|
66700
66721
|
|
|
66701
|
-
|
|
66702
|
-
|
|
66703
|
-
|
|
66704
|
-
});
|
|
66722
|
+
for (let config of configList) {
|
|
66723
|
+
promises.push(this.loadTrack(config));
|
|
66724
|
+
}
|
|
66705
66725
|
|
|
66706
|
-
|
|
66707
|
-
|
|
66708
|
-
|
|
66726
|
+
const loadedTracks = await Promise.all(promises);
|
|
66727
|
+
const groupAutoscaleViews = this.trackViews.filter(function (trackView) {
|
|
66728
|
+
return trackView.track.autoscaleGroup;
|
|
66729
|
+
});
|
|
66709
66730
|
|
|
66710
|
-
|
|
66711
|
-
|
|
66712
|
-
await this.resize();
|
|
66731
|
+
if (groupAutoscaleViews.length > 0) {
|
|
66732
|
+
this.updateViews();
|
|
66713
66733
|
}
|
|
66734
|
+
|
|
66735
|
+
return loadedTracks;
|
|
66714
66736
|
}
|
|
66715
66737
|
|
|
66716
66738
|
async loadROI(config) {
|
|
@@ -66724,7 +66746,9 @@
|
|
|
66724
66746
|
}
|
|
66725
66747
|
} else {
|
|
66726
66748
|
this.roi.push(new ROI(config, this.genome));
|
|
66727
|
-
}
|
|
66749
|
+
} // Force reload all views (force = true) to insure ROI features are loaded. Wasteful but this function is
|
|
66750
|
+
// rarely called.
|
|
66751
|
+
|
|
66728
66752
|
|
|
66729
66753
|
await this.updateViews(true);
|
|
66730
66754
|
}
|
|
@@ -66738,7 +66762,7 @@
|
|
|
66738
66762
|
}
|
|
66739
66763
|
|
|
66740
66764
|
for (let tv of this.trackViews) {
|
|
66741
|
-
tv.
|
|
66765
|
+
tv.repaintViews();
|
|
66742
66766
|
}
|
|
66743
66767
|
}
|
|
66744
66768
|
|
|
@@ -66746,7 +66770,7 @@
|
|
|
66746
66770
|
this.roi = [];
|
|
66747
66771
|
|
|
66748
66772
|
for (let tv of this.trackViews) {
|
|
66749
|
-
tv.
|
|
66773
|
+
tv.repaintViews();
|
|
66750
66774
|
}
|
|
66751
66775
|
}
|
|
66752
66776
|
|
|
@@ -66762,25 +66786,13 @@
|
|
|
66762
66786
|
/**
|
|
66763
66787
|
* Return a promise to load a track.
|
|
66764
66788
|
*
|
|
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
66789
|
* @param config
|
|
66778
66790
|
* @param doResize - undefined by default
|
|
66779
66791
|
* @returns {*}
|
|
66780
66792
|
*/
|
|
66781
66793
|
|
|
66782
66794
|
|
|
66783
|
-
async loadTrack(config
|
|
66795
|
+
async loadTrack(config) {
|
|
66784
66796
|
// config might be json
|
|
66785
66797
|
if (isString$3(config)) {
|
|
66786
66798
|
config = JSON.parse(config);
|
|
@@ -66845,11 +66857,6 @@
|
|
|
66845
66857
|
|
|
66846
66858
|
msg += ": " + config.url;
|
|
66847
66859
|
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
66860
|
}
|
|
66854
66861
|
}
|
|
66855
66862
|
/**
|
|
@@ -67065,8 +67072,18 @@
|
|
|
67065
67072
|
trackView.setTrackHeight(newHeight);
|
|
67066
67073
|
});
|
|
67067
67074
|
}
|
|
67075
|
+
/**
|
|
67076
|
+
* API function to signal that this browser visibility has changed, e.g. from hiding/showing in a tab interface.
|
|
67077
|
+
*
|
|
67078
|
+
* @returns {Promise<void>}
|
|
67079
|
+
*/
|
|
67080
|
+
|
|
67068
67081
|
|
|
67069
67082
|
async visibilityChange() {
|
|
67083
|
+
this.layoutChange();
|
|
67084
|
+
}
|
|
67085
|
+
|
|
67086
|
+
async layoutChange() {
|
|
67070
67087
|
const status = this.referenceFrameList.find(referenceFrame => referenceFrame.bpPerPixel < 0);
|
|
67071
67088
|
|
|
67072
67089
|
if (status) {
|
|
@@ -67082,43 +67099,11 @@
|
|
|
67082
67099
|
this.navbarManager.navbarDidResize(this.$navigation.width(), isWGV);
|
|
67083
67100
|
}
|
|
67084
67101
|
|
|
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();
|
|
67102
|
+
resize.call(this);
|
|
67103
|
+
await this.updateViews();
|
|
67119
67104
|
}
|
|
67120
67105
|
|
|
67121
|
-
async updateViews(
|
|
67106
|
+
async updateViews() {
|
|
67122
67107
|
const trackViews = this.trackViews;
|
|
67123
67108
|
this.updateLocusSearchWidget();
|
|
67124
67109
|
|
|
@@ -67129,7 +67114,7 @@
|
|
|
67129
67114
|
|
|
67130
67115
|
if (this.dragObject) {
|
|
67131
67116
|
for (let trackView of trackViews) {
|
|
67132
|
-
await trackView.updateViews(
|
|
67117
|
+
await trackView.updateViews();
|
|
67133
67118
|
}
|
|
67134
67119
|
} else {
|
|
67135
67120
|
// Group autoscale
|
|
@@ -67178,19 +67163,25 @@
|
|
|
67178
67163
|
for (let trackView of groupTrackViews) {
|
|
67179
67164
|
trackView.track.dataRange = dataRange;
|
|
67180
67165
|
trackView.track.autoscale = false;
|
|
67181
|
-
p.push(trackView.updateViews(
|
|
67166
|
+
p.push(trackView.updateViews());
|
|
67182
67167
|
}
|
|
67183
67168
|
|
|
67184
67169
|
await Promise.all(p);
|
|
67185
67170
|
}
|
|
67186
67171
|
}
|
|
67187
67172
|
|
|
67188
|
-
await Promise.all(otherTracks.map(tv => tv.updateViews(
|
|
67173
|
+
await Promise.all(otherTracks.map(tv => tv.updateViews())); // for (let trackView of otherTracks) {
|
|
67189
67174
|
// await trackView.updateViews(force);
|
|
67190
67175
|
// }
|
|
67191
67176
|
}
|
|
67192
67177
|
}
|
|
67193
67178
|
|
|
67179
|
+
repaintViews() {
|
|
67180
|
+
for (let trackView of this.trackViews) {
|
|
67181
|
+
trackView.repaintViews();
|
|
67182
|
+
}
|
|
67183
|
+
}
|
|
67184
|
+
|
|
67194
67185
|
updateLocusSearchWidget() {
|
|
67195
67186
|
const referenceFrameList = this.referenceFrameList; // Update end position of reference frames based on pixel widths. This is hacky, but its been done here
|
|
67196
67187
|
// for a long time, although indirectly.
|
|
@@ -67248,41 +67239,53 @@
|
|
|
67248
67239
|
referenceFrame.zoomWithScaleFactor(this, scaleFactor, viewportWidth, centerBPOrUndefined);
|
|
67249
67240
|
}
|
|
67250
67241
|
}
|
|
67242
|
+
/**
|
|
67243
|
+
* Add a new multi-locus panel for the specified region
|
|
67244
|
+
* @param chr
|
|
67245
|
+
* @param start
|
|
67246
|
+
* @param end
|
|
67247
|
+
* @param referenceFrameLeft - optional, if supplied new panel should be placed to the immediate right
|
|
67248
|
+
*/
|
|
67249
|
+
|
|
67251
67250
|
|
|
67252
|
-
async
|
|
67251
|
+
async addMultiLocusPanel(chr, start, end, referenceFrameLeft) {
|
|
67253
67252
|
// account for reduced viewport width as a result of adding right mate pair panel
|
|
67254
67253
|
const viewportWidth = this.calculateViewportWidth(1 + this.referenceFrameList.length);
|
|
67255
67254
|
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
67255
|
|
|
67258
|
-
|
|
67259
|
-
|
|
67256
|
+
for (let refFrame of this.referenceFrameList) {
|
|
67257
|
+
refFrame.bpPerPixel *= scaleFactor;
|
|
67258
|
+
}
|
|
67259
|
+
|
|
67260
|
+
const bpp = (end - start) / viewportWidth;
|
|
67261
|
+
const newReferenceFrame = new ReferenceFrame(this.genome, chr, start, end, bpp);
|
|
67262
|
+
const indexLeft = referenceFrameLeft ? this.referenceFrameList.indexOf(referenceFrameLeft) : this.referenceFrameList.length - 1;
|
|
67263
|
+
const indexRight = 1 + indexLeft; // TODO -- this is really ugly
|
|
67260
67264
|
|
|
67261
|
-
const indexLeft = this.referenceFrameList.indexOf(referenceFrameLeft);
|
|
67262
|
-
const indexRight = 1 + this.referenceFrameList.indexOf(referenceFrameLeft);
|
|
67263
67265
|
const {
|
|
67264
67266
|
$viewport
|
|
67265
67267
|
} = this.trackViews[0].viewports[indexLeft];
|
|
67266
67268
|
const viewportColumn = viewportColumnManager.insertAfter($viewport.get(0).parentElement);
|
|
67267
67269
|
|
|
67268
67270
|
if (indexRight === this.referenceFrameList.length) {
|
|
67269
|
-
this.referenceFrameList.push(
|
|
67271
|
+
this.referenceFrameList.push(newReferenceFrame);
|
|
67270
67272
|
|
|
67271
67273
|
for (let trackView of this.trackViews) {
|
|
67272
|
-
const viewport = createViewport(trackView, viewportColumn,
|
|
67274
|
+
const viewport = createViewport(trackView, viewportColumn, newReferenceFrame);
|
|
67273
67275
|
trackView.viewports.push(viewport);
|
|
67274
67276
|
}
|
|
67275
67277
|
} else {
|
|
67276
|
-
this.referenceFrameList.splice(indexRight, 0,
|
|
67278
|
+
this.referenceFrameList.splice(indexRight, 0, newReferenceFrame);
|
|
67277
67279
|
|
|
67278
67280
|
for (let trackView of this.trackViews) {
|
|
67279
|
-
const viewport = createViewport(trackView, viewportColumn,
|
|
67281
|
+
const viewport = createViewport(trackView, viewportColumn, newReferenceFrame);
|
|
67280
67282
|
trackView.viewports.splice(indexRight, 0, viewport);
|
|
67281
67283
|
}
|
|
67282
67284
|
}
|
|
67283
67285
|
|
|
67284
67286
|
this.centerLineList = this.createCenterLineList(this.columnContainer);
|
|
67285
|
-
|
|
67287
|
+
resize.call(this);
|
|
67288
|
+
await this.updateViews(true);
|
|
67286
67289
|
}
|
|
67287
67290
|
|
|
67288
67291
|
async removeMultiLocusPanel(referenceFrame) {
|
|
@@ -67311,8 +67314,15 @@
|
|
|
67311
67314
|
const scaleFactor = this.calculateViewportWidth(1 + this.referenceFrameList.length) / this.calculateViewportWidth(this.referenceFrameList.length);
|
|
67312
67315
|
await this.rescaleForMultiLocus(scaleFactor);
|
|
67313
67316
|
}
|
|
67317
|
+
/**
|
|
67318
|
+
* Goto the locus represented by the selected referenceFrame, discarding all other panels
|
|
67319
|
+
*
|
|
67320
|
+
* @param referenceFrame
|
|
67321
|
+
* @returns {Promise<void>}
|
|
67322
|
+
*/
|
|
67323
|
+
|
|
67314
67324
|
|
|
67315
|
-
async
|
|
67325
|
+
async gotoMultilocusPanel(referenceFrame) {
|
|
67316
67326
|
const referenceFrameIndex = this.referenceFrameList.indexOf(referenceFrame); // Remove columns for unselected panels
|
|
67317
67327
|
|
|
67318
67328
|
this.columnContainer.querySelectorAll('.igv-column').forEach((column, c) => {
|
|
@@ -67360,7 +67370,7 @@
|
|
|
67360
67370
|
|
|
67361
67371
|
this.centerLineList = this.createCenterLineList(this.columnContainer);
|
|
67362
67372
|
this.updateUIWithReferenceFrameList();
|
|
67363
|
-
await this.updateViews(
|
|
67373
|
+
await this.updateViews();
|
|
67364
67374
|
}
|
|
67365
67375
|
/**
|
|
67366
67376
|
* @deprecated This is a deprecated method with no known usages. To be removed in a future release.
|
|
@@ -67613,20 +67623,6 @@
|
|
|
67613
67623
|
const surl = (idx > 0 ? path.substring(0, idx) : path) + "?sessionURL=blob:" + this.compressedSession();
|
|
67614
67624
|
return surl;
|
|
67615
67625
|
}
|
|
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
67626
|
/**
|
|
67631
67627
|
* Record a mouse click on a specific viewport. This might be the start of a drag operation. Dragging
|
|
67632
67628
|
* (panning) is handled here so that the mouse can move out of a specific viewport (e.g. stray into another
|
|
@@ -67661,10 +67657,22 @@
|
|
|
67661
67657
|
this.fireEvent('trackdragend');
|
|
67662
67658
|
}
|
|
67663
67659
|
}
|
|
67660
|
+
/**
|
|
67661
|
+
* Track drag here refers to vertical dragging to reorder tracks, not horizontal panning.
|
|
67662
|
+
*
|
|
67663
|
+
* @param trackView
|
|
67664
|
+
*/
|
|
67665
|
+
|
|
67664
67666
|
|
|
67665
67667
|
startTrackDrag(trackView) {
|
|
67666
67668
|
this.dragTrack = trackView;
|
|
67667
67669
|
}
|
|
67670
|
+
/**
|
|
67671
|
+
* Track drag here refers to vertical dragging to reorder tracks, not horizontal panning.
|
|
67672
|
+
*
|
|
67673
|
+
* @param dragDestination
|
|
67674
|
+
*/
|
|
67675
|
+
|
|
67668
67676
|
|
|
67669
67677
|
updateTrackDrag(dragDestination) {
|
|
67670
67678
|
if (dragDestination && this.dragTrack) {
|
|
@@ -67738,12 +67746,9 @@
|
|
|
67738
67746
|
}
|
|
67739
67747
|
|
|
67740
67748
|
addWindowResizeHandler() {
|
|
67741
|
-
this.
|
|
67749
|
+
// Create a copy of the prototype "resize" function bound to this instance. Neccessary to support removing.
|
|
67750
|
+
this.boundWindowResizeHandler = resize.bind(this);
|
|
67742
67751
|
window.addEventListener('resize', this.boundWindowResizeHandler);
|
|
67743
|
-
|
|
67744
|
-
function windowResizeHandler() {
|
|
67745
|
-
this.resize();
|
|
67746
|
-
}
|
|
67747
67752
|
}
|
|
67748
67753
|
|
|
67749
67754
|
removeWindowResizeHandler() {
|
|
@@ -67815,6 +67820,8 @@
|
|
|
67815
67820
|
}
|
|
67816
67821
|
|
|
67817
67822
|
createCircularView(container, show) {
|
|
67823
|
+
show = show === true; // convert undefined to boolean
|
|
67824
|
+
|
|
67818
67825
|
this.circularView = createCircularView(container, this);
|
|
67819
67826
|
this.circularViewControl = new CircularViewControl(this.$toggle_button_container.get(0), this);
|
|
67820
67827
|
this.circularView.setAssembly({
|
|
@@ -67822,7 +67829,8 @@
|
|
|
67822
67829
|
id: this.genome.id,
|
|
67823
67830
|
chromosomes: makeCircViewChromosomes(this.genome)
|
|
67824
67831
|
});
|
|
67825
|
-
this.circularViewVisible = show
|
|
67832
|
+
this.circularViewVisible = show;
|
|
67833
|
+
return this.circularView;
|
|
67826
67834
|
}
|
|
67827
67835
|
|
|
67828
67836
|
get circularViewVisible() {
|
|
@@ -67837,6 +67845,48 @@
|
|
|
67837
67845
|
}
|
|
67838
67846
|
|
|
67839
67847
|
}
|
|
67848
|
+
/**
|
|
67849
|
+
* Function called win window is resized, or visibility changed (e.g. "show" from a tab). This is a function rather
|
|
67850
|
+
* than class method because it needs to be copied and bound to specific instances of browser to support listener
|
|
67851
|
+
* removal
|
|
67852
|
+
*
|
|
67853
|
+
* @returns {Promise<void>}
|
|
67854
|
+
*/
|
|
67855
|
+
|
|
67856
|
+
|
|
67857
|
+
async function resize() {
|
|
67858
|
+
const viewportWidth = this.calculateViewportWidth(this.referenceFrameList.length);
|
|
67859
|
+
|
|
67860
|
+
for (let referenceFrame of this.referenceFrameList) {
|
|
67861
|
+
const index = this.referenceFrameList.indexOf(referenceFrame);
|
|
67862
|
+
const {
|
|
67863
|
+
chr,
|
|
67864
|
+
genome
|
|
67865
|
+
} = referenceFrame;
|
|
67866
|
+
const {
|
|
67867
|
+
bpLength
|
|
67868
|
+
} = genome.getChromosome(referenceFrame.chr);
|
|
67869
|
+
const viewportWidthBP = referenceFrame.toBP(viewportWidth); // viewportWidthBP > bpLength occurs when locus is full chromosome and user widens browser
|
|
67870
|
+
|
|
67871
|
+
if (GenomeUtils.isWholeGenomeView(chr) || viewportWidthBP > bpLength) {
|
|
67872
|
+
// console.log(`${ Date.now() } Recalc referenceFrame(${ index }) bpp. viewport ${ StringUtils.numberFormatter(viewportWidthBP) } > ${ StringUtils.numberFormatter(bpLength) }.`)
|
|
67873
|
+
referenceFrame.bpPerPixel = bpLength / viewportWidth;
|
|
67874
|
+
} else {
|
|
67875
|
+
// console.log(`${ Date.now() } Recalc referenceFrame(${ index }) end.`)
|
|
67876
|
+
referenceFrame.end = referenceFrame.start + referenceFrame.toBP(viewportWidth);
|
|
67877
|
+
}
|
|
67878
|
+
|
|
67879
|
+
for (let {
|
|
67880
|
+
viewports
|
|
67881
|
+
} of this.trackViews) {
|
|
67882
|
+
viewports[index].setWidth(viewportWidth);
|
|
67883
|
+
}
|
|
67884
|
+
}
|
|
67885
|
+
|
|
67886
|
+
this.updateUIWithReferenceFrameList(); //TODO -- update view only if needed. Reducing size never needed. Increasing size maybe
|
|
67887
|
+
|
|
67888
|
+
await this.updateViews(true);
|
|
67889
|
+
}
|
|
67840
67890
|
|
|
67841
67891
|
function handleMouseMove(e) {
|
|
67842
67892
|
e.preventDefault();
|