igv 2.11.2 → 2.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -10
- package/dist/igv.esm.js +993 -1118
- package/dist/igv.esm.min.js +9 -9
- package/dist/igv.esm.min.js.map +1 -1
- package/dist/igv.js +982 -1041
- package/dist/igv.min.js +10 -10
- package/dist/igv.min.js.map +1 -1
- package/package.json +4 -4
package/dist/igv.esm.js
CHANGED
|
@@ -17624,7 +17624,7 @@ function Node(interval) {
|
|
|
17624
17624
|
* @constructor
|
|
17625
17625
|
*/
|
|
17626
17626
|
|
|
17627
|
-
class FeatureCache {
|
|
17627
|
+
class FeatureCache$1 {
|
|
17628
17628
|
|
|
17629
17629
|
constructor(featureList, genome, range) {
|
|
17630
17630
|
|
|
@@ -19854,6 +19854,7 @@ function inferTrackType(config) {
|
|
|
19854
19854
|
return "alignment"
|
|
19855
19855
|
case "bedpe":
|
|
19856
19856
|
case "bedpe-loop":
|
|
19857
|
+
case "biginteract":
|
|
19857
19858
|
return "interact"
|
|
19858
19859
|
case "bp":
|
|
19859
19860
|
return "arc"
|
|
@@ -19862,7 +19863,6 @@ function inferTrackType(config) {
|
|
|
19862
19863
|
case "bed":
|
|
19863
19864
|
case "bigbed":
|
|
19864
19865
|
case "bb":
|
|
19865
|
-
case "biginteract":
|
|
19866
19866
|
return "bedtype"
|
|
19867
19867
|
default:
|
|
19868
19868
|
return "annotation"
|
|
@@ -20113,6 +20113,38 @@ function isSecureContext() {
|
|
|
20113
20113
|
return window.location.protocol === "https:" || window.location.hostname === "localhost"
|
|
20114
20114
|
}
|
|
20115
20115
|
|
|
20116
|
+
const pairs =
|
|
20117
|
+
[
|
|
20118
|
+
['A', 'T'],
|
|
20119
|
+
['G', 'C'],
|
|
20120
|
+
['Y', 'R'],
|
|
20121
|
+
['W', 'S'],
|
|
20122
|
+
['K', 'M'],
|
|
20123
|
+
['D', 'H'],
|
|
20124
|
+
['B', 'V']
|
|
20125
|
+
];
|
|
20126
|
+
|
|
20127
|
+
const complements = new Map();
|
|
20128
|
+
for (let p of pairs) {
|
|
20129
|
+
const p1 = p[0];
|
|
20130
|
+
const p2 = p[1];
|
|
20131
|
+
complements.set(p1, p2);
|
|
20132
|
+
complements.set(p2, p1);
|
|
20133
|
+
complements.set(p1.toLowerCase(), p2.toLowerCase());
|
|
20134
|
+
complements.set(p2.toLowerCase(), p1.toLowerCase());
|
|
20135
|
+
}
|
|
20136
|
+
|
|
20137
|
+
function reverseComplementSequence(sequence) {
|
|
20138
|
+
|
|
20139
|
+
let comp = '';
|
|
20140
|
+
let idx = sequence.length;
|
|
20141
|
+
while (idx-- > 0) {
|
|
20142
|
+
const base = sequence[idx];
|
|
20143
|
+
comp += complements.has(base) ? complements.get(base) : base;
|
|
20144
|
+
}
|
|
20145
|
+
return comp
|
|
20146
|
+
}
|
|
20147
|
+
|
|
20116
20148
|
/*
|
|
20117
20149
|
* The MIT License (MIT)
|
|
20118
20150
|
*
|
|
@@ -20237,7 +20269,6 @@ class SequenceTrack {
|
|
|
20237
20269
|
}
|
|
20238
20270
|
|
|
20239
20271
|
menuItemList() {
|
|
20240
|
-
|
|
20241
20272
|
return [
|
|
20242
20273
|
{
|
|
20243
20274
|
name: this.reversed ? "Forward" : "Reverse",
|
|
@@ -20272,17 +20303,20 @@ class SequenceTrack {
|
|
|
20272
20303
|
contextMenuItemList(clickState) {
|
|
20273
20304
|
const viewport = clickState.viewport;
|
|
20274
20305
|
if (viewport.referenceFrame.bpPerPixel <= 1) {
|
|
20306
|
+
const pixelWidth = viewport.getWidth();
|
|
20307
|
+
const bpWindow = pixelWidth * viewport.referenceFrame.bpPerPixel;
|
|
20308
|
+
const chr = viewport.referenceFrame.chr;
|
|
20309
|
+
const start = Math.floor(viewport.referenceFrame.start);
|
|
20310
|
+
const end = Math.ceil(start + bpWindow);
|
|
20275
20311
|
const items = [
|
|
20276
20312
|
{
|
|
20277
|
-
label: 'View visible sequence...',
|
|
20313
|
+
label: this.reversed ? 'View visible sequence (reversed)...' : 'View visible sequence...',
|
|
20278
20314
|
click: async () => {
|
|
20279
|
-
|
|
20280
|
-
|
|
20281
|
-
|
|
20282
|
-
|
|
20283
|
-
|
|
20284
|
-
const sequence = await this.browser.genome.sequence.getSequence(chr, start, end);
|
|
20285
|
-
Alert.presentAlert(sequence);
|
|
20315
|
+
let seq = await this.browser.genome.sequence.getSequence(chr, start, end);
|
|
20316
|
+
if (this.reversed) {
|
|
20317
|
+
seq = reverseComplementSequence(seq);
|
|
20318
|
+
}
|
|
20319
|
+
Alert.presentAlert(seq);
|
|
20286
20320
|
}
|
|
20287
20321
|
}
|
|
20288
20322
|
];
|
|
@@ -20290,14 +20324,18 @@ class SequenceTrack {
|
|
|
20290
20324
|
items.push({
|
|
20291
20325
|
label: 'Copy visible sequence',
|
|
20292
20326
|
click: async () => {
|
|
20293
|
-
|
|
20294
|
-
|
|
20295
|
-
|
|
20296
|
-
|
|
20297
|
-
|
|
20298
|
-
|
|
20299
|
-
|
|
20327
|
+
let seq = await this.browser.genome.sequence.getSequence(chr, start, end);
|
|
20328
|
+
if (this.reversed) {
|
|
20329
|
+
seq = reverseComplementSequence(seq);
|
|
20330
|
+
}
|
|
20331
|
+
try {
|
|
20332
|
+
await navigator.clipboard.writeText(seq);
|
|
20333
|
+
} catch (e) {
|
|
20334
|
+
console.error(e);
|
|
20335
|
+
Alert.presentAlert(`error copying sequence to clipboard ${e}`);
|
|
20336
|
+
}
|
|
20300
20337
|
}
|
|
20338
|
+
|
|
20301
20339
|
});
|
|
20302
20340
|
}
|
|
20303
20341
|
items.push('<hr/>');
|
|
@@ -20441,7 +20479,7 @@ class SequenceTrack {
|
|
|
20441
20479
|
}
|
|
20442
20480
|
}
|
|
20443
20481
|
|
|
20444
|
-
supportsWholeGenome() {
|
|
20482
|
+
get supportsWholeGenome() {
|
|
20445
20483
|
return false
|
|
20446
20484
|
}
|
|
20447
20485
|
|
|
@@ -20515,13 +20553,13 @@ class Viewport {
|
|
|
20515
20553
|
this.$content.height(this.$viewport.height());
|
|
20516
20554
|
this.contentDiv = this.$content.get(0);
|
|
20517
20555
|
|
|
20518
|
-
this.$canvas =
|
|
20519
|
-
this.$content.append(this.$canvas)
|
|
20520
|
-
|
|
20521
|
-
this.canvas = this.$canvas.get(0)
|
|
20522
|
-
this.ctx = this.canvas.getContext("2d")
|
|
20556
|
+
// this.$canvas = $('<canvas>')
|
|
20557
|
+
// this.$content.append(this.$canvas)
|
|
20558
|
+
//
|
|
20559
|
+
// this.canvas = this.$canvas.get(0)
|
|
20560
|
+
// this.ctx = this.canvas.getContext("2d")
|
|
20523
20561
|
|
|
20524
|
-
this.
|
|
20562
|
+
this.$viewport.width(width);
|
|
20525
20563
|
|
|
20526
20564
|
this.initializationHelper();
|
|
20527
20565
|
|
|
@@ -20588,14 +20626,13 @@ class Viewport {
|
|
|
20588
20626
|
console.log('Viewport - draw(drawConfiguration, features, roiFeatures)');
|
|
20589
20627
|
}
|
|
20590
20628
|
|
|
20591
|
-
checkContentHeight() {
|
|
20629
|
+
checkContentHeight(features) {
|
|
20592
20630
|
|
|
20593
20631
|
let track = this.trackView.track;
|
|
20594
|
-
|
|
20632
|
+
features = features || this.cachedFeatures;
|
|
20595
20633
|
if ("FILL" === track.displayMode) {
|
|
20596
20634
|
this.setContentHeight(this.$viewport.height());
|
|
20597
20635
|
} else if (typeof track.computePixelHeight === 'function') {
|
|
20598
|
-
let features = this.cachedFeatures;
|
|
20599
20636
|
if (features && features.length > 0) {
|
|
20600
20637
|
let requiredContentHeight = track.computePixelHeight(features);
|
|
20601
20638
|
let currentContentHeight = this.$content.height();
|
|
@@ -20611,12 +20648,10 @@ class Viewport {
|
|
|
20611
20648
|
}
|
|
20612
20649
|
|
|
20613
20650
|
setContentHeight(contentHeight) {
|
|
20651
|
+
|
|
20614
20652
|
// Maximum height of a canvas is ~32,000 pixels on Chrome, possibly smaller on other platforms
|
|
20615
20653
|
contentHeight = Math.min(contentHeight, 32000);
|
|
20616
|
-
|
|
20617
20654
|
this.$content.height(contentHeight);
|
|
20618
|
-
|
|
20619
|
-
if (this.tile) this.tile.invalidate = true;
|
|
20620
20655
|
}
|
|
20621
20656
|
|
|
20622
20657
|
isLoading() {
|
|
@@ -20633,8 +20668,6 @@ class Viewport {
|
|
|
20633
20668
|
|
|
20634
20669
|
setWidth(width) {
|
|
20635
20670
|
this.$viewport.width(width);
|
|
20636
|
-
this.canvas.style.width = (`${width}px`);
|
|
20637
|
-
this.canvas.setAttribute('width', width);
|
|
20638
20671
|
}
|
|
20639
20672
|
|
|
20640
20673
|
getWidth() {
|
|
@@ -20664,8 +20697,6 @@ class Viewport {
|
|
|
20664
20697
|
this.popover.dispose();
|
|
20665
20698
|
}
|
|
20666
20699
|
|
|
20667
|
-
this.removeMouseHandlers();
|
|
20668
|
-
|
|
20669
20700
|
this.$viewport.get(0).remove();
|
|
20670
20701
|
|
|
20671
20702
|
// Null out all properties -- this should not be neccessary, but just in case there is a
|
|
@@ -22761,7 +22792,7 @@ const Cytoband = function (start, end, name, typestain) {
|
|
|
22761
22792
|
}
|
|
22762
22793
|
};
|
|
22763
22794
|
|
|
22764
|
-
const _version = "2.
|
|
22795
|
+
const _version = "2.12.0";
|
|
22765
22796
|
function version() {
|
|
22766
22797
|
return _version
|
|
22767
22798
|
}
|
|
@@ -23115,6 +23146,7 @@ class Genome {
|
|
|
23115
23146
|
}
|
|
23116
23147
|
|
|
23117
23148
|
async getSequence(chr, start, end) {
|
|
23149
|
+
chr = this.getChromosomeName(chr);
|
|
23118
23150
|
return this.sequence.getSequence(chr, start, end)
|
|
23119
23151
|
}
|
|
23120
23152
|
}
|
|
@@ -23250,28 +23282,27 @@ class TrackViewport extends Viewport {
|
|
|
23250
23282
|
this.$viewport.append(this.$spinner);
|
|
23251
23283
|
this.$spinner.append($$1('<div>'));
|
|
23252
23284
|
|
|
23253
|
-
const
|
|
23254
|
-
|
|
23285
|
+
const track = this.trackView.track;
|
|
23255
23286
|
if ('sequence' !== track.type) {
|
|
23256
23287
|
this.$zoomInNotice = this.createZoomInNotice(this.$content);
|
|
23257
23288
|
}
|
|
23258
23289
|
|
|
23259
|
-
if (track.name && "sequence" !== track.
|
|
23260
|
-
|
|
23290
|
+
if (track.name && "sequence" !== track.id) {
|
|
23261
23291
|
this.$trackLabel = $$1('<div class="igv-track-label">');
|
|
23262
23292
|
this.$viewport.append(this.$trackLabel);
|
|
23263
23293
|
this.setTrackLabel(track.name);
|
|
23264
|
-
|
|
23265
23294
|
if (false === this.browser.trackLabelsVisible) {
|
|
23266
23295
|
this.$trackLabel.hide();
|
|
23267
23296
|
}
|
|
23268
|
-
|
|
23269
23297
|
}
|
|
23270
23298
|
|
|
23271
23299
|
this.stopSpinner();
|
|
23272
|
-
|
|
23273
23300
|
this.addMouseHandlers();
|
|
23301
|
+
}
|
|
23274
23302
|
|
|
23303
|
+
setContentHeight(contentHeight) {
|
|
23304
|
+
super.setContentHeight(contentHeight);
|
|
23305
|
+
if (this.featureCache) this.featureCache.redraw = true;
|
|
23275
23306
|
}
|
|
23276
23307
|
|
|
23277
23308
|
setTrackLabel(label) {
|
|
@@ -23293,61 +23324,74 @@ class TrackViewport extends Viewport {
|
|
|
23293
23324
|
}
|
|
23294
23325
|
}
|
|
23295
23326
|
|
|
23327
|
+
/**
|
|
23328
|
+
* Test to determine if we are zoomed in far enough to see features. Applicable to tracks with visibility windows.
|
|
23329
|
+
*
|
|
23330
|
+
* As a side effect the viewports canvas is removed if zoomed out.
|
|
23331
|
+
*
|
|
23332
|
+
* @returns {boolean} true if we are zoomed in past visibility window, false otherwise
|
|
23333
|
+
*/
|
|
23296
23334
|
checkZoomIn() {
|
|
23297
23335
|
|
|
23298
|
-
const
|
|
23299
|
-
|
|
23300
|
-
if (this.referenceFrame.chr.toLowerCase() === "all" && !this.trackView.track.supportsWholeGenome()) {
|
|
23336
|
+
const zoomedOutOfWindow = () => {
|
|
23337
|
+
if (this.referenceFrame.chr.toLowerCase() === "all" && !this.trackView.track.supportsWholeGenome) {
|
|
23301
23338
|
return true
|
|
23302
23339
|
} else {
|
|
23303
23340
|
const visibilityWindow = this.trackView.track.visibilityWindow;
|
|
23304
23341
|
return (
|
|
23305
23342
|
visibilityWindow !== undefined && visibilityWindow > 0 &&
|
|
23306
|
-
(referenceFrame.bpPerPixel * this.$viewport.width() > visibilityWindow))
|
|
23343
|
+
(this.referenceFrame.bpPerPixel * this.$viewport.width() > visibilityWindow))
|
|
23307
23344
|
}
|
|
23308
23345
|
};
|
|
23309
23346
|
|
|
23347
|
+
if (this.trackView.track && "sequence" === this.trackView.track.type && this.referenceFrame.bpPerPixel > 1) {
|
|
23348
|
+
$$1(this.canvas).remove();
|
|
23349
|
+
this.canvas = undefined;
|
|
23350
|
+
//this.featureCache = undefined
|
|
23351
|
+
return false
|
|
23352
|
+
}
|
|
23353
|
+
|
|
23310
23354
|
if (!(this.viewIsReady())) {
|
|
23311
23355
|
return false
|
|
23312
23356
|
}
|
|
23313
23357
|
|
|
23314
|
-
if (this.$zoomInNotice) {
|
|
23315
|
-
if (showZoomInNotice()) {
|
|
23316
|
-
// Out of visibility window
|
|
23317
|
-
if (this.canvas) {
|
|
23318
|
-
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
23319
|
-
this.tile = undefined;
|
|
23320
|
-
}
|
|
23321
|
-
this.$zoomInNotice.show();
|
|
23322
23358
|
|
|
23323
|
-
|
|
23324
|
-
const minHeight = this.trackView.minHeight || 0;
|
|
23325
|
-
this.setContentHeight(minHeight);
|
|
23326
|
-
}
|
|
23359
|
+
if (zoomedOutOfWindow()) {
|
|
23327
23360
|
|
|
23328
|
-
|
|
23329
|
-
|
|
23361
|
+
// Out of visibility window
|
|
23362
|
+
if (this.canvas) {
|
|
23363
|
+
$$1(this.canvas).remove();
|
|
23364
|
+
this.canvas = undefined;
|
|
23365
|
+
//this.featureCache = undefined
|
|
23366
|
+
}
|
|
23367
|
+
if (this.trackView.track.autoHeight) {
|
|
23368
|
+
const minHeight = this.trackView.minHeight || 0;
|
|
23369
|
+
this.setContentHeight(minHeight);
|
|
23370
|
+
}
|
|
23371
|
+
if (this.$zoomInNotice) {
|
|
23372
|
+
this.$zoomInNotice.show();
|
|
23373
|
+
}
|
|
23374
|
+
return false
|
|
23375
|
+
} else {
|
|
23376
|
+
if (this.$zoomInNotice) {
|
|
23330
23377
|
this.$zoomInNotice.hide();
|
|
23331
|
-
return true
|
|
23332
23378
|
}
|
|
23379
|
+
return true
|
|
23333
23380
|
}
|
|
23334
23381
|
|
|
23335
|
-
return true
|
|
23336
|
-
|
|
23337
|
-
|
|
23338
23382
|
}
|
|
23339
23383
|
|
|
23384
|
+
/**
|
|
23385
|
+
* Adjust the canvas to the current genomic state.
|
|
23386
|
+
*/
|
|
23340
23387
|
shift() {
|
|
23341
|
-
const
|
|
23342
|
-
|
|
23343
|
-
|
|
23344
|
-
|
|
23345
|
-
|
|
23346
|
-
|
|
23347
|
-
|
|
23348
|
-
|
|
23349
|
-
const pixelOffset = Math.round((self.tile.startBP - referenceFrame.start) / referenceFrame.bpPerPixel);
|
|
23350
|
-
self.canvas.style.left = pixelOffset + "px";
|
|
23388
|
+
const referenceFrame = this.referenceFrame;
|
|
23389
|
+
if (this.canvas &&
|
|
23390
|
+
this.canvas._data &&
|
|
23391
|
+
this.canvas._data.chr === this.referenceFrame.chr &&
|
|
23392
|
+
this.canvas._data.bpPerPixel === referenceFrame.bpPerPixel) {
|
|
23393
|
+
const pixelOffset = Math.round((this.canvas._data.startBP - referenceFrame.start) / referenceFrame.bpPerPixel);
|
|
23394
|
+
this.canvas.style.left = pixelOffset + "px";
|
|
23351
23395
|
}
|
|
23352
23396
|
}
|
|
23353
23397
|
|
|
@@ -23370,22 +23414,23 @@ class TrackViewport extends Viewport {
|
|
|
23370
23414
|
this.startSpinner();
|
|
23371
23415
|
|
|
23372
23416
|
try {
|
|
23373
|
-
const
|
|
23417
|
+
const track = this.trackView.track;
|
|
23418
|
+
const features = await this.getFeatures(track, chr, bpStart, bpEnd, referenceFrame.bpPerPixel);
|
|
23374
23419
|
let roiFeatures = [];
|
|
23375
|
-
const roi = mergeArrays(this.browser.roi,
|
|
23420
|
+
const roi = mergeArrays(this.browser.roi, track.roi);
|
|
23376
23421
|
if (roi) {
|
|
23377
23422
|
for (let r of roi) {
|
|
23378
|
-
const f = await
|
|
23379
|
-
r.getFeatures(chr, bpStart, bpEnd, referenceFrame.bpPerPixel);
|
|
23423
|
+
const f = await r.getFeatures(chr, bpStart, bpEnd, referenceFrame.bpPerPixel);
|
|
23380
23424
|
roiFeatures.push({track: r, features: f});
|
|
23381
23425
|
}
|
|
23382
23426
|
}
|
|
23383
23427
|
|
|
23384
|
-
|
|
23428
|
+
const mr = track && ("wig" === track.type || "merged" === track.type); // wig tracks are potentially multiresolution (e.g. bigwig)
|
|
23429
|
+
this.featureCache = new FeatureCache(chr, bpStart, bpEnd, referenceFrame.bpPerPixel, features, roiFeatures, mr);
|
|
23385
23430
|
this.loading = false;
|
|
23386
23431
|
this.hideMessage();
|
|
23387
23432
|
this.stopSpinner();
|
|
23388
|
-
return this.
|
|
23433
|
+
return this.featureCache
|
|
23389
23434
|
} catch (error) {
|
|
23390
23435
|
// Track might have been removed during load
|
|
23391
23436
|
if (this.trackView && this.trackView.disposed !== true) {
|
|
@@ -23399,32 +23444,29 @@ class TrackViewport extends Viewport {
|
|
|
23399
23444
|
}
|
|
23400
23445
|
}
|
|
23401
23446
|
|
|
23402
|
-
|
|
23447
|
+
/**
|
|
23448
|
+
* Repaint the canvas using the cached features
|
|
23449
|
+
*
|
|
23450
|
+
*/
|
|
23451
|
+
repaint() {
|
|
23403
23452
|
|
|
23404
|
-
if (undefined === this.
|
|
23453
|
+
if (undefined === this.featureCache) {
|
|
23405
23454
|
return
|
|
23406
23455
|
}
|
|
23407
23456
|
|
|
23408
|
-
let {features, roiFeatures
|
|
23457
|
+
let {features, roiFeatures} = this.featureCache;
|
|
23458
|
+
//this.tile.bpPerPixel = this.referenceFrame.bpPerPixel
|
|
23409
23459
|
|
|
23410
23460
|
// const isWGV = GenomeUtils.isWholeGenomeView(this.browser.referenceFrameList[0].chr)
|
|
23411
23461
|
const isWGV = GenomeUtils.isWholeGenomeView(this.referenceFrame.chr);
|
|
23412
|
-
let pixelWidth;
|
|
23413
|
-
|
|
23414
|
-
if (isWGV) {
|
|
23415
|
-
bpPerPixel = this.referenceFrame.end / this.$viewport.width();
|
|
23416
|
-
startBP = 0;
|
|
23417
|
-
endBP = this.referenceFrame.end;
|
|
23418
|
-
pixelWidth = this.$viewport.width();
|
|
23419
|
-
} else {
|
|
23420
|
-
pixelWidth = Math.ceil((endBP - startBP) / bpPerPixel);
|
|
23421
|
-
}
|
|
23422
23462
|
|
|
23463
|
+
// Canvas dimensions. There is no left-right panning for WGV so canvas width is viewport width.
|
|
23423
23464
|
// For deep tracks we paint a canvas == 3*viewportHeight centered on the current vertical scroll position
|
|
23465
|
+
const pixelWidth = isWGV ? this.$viewport.width() : 3 * this.$viewport.width();
|
|
23424
23466
|
const viewportHeight = this.$viewport.height();
|
|
23425
23467
|
const contentHeight = this.getContentHeight();
|
|
23426
23468
|
const minHeight = roiFeatures ? Math.max(contentHeight, viewportHeight) : contentHeight; // Need to fill viewport for ROIs.
|
|
23427
|
-
|
|
23469
|
+
const pixelHeight = Math.min(minHeight, 3 * viewportHeight);
|
|
23428
23470
|
if (0 === pixelWidth || 0 === pixelHeight) {
|
|
23429
23471
|
if (this.canvas) {
|
|
23430
23472
|
$$1(this.canvas).remove();
|
|
@@ -23433,30 +23475,25 @@ class TrackViewport extends Viewport {
|
|
|
23433
23475
|
}
|
|
23434
23476
|
const canvasTop = Math.max(0, -(this.$content.position().top) - viewportHeight);
|
|
23435
23477
|
|
|
23436
|
-
|
|
23437
|
-
|
|
23438
|
-
|
|
23439
|
-
|
|
23440
|
-
} else {
|
|
23441
|
-
devicePixelRatio = (this.trackView.track.supportHiDPI === false) ? 1 : window.devicePixelRatio;
|
|
23442
|
-
}
|
|
23443
|
-
|
|
23444
|
-
const pixelXOffset = Math.round((startBP - this.referenceFrame.start) / this.referenceFrame.bpPerPixel);
|
|
23478
|
+
const bpPerPixel = this.referenceFrame.bpPerPixel;
|
|
23479
|
+
const startBP = this.referenceFrame.start - (isWGV ? 0 : pixelWidth / 3 * bpPerPixel);
|
|
23480
|
+
const endBP = this.referenceFrame.end + (isWGV ? 0 : pixelWidth / 3 * bpPerPixel);
|
|
23481
|
+
const pixelXOffset = Math.round((startBP - this.referenceFrame.start) / bpPerPixel);
|
|
23445
23482
|
|
|
23446
23483
|
const newCanvas = $$1('<canvas class="igv-canvas">').get(0);
|
|
23447
|
-
const ctx = newCanvas.getContext("2d");
|
|
23448
|
-
|
|
23449
23484
|
newCanvas.style.width = pixelWidth + "px";
|
|
23450
23485
|
newCanvas.style.height = pixelHeight + "px";
|
|
23486
|
+
newCanvas.style.left = pixelXOffset + "px";
|
|
23487
|
+
newCanvas.style.top = canvasTop + "px";
|
|
23451
23488
|
|
|
23489
|
+
// Always use high DPI if in "FILL" display mode, otherwise use track setting;
|
|
23490
|
+
const devicePixelRatio = ("FILL" === this.trackView.track.displayMode || this.trackView.track.supportHiDPI !== false) ?
|
|
23491
|
+
window.devicePixelRatio : 1;
|
|
23452
23492
|
newCanvas.width = devicePixelRatio * pixelWidth;
|
|
23453
23493
|
newCanvas.height = devicePixelRatio * pixelHeight;
|
|
23454
23494
|
|
|
23495
|
+
const ctx = newCanvas.getContext("2d");
|
|
23455
23496
|
ctx.scale(devicePixelRatio, devicePixelRatio);
|
|
23456
|
-
|
|
23457
|
-
newCanvas.style.left = pixelXOffset + "px";
|
|
23458
|
-
newCanvas.style.top = canvasTop + "px";
|
|
23459
|
-
|
|
23460
23497
|
ctx.translate(0, -canvasTop);
|
|
23461
23498
|
|
|
23462
23499
|
const drawConfiguration =
|
|
@@ -23477,17 +23514,27 @@ class TrackViewport extends Viewport {
|
|
|
23477
23514
|
|
|
23478
23515
|
this.draw(drawConfiguration, features, roiFeatures);
|
|
23479
23516
|
|
|
23480
|
-
this.
|
|
23517
|
+
this.featureCache.canvasTop = canvasTop;
|
|
23518
|
+
this.featureCache.height = pixelHeight;
|
|
23481
23519
|
|
|
23482
|
-
if (this
|
|
23483
|
-
this
|
|
23520
|
+
if (this.canvas) {
|
|
23521
|
+
$$1(this.canvas).remove();
|
|
23484
23522
|
}
|
|
23485
|
-
|
|
23486
|
-
|
|
23523
|
+
newCanvas._data = {
|
|
23524
|
+
chr: this.featureCache.chr, bpPerPixel, startBP, endBP, pixelHeight, pixelTop: canvasTop
|
|
23525
|
+
};
|
|
23487
23526
|
this.canvas = newCanvas;
|
|
23488
|
-
this.
|
|
23527
|
+
this.$content.append($$1(newCanvas));
|
|
23528
|
+
|
|
23489
23529
|
}
|
|
23490
23530
|
|
|
23531
|
+
/**
|
|
23532
|
+
* Draw the associated track.
|
|
23533
|
+
*
|
|
23534
|
+
* @param drawConfiguration
|
|
23535
|
+
* @param features
|
|
23536
|
+
* @param roiFeatures
|
|
23537
|
+
*/
|
|
23491
23538
|
draw(drawConfiguration, features, roiFeatures) {
|
|
23492
23539
|
|
|
23493
23540
|
// console.log(`${ Date.now() } viewport draw(). track ${ this.trackView.track.type }. content-css-top ${ this.$content.css('top') }. canvas-top ${ drawConfiguration.pixelTop }.`)
|
|
@@ -23504,60 +23551,6 @@ class TrackViewport extends Viewport {
|
|
|
23504
23551
|
}
|
|
23505
23552
|
}
|
|
23506
23553
|
|
|
23507
|
-
// TODO: Nolonger used. Will discard
|
|
23508
|
-
async toSVG(tile) {
|
|
23509
|
-
|
|
23510
|
-
// Nothing to do if zoomInNotice is active
|
|
23511
|
-
if (this.$zoomInNotice && this.$zoomInNotice.is(":visible")) {
|
|
23512
|
-
return
|
|
23513
|
-
}
|
|
23514
|
-
|
|
23515
|
-
const referenceFrame = this.referenceFrame;
|
|
23516
|
-
const bpPerPixel = tile.bpPerPixel;
|
|
23517
|
-
const features = tile.features;
|
|
23518
|
-
const roiFeatures = tile.roiFeatures;
|
|
23519
|
-
const pixelWidth = this.$viewport.width();
|
|
23520
|
-
const pixelHeight = this.$viewport.height();
|
|
23521
|
-
const bpStart = referenceFrame.start;
|
|
23522
|
-
const bpEnd = referenceFrame.start + pixelWidth * referenceFrame.bpPerPixel;
|
|
23523
|
-
|
|
23524
|
-
const ctx$1 = new ctx(
|
|
23525
|
-
{
|
|
23526
|
-
// svg
|
|
23527
|
-
width: pixelWidth,
|
|
23528
|
-
height: pixelHeight,
|
|
23529
|
-
viewbox:
|
|
23530
|
-
{
|
|
23531
|
-
x: 0,
|
|
23532
|
-
y: -this.$content.position().top,
|
|
23533
|
-
width: pixelWidth,
|
|
23534
|
-
height: pixelHeight
|
|
23535
|
-
}
|
|
23536
|
-
|
|
23537
|
-
});
|
|
23538
|
-
|
|
23539
|
-
const drawConfiguration =
|
|
23540
|
-
{
|
|
23541
|
-
viewport: this,
|
|
23542
|
-
context: ctx$1,
|
|
23543
|
-
top: -this.$content.position().top,
|
|
23544
|
-
pixelTop: 0, // for compatibility with canvas draw
|
|
23545
|
-
pixelWidth,
|
|
23546
|
-
pixelHeight,
|
|
23547
|
-
bpStart,
|
|
23548
|
-
bpEnd,
|
|
23549
|
-
bpPerPixel,
|
|
23550
|
-
referenceFrame: this.referenceFrame,
|
|
23551
|
-
selection: this.selection,
|
|
23552
|
-
viewportWidth: pixelWidth,
|
|
23553
|
-
};
|
|
23554
|
-
|
|
23555
|
-
this.draw(drawConfiguration, features, roiFeatures);
|
|
23556
|
-
|
|
23557
|
-
return ctx$1.getSerializedSvg(true)
|
|
23558
|
-
|
|
23559
|
-
}
|
|
23560
|
-
|
|
23561
23554
|
containsPosition(chr, position) {
|
|
23562
23555
|
if (this.referenceFrame.chr === chr && position >= this.referenceFrame.start) {
|
|
23563
23556
|
return position <= this.referenceFrame.calculateEnd(this.getWidth())
|
|
@@ -23570,18 +23563,20 @@ class TrackViewport extends Viewport {
|
|
|
23570
23563
|
return this.loading
|
|
23571
23564
|
}
|
|
23572
23565
|
|
|
23573
|
-
|
|
23566
|
+
savePNG() {
|
|
23574
23567
|
|
|
23575
|
-
if (!this.
|
|
23568
|
+
if (!this.canvas) return
|
|
23576
23569
|
|
|
23577
|
-
const
|
|
23570
|
+
const canvasMetadata = this.featureCache;
|
|
23571
|
+
const canvasTop = canvasMetadata ? canvasMetadata.canvasTop : 0;
|
|
23578
23572
|
const devicePixelRatio = window.devicePixelRatio;
|
|
23579
23573
|
const w = this.$viewport.width() * devicePixelRatio;
|
|
23580
23574
|
const h = this.$viewport.height() * devicePixelRatio;
|
|
23581
23575
|
const x = -$$1(this.canvas).position().left * devicePixelRatio;
|
|
23582
23576
|
const y = (-this.$content.position().top - canvasTop) * devicePixelRatio;
|
|
23583
23577
|
|
|
23584
|
-
const
|
|
23578
|
+
const ctx = this.canvas.getContext("2d");
|
|
23579
|
+
const imageData = ctx.getImageData(x, y, w, h);
|
|
23585
23580
|
const exportCanvas = document.createElement('canvas');
|
|
23586
23581
|
const exportCtx = exportCanvas.getContext('2d');
|
|
23587
23582
|
exportCanvas.width = imageData.width;
|
|
@@ -23704,32 +23699,53 @@ class TrackViewport extends Viewport {
|
|
|
23704
23699
|
selection: this.selection
|
|
23705
23700
|
};
|
|
23706
23701
|
|
|
23707
|
-
const features = this.
|
|
23708
|
-
const roiFeatures = this.
|
|
23702
|
+
const features = this.featureCache ? this.featureCache.features : [];
|
|
23703
|
+
const roiFeatures = this.featureCache ? this.featureCache.roiFeatures : undefined;
|
|
23709
23704
|
this.draw(config, features, roiFeatures);
|
|
23710
23705
|
|
|
23711
23706
|
context.restore();
|
|
23712
23707
|
|
|
23713
23708
|
}
|
|
23714
23709
|
|
|
23715
|
-
|
|
23716
|
-
return this.
|
|
23710
|
+
get cachedFeatures() {
|
|
23711
|
+
return this.featureCache ? this.featureCache.features : []
|
|
23717
23712
|
}
|
|
23718
23713
|
|
|
23719
23714
|
async getFeatures(track, chr, start, end, bpPerPixel) {
|
|
23720
23715
|
|
|
23721
|
-
if (this.
|
|
23722
|
-
return this.
|
|
23716
|
+
if (this.featureCache && this.featureCache.containsRange(chr, start, end, bpPerPixel)) {
|
|
23717
|
+
return this.featureCache.features
|
|
23723
23718
|
} else if (typeof track.getFeatures === "function") {
|
|
23724
23719
|
const features = await track.getFeatures(chr, start, end, bpPerPixel, this);
|
|
23725
|
-
this.
|
|
23726
|
-
this.checkContentHeight();
|
|
23720
|
+
this.checkContentHeight(features);
|
|
23727
23721
|
return features
|
|
23728
23722
|
} else {
|
|
23729
23723
|
return undefined
|
|
23730
23724
|
}
|
|
23731
23725
|
}
|
|
23732
23726
|
|
|
23727
|
+
needsRepaint() {
|
|
23728
|
+
|
|
23729
|
+
if (!this.canvas) return true
|
|
23730
|
+
|
|
23731
|
+
const data = this.canvas._data;
|
|
23732
|
+
return !data ||
|
|
23733
|
+
this.referenceFrame.start < data.startBP ||
|
|
23734
|
+
this.referenceFrame.end > data.endBP ||
|
|
23735
|
+
this.referenceFrame.chr !== data.chr ||
|
|
23736
|
+
this.referenceFrame.bpPerPixel != data.bpPerPixel
|
|
23737
|
+
}
|
|
23738
|
+
|
|
23739
|
+
needsReload() {
|
|
23740
|
+
if (!this.featureCache) return true
|
|
23741
|
+
const referenceFrame = this.referenceFrame;
|
|
23742
|
+
const chr = this.referenceFrame.chr;
|
|
23743
|
+
const start = referenceFrame.start;
|
|
23744
|
+
const end = start + referenceFrame.toBP($$1(this.contentDiv).width());
|
|
23745
|
+
const bpPerPixel = referenceFrame.bpPerPixel;
|
|
23746
|
+
return (!this.featureCache.containsRange(chr, start, end, bpPerPixel))
|
|
23747
|
+
}
|
|
23748
|
+
|
|
23733
23749
|
createZoomInNotice($parent) {
|
|
23734
23750
|
|
|
23735
23751
|
const $container = $$1('<div>', {class: 'igv-zoom-in-notice-container'});
|
|
@@ -23751,15 +23767,33 @@ class TrackViewport extends Viewport {
|
|
|
23751
23767
|
|
|
23752
23768
|
addMouseHandlers() {
|
|
23753
23769
|
|
|
23754
|
-
this
|
|
23755
|
-
|
|
23756
|
-
this.addViewportMouseDownHandler(this.$viewport.get(0));
|
|
23770
|
+
const viewport = this.$viewport.get(0);
|
|
23757
23771
|
|
|
23758
|
-
this.
|
|
23772
|
+
this.addViewportContextMenuHandler(viewport);
|
|
23759
23773
|
|
|
23760
|
-
|
|
23774
|
+
const md = (event) => {
|
|
23775
|
+
this.enableClick = true;
|
|
23776
|
+
this.browser.mouseDownOnViewport(event, this);
|
|
23777
|
+
pageCoordinates$1(event);
|
|
23778
|
+
};
|
|
23779
|
+
viewport.addEventListener('mousedown', md);
|
|
23780
|
+
viewport.addEventListener('touchstart', md);
|
|
23781
|
+
|
|
23782
|
+
const mu = (event) => {
|
|
23783
|
+
// Any mouse up cancels drag and scrolling
|
|
23784
|
+
if (this.browser.dragObject || this.browser.isScrolling) {
|
|
23785
|
+
this.browser.cancelTrackPan();
|
|
23786
|
+
// event.preventDefault();
|
|
23787
|
+
// event.stopPropagation();
|
|
23788
|
+
this.enableClick = false; // Until next mouse down
|
|
23789
|
+
} else {
|
|
23790
|
+
this.browser.cancelTrackPan();
|
|
23791
|
+
this.browser.endTrackDrag();
|
|
23792
|
+
}
|
|
23793
|
+
};
|
|
23761
23794
|
|
|
23762
|
-
|
|
23795
|
+
viewport.addEventListener('mouseup', mu);
|
|
23796
|
+
viewport.addEventListener('touchend', mu);
|
|
23763
23797
|
|
|
23764
23798
|
this.addViewportClickHandler(this.$viewport.get(0));
|
|
23765
23799
|
|
|
@@ -23769,32 +23803,9 @@ class TrackViewport extends Viewport {
|
|
|
23769
23803
|
|
|
23770
23804
|
}
|
|
23771
23805
|
|
|
23772
|
-
removeMouseHandlers() {
|
|
23773
|
-
|
|
23774
|
-
this.removeViewportContextMenuHandler(this.$viewport.get(0));
|
|
23775
|
-
|
|
23776
|
-
this.removeViewportMouseDownHandler(this.$viewport.get(0));
|
|
23777
|
-
|
|
23778
|
-
this.removeViewportTouchStartHandler(this.$viewport.get(0));
|
|
23779
|
-
|
|
23780
|
-
this.removeViewportMouseUpHandler(this.$viewport.get(0));
|
|
23781
|
-
|
|
23782
|
-
this.removeViewportTouchEndHandler(this.$viewport.get(0));
|
|
23783
|
-
|
|
23784
|
-
this.removeViewportClickHandler(this.$viewport.get(0));
|
|
23785
|
-
|
|
23786
|
-
if (this.trackView.track.name && "sequence" !== this.trackView.track.config.type) {
|
|
23787
|
-
this.removeTrackLabelClickHandler(this.$trackLabel.get(0));
|
|
23788
|
-
}
|
|
23789
|
-
|
|
23790
|
-
}
|
|
23791
|
-
|
|
23792
23806
|
addViewportContextMenuHandler(viewport) {
|
|
23793
23807
|
|
|
23794
|
-
|
|
23795
|
-
viewport.addEventListener('contextmenu', this.boundContextMenuHandler);
|
|
23796
|
-
|
|
23797
|
-
function contextMenuHandler(event) {
|
|
23808
|
+
viewport.addEventListener('contextmenu', (event) => {
|
|
23798
23809
|
|
|
23799
23810
|
// Ignore if we are doing a drag. This can happen with touch events.
|
|
23800
23811
|
if (this.browser.dragObject) {
|
|
@@ -23827,56 +23838,14 @@ class TrackViewport extends Viewport {
|
|
|
23827
23838
|
menuItems.push({label: 'Save Image (SVG)', click: () => this.saveSVG()});
|
|
23828
23839
|
|
|
23829
23840
|
this.browser.menuPopup.presentTrackContextMenu(event, menuItems);
|
|
23830
|
-
}
|
|
23831
|
-
|
|
23832
|
-
}
|
|
23833
|
-
|
|
23834
|
-
removeViewportContextMenuHandler(viewport) {
|
|
23835
|
-
viewport.removeEventListener('contextmenu', this.boundContextMenuHandler);
|
|
23836
|
-
}
|
|
23837
|
-
|
|
23838
|
-
addViewportMouseDownHandler(viewport) {
|
|
23839
|
-
this.boundMouseDownHandler = mouseDownHandler.bind(this);
|
|
23840
|
-
viewport.addEventListener('mousedown', this.boundMouseDownHandler);
|
|
23841
|
-
}
|
|
23842
|
-
|
|
23843
|
-
removeViewportMouseDownHandler(viewport) {
|
|
23844
|
-
viewport.removeEventListener('mousedown', this.boundMouseDownHandler);
|
|
23845
|
-
}
|
|
23846
|
-
|
|
23847
|
-
addViewportTouchStartHandler(viewport) {
|
|
23848
|
-
this.boundTouchStartHandler = mouseDownHandler.bind(this);
|
|
23849
|
-
viewport.addEventListener('touchstart', this.boundTouchStartHandler);
|
|
23850
|
-
}
|
|
23851
|
-
|
|
23852
|
-
removeViewportTouchStartHandler(viewport) {
|
|
23853
|
-
viewport.removeEventListener('touchstart', this.boundTouchStartHandler);
|
|
23854
|
-
}
|
|
23855
|
-
|
|
23856
|
-
addViewportMouseUpHandler(viewport) {
|
|
23857
|
-
this.boundMouseUpHandler = mouseUpHandler.bind(this);
|
|
23858
|
-
viewport.addEventListener('mouseup', this.boundMouseUpHandler);
|
|
23859
|
-
}
|
|
23860
|
-
|
|
23861
|
-
removeViewportMouseUpHandler(viewport) {
|
|
23862
|
-
viewport.removeEventListener('mouseup', this.boundMouseUpHandler);
|
|
23863
|
-
}
|
|
23841
|
+
});
|
|
23864
23842
|
|
|
23865
|
-
addViewportTouchEndHandler(viewport) {
|
|
23866
|
-
this.boundTouchEndHandler = mouseUpHandler.bind(this);
|
|
23867
|
-
viewport.addEventListener('touchend', this.boundTouchEndHandler);
|
|
23868
23843
|
}
|
|
23869
23844
|
|
|
23870
|
-
removeViewportTouchEndHandler(viewport) {
|
|
23871
|
-
viewport.removeEventListener('touchend', this.boundTouchEndHandler);
|
|
23872
|
-
}
|
|
23873
23845
|
|
|
23874
23846
|
addViewportClickHandler(viewport) {
|
|
23875
23847
|
|
|
23876
|
-
|
|
23877
|
-
viewport.addEventListener('click', this.boundClickHandler);
|
|
23878
|
-
|
|
23879
|
-
function clickHandler(event) {
|
|
23848
|
+
viewport.addEventListener('click', (event) => {
|
|
23880
23849
|
|
|
23881
23850
|
if (this.enableClick) {
|
|
23882
23851
|
if (3 === event.which || event.ctrlKey) {
|
|
@@ -23961,21 +23930,12 @@ class TrackViewport extends Viewport {
|
|
|
23961
23930
|
lastClickTime = time;
|
|
23962
23931
|
|
|
23963
23932
|
}
|
|
23964
|
-
|
|
23965
|
-
}
|
|
23966
|
-
|
|
23967
|
-
}
|
|
23968
|
-
|
|
23969
|
-
removeViewportClickHandler(viewport) {
|
|
23970
|
-
viewport.removeEventListener('click', this.boundClickHandler);
|
|
23933
|
+
});
|
|
23971
23934
|
}
|
|
23972
23935
|
|
|
23973
23936
|
addTrackLabelClickHandler(trackLabel) {
|
|
23974
23937
|
|
|
23975
|
-
|
|
23976
|
-
trackLabel.addEventListener('click', this.boundTrackLabelClickHandler);
|
|
23977
|
-
|
|
23978
|
-
function clickHandler(event) {
|
|
23938
|
+
trackLabel.addEventListener('click', (event) => {
|
|
23979
23939
|
|
|
23980
23940
|
event.stopPropagation();
|
|
23981
23941
|
|
|
@@ -23995,48 +23955,18 @@ class TrackViewport extends Viewport {
|
|
|
23995
23955
|
this.popover = new Popover(this.browser.columnContainer, (track.name || ''));
|
|
23996
23956
|
this.popover.presentContentWithEvent(event, str);
|
|
23997
23957
|
}
|
|
23998
|
-
}
|
|
23999
|
-
}
|
|
24000
|
-
|
|
24001
|
-
removeTrackLabelClickHandler(trackLabel) {
|
|
24002
|
-
trackLabel.removeEventListener('click', this.boundTrackLabelClickHandler);
|
|
23958
|
+
});
|
|
24003
23959
|
}
|
|
24004
23960
|
|
|
24005
23961
|
}
|
|
24006
23962
|
|
|
24007
|
-
function mouseDownHandler(event) {
|
|
24008
|
-
this.enableClick = true;
|
|
24009
|
-
this.browser.mouseDownOnViewport(event, this);
|
|
24010
|
-
pageCoordinates$1(event);
|
|
24011
|
-
}
|
|
24012
|
-
|
|
24013
|
-
function mouseUpHandler(event) {
|
|
24014
|
-
|
|
24015
|
-
// Any mouse up cancels drag and scrolling
|
|
24016
|
-
if (this.browser.dragObject || this.browser.isScrolling) {
|
|
24017
|
-
this.browser.cancelTrackPan();
|
|
24018
|
-
// event.preventDefault();
|
|
24019
|
-
// event.stopPropagation();
|
|
24020
|
-
this.enableClick = false; // Until next mouse down
|
|
24021
|
-
} else {
|
|
24022
|
-
this.browser.cancelTrackPan();
|
|
24023
|
-
this.browser.endTrackDrag();
|
|
24024
|
-
}
|
|
24025
|
-
}
|
|
24026
|
-
|
|
24027
23963
|
function createClickState(event, viewport) {
|
|
24028
23964
|
|
|
24029
23965
|
const referenceFrame = viewport.referenceFrame;
|
|
24030
|
-
|
|
24031
23966
|
const viewportCoords = translateMouseCoordinates$1(event, viewport.contentDiv);
|
|
24032
23967
|
const canvasCoords = translateMouseCoordinates$1(event, viewport.canvas);
|
|
24033
|
-
|
|
24034
23968
|
const genomicLocation = ((referenceFrame.start) + referenceFrame.toBP(viewportCoords.x));
|
|
24035
23969
|
|
|
24036
|
-
if (undefined === genomicLocation || null === viewport.tile) {
|
|
24037
|
-
return undefined
|
|
24038
|
-
}
|
|
24039
|
-
|
|
24040
23970
|
return {
|
|
24041
23971
|
event,
|
|
24042
23972
|
viewport,
|
|
@@ -24097,22 +24027,30 @@ function formatPopoverText(nameValues) {
|
|
|
24097
24027
|
return rows.join('')
|
|
24098
24028
|
}
|
|
24099
24029
|
|
|
24100
|
-
|
|
24101
|
-
this.chr = chr;
|
|
24102
|
-
this.startBP = tileStart;
|
|
24103
|
-
this.endBP = tileEnd;
|
|
24104
|
-
this.bpPerPixel = bpPerPixel;
|
|
24105
|
-
this.features = features;
|
|
24106
|
-
this.roiFeatures = roiFeatures;
|
|
24107
|
-
};
|
|
24030
|
+
class FeatureCache {
|
|
24108
24031
|
|
|
24109
|
-
|
|
24110
|
-
|
|
24111
|
-
|
|
24032
|
+
constructor(chr, tileStart, tileEnd, bpPerPixel, features, roiFeatures, multiresolution) {
|
|
24033
|
+
this.chr = chr;
|
|
24034
|
+
this.startBP = tileStart;
|
|
24035
|
+
this.endBP = tileEnd;
|
|
24036
|
+
this.bpPerPixel = bpPerPixel;
|
|
24037
|
+
this.features = features;
|
|
24038
|
+
this.roiFeatures = roiFeatures;
|
|
24039
|
+
this.multiresolution = multiresolution;
|
|
24040
|
+
}
|
|
24112
24041
|
|
|
24113
|
-
|
|
24114
|
-
|
|
24115
|
-
|
|
24042
|
+
containsRange(chr, start, end, bpPerPixel) {
|
|
24043
|
+
|
|
24044
|
+
// For multi-resolution tracks allow for a 2X change in bpPerPixel
|
|
24045
|
+
const r = this.multiresolution ? this.bpPerPixel / bpPerPixel : 1;
|
|
24046
|
+
|
|
24047
|
+
return start >= this.startBP && end <= this.endBP && chr === this.chr && r > 0.5 && r < 2
|
|
24048
|
+
}
|
|
24049
|
+
|
|
24050
|
+
overlapsRange(chr, start, end) {
|
|
24051
|
+
return this.chr === chr && end >= this.startBP && start <= this.endBP
|
|
24052
|
+
}
|
|
24053
|
+
}
|
|
24116
24054
|
|
|
24117
24055
|
|
|
24118
24056
|
/**
|
|
@@ -24277,11 +24215,13 @@ class RulerSweeper {
|
|
|
24277
24215
|
|
|
24278
24216
|
validateLocusExtent(this.rulerViewport.browser.genome.getChromosome(this.rulerViewport.referenceFrame.chr).bpLength, extent, this.rulerViewport.browser.minimumBases());
|
|
24279
24217
|
|
|
24280
|
-
|
|
24281
|
-
|
|
24282
|
-
this.rulerViewport.referenceFrame.
|
|
24218
|
+
const newStart = Math.round(extent.start);
|
|
24219
|
+
const newEnd = Math.round(extent.end);
|
|
24220
|
+
this.rulerViewport.referenceFrame.bpPerPixel = (newEnd - newStart) / this.rulerViewport.contentDiv.clientWidth;
|
|
24221
|
+
this.rulerViewport.referenceFrame.start = newStart;
|
|
24222
|
+
this.rulerViewport.referenceFrame.end = newEnd;
|
|
24283
24223
|
|
|
24284
|
-
this.rulerViewport.browser.updateViews(
|
|
24224
|
+
this.rulerViewport.browser.updateViews();
|
|
24285
24225
|
}
|
|
24286
24226
|
|
|
24287
24227
|
}
|
|
@@ -24419,6 +24359,18 @@ class PairedAlignment {
|
|
|
24419
24359
|
return true // By definition
|
|
24420
24360
|
}
|
|
24421
24361
|
|
|
24362
|
+
isMateMapped() {
|
|
24363
|
+
return true // By definition
|
|
24364
|
+
}
|
|
24365
|
+
|
|
24366
|
+
isProperPair() {
|
|
24367
|
+
return this.firstAlignment.isProperPair()
|
|
24368
|
+
}
|
|
24369
|
+
|
|
24370
|
+
get fragmentLength() {
|
|
24371
|
+
return Math.abs(this.firstAlignment.fragmentLength)
|
|
24372
|
+
}
|
|
24373
|
+
|
|
24422
24374
|
firstOfPairStrand() {
|
|
24423
24375
|
|
|
24424
24376
|
if (this.firstAlignment.isFirstOfPair()) {
|
|
@@ -24760,7 +24712,10 @@ function packAlignmentRows(alignments, start, end, showSoftClips) {
|
|
|
24760
24712
|
|
|
24761
24713
|
|
|
24762
24714
|
class AlignmentContainer {
|
|
24763
|
-
|
|
24715
|
+
|
|
24716
|
+
// this.config.samplingWindowSize, this.config.samplingDepth,
|
|
24717
|
+
// this.config.pairsSupported, this.config.alleleFreqThreshold)
|
|
24718
|
+
constructor(chr, start, end, {samplingWindowSize, samplingDepth, pairsSupported, alleleFreqThreshold}) {
|
|
24764
24719
|
|
|
24765
24720
|
this.chr = chr;
|
|
24766
24721
|
this.start = Math.floor(start);
|
|
@@ -24788,17 +24743,12 @@ class AlignmentContainer {
|
|
|
24788
24743
|
return alignment.isMapped() && !alignment.isFailsVendorQualityCheck()
|
|
24789
24744
|
};
|
|
24790
24745
|
|
|
24791
|
-
this.pairedEndStats = new PairedEndStats();
|
|
24792
24746
|
}
|
|
24793
24747
|
|
|
24794
24748
|
push(alignment) {
|
|
24795
24749
|
|
|
24796
24750
|
if (this.filter(alignment) === false) return
|
|
24797
24751
|
|
|
24798
|
-
if (alignment.isPaired()) {
|
|
24799
|
-
this.pairedEndStats.push(alignment);
|
|
24800
|
-
}
|
|
24801
|
-
|
|
24802
24752
|
this.coverageMap.incCounts(alignment); // Count coverage before any downsampling
|
|
24803
24753
|
|
|
24804
24754
|
if (this.pairsSupported && this.downsampledReads.has(alignment.readName)) {
|
|
@@ -24830,8 +24780,6 @@ class AlignmentContainer {
|
|
|
24830
24780
|
|
|
24831
24781
|
this.pairsCache = undefined;
|
|
24832
24782
|
this.downsampledReads = undefined;
|
|
24833
|
-
|
|
24834
|
-
this.pairedEndStats.compute();
|
|
24835
24783
|
}
|
|
24836
24784
|
|
|
24837
24785
|
contains(chr, start, end) {
|
|
@@ -25142,69 +25090,6 @@ class DownsampledInterval {
|
|
|
25142
25090
|
}
|
|
25143
25091
|
}
|
|
25144
25092
|
|
|
25145
|
-
class PairedEndStats {
|
|
25146
|
-
|
|
25147
|
-
constructor(lowerPercentile, upperPercentile) {
|
|
25148
|
-
this.totalCount = 0;
|
|
25149
|
-
this.frCount = 0;
|
|
25150
|
-
this.rfCount = 0;
|
|
25151
|
-
this.ffCount = 0;
|
|
25152
|
-
this.sumF = 0;
|
|
25153
|
-
this.sumF2 = 0;
|
|
25154
|
-
//this.lp = lowerPercentile === undefined ? 0.005 : lowerPercentile;
|
|
25155
|
-
//this.up = upperPercentile === undefined ? 0.995 : upperPercentile;
|
|
25156
|
-
//this.digest = new Digest();
|
|
25157
|
-
}
|
|
25158
|
-
|
|
25159
|
-
push(alignment) {
|
|
25160
|
-
|
|
25161
|
-
if (alignment.isProperPair()) {
|
|
25162
|
-
|
|
25163
|
-
var fragmentLength = Math.abs(alignment.fragmentLength);
|
|
25164
|
-
//this.digest.push(fragmentLength);
|
|
25165
|
-
this.sumF += fragmentLength;
|
|
25166
|
-
this.sumF2 += fragmentLength * fragmentLength;
|
|
25167
|
-
|
|
25168
|
-
var po = alignment.pairOrientation;
|
|
25169
|
-
|
|
25170
|
-
if (typeof po === "string" && po.length === 4) {
|
|
25171
|
-
var tmp = '' + po.charAt(0) + po.charAt(2);
|
|
25172
|
-
switch (tmp) {
|
|
25173
|
-
case 'FF':
|
|
25174
|
-
case 'RR':
|
|
25175
|
-
this.ffCount++;
|
|
25176
|
-
break
|
|
25177
|
-
case "FR":
|
|
25178
|
-
this.frCount++;
|
|
25179
|
-
break
|
|
25180
|
-
case"RF":
|
|
25181
|
-
this.rfCount++;
|
|
25182
|
-
}
|
|
25183
|
-
}
|
|
25184
|
-
this.totalCount++;
|
|
25185
|
-
}
|
|
25186
|
-
}
|
|
25187
|
-
|
|
25188
|
-
compute() {
|
|
25189
|
-
|
|
25190
|
-
if (this.totalCount > 100) {
|
|
25191
|
-
if (this.ffCount / this.totalCount > 0.9) this.orienation = "ff";
|
|
25192
|
-
else if (this.frCount / this.totalCount > 0.9) this.orienation = "fr";
|
|
25193
|
-
else if (this.rfCount / this.totalCount > 0.9) this.orienation = "rf";
|
|
25194
|
-
|
|
25195
|
-
|
|
25196
|
-
var fMean = this.sumF / this.totalCount;
|
|
25197
|
-
var stdDev = Math.sqrt((this.totalCount * this.sumF2 - this.sumF * this.sumF) / (this.totalCount * this.totalCount));
|
|
25198
|
-
this.lowerFragmentLength = fMean - 3 * stdDev;
|
|
25199
|
-
this.upperFragmentLength = fMean + 3 * stdDev;
|
|
25200
|
-
|
|
25201
|
-
//this.lowerFragmentLength = this.digest.percentile(this.lp);
|
|
25202
|
-
//this.upperFragmentLength = this.digest.percentile(this.up);
|
|
25203
|
-
//this.digest = undefined;
|
|
25204
|
-
}
|
|
25205
|
-
}
|
|
25206
|
-
}
|
|
25207
|
-
|
|
25208
25093
|
class SupplementaryAlignment {
|
|
25209
25094
|
|
|
25210
25095
|
constructor(rec) {
|
|
@@ -25925,7 +25810,7 @@ const BamUtils = {
|
|
|
25925
25810
|
const lseq = readInt(ba, offset + 20);
|
|
25926
25811
|
const mateChrIdx = readInt(ba, offset + 24);
|
|
25927
25812
|
const matePos = readInt(ba, offset + 28);
|
|
25928
|
-
const
|
|
25813
|
+
const fragmentLength = readInt(ba, offset + 32);
|
|
25929
25814
|
|
|
25930
25815
|
let readName = [];
|
|
25931
25816
|
for (let j = 0; j < nl - 1; ++j) {
|
|
@@ -25966,7 +25851,7 @@ const BamUtils = {
|
|
|
25966
25851
|
alignment.readName = readName;
|
|
25967
25852
|
alignment.cigar = cigar;
|
|
25968
25853
|
alignment.lengthOnRef = lengthOnRef;
|
|
25969
|
-
alignment.fragmentLength =
|
|
25854
|
+
alignment.fragmentLength = fragmentLength;
|
|
25970
25855
|
alignment.mq = mq;
|
|
25971
25856
|
|
|
25972
25857
|
BamUtils.bam_tag2cigar(ba, blockEnd, p, lseq, alignment, cigarArray);
|
|
@@ -26394,7 +26279,7 @@ class BamReaderNonIndexed {
|
|
|
26394
26279
|
const header = this.header;
|
|
26395
26280
|
const queryChr = header.chrAliasTable.hasOwnProperty(chr) ? header.chrAliasTable[chr] : chr;
|
|
26396
26281
|
const qAlignments = this.alignmentCache.queryFeatures(queryChr, bpStart, bpEnd);
|
|
26397
|
-
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.
|
|
26282
|
+
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config);
|
|
26398
26283
|
for (let a of qAlignments) {
|
|
26399
26284
|
alignmentContainer.push(a);
|
|
26400
26285
|
}
|
|
@@ -26421,13 +26306,13 @@ class BamReaderNonIndexed {
|
|
|
26421
26306
|
const alignments = [];
|
|
26422
26307
|
this.header = BamUtils.decodeBamHeader(data);
|
|
26423
26308
|
BamUtils.decodeBamRecords(data, this.header.size, alignments, this.header.chrNames);
|
|
26424
|
-
this.alignmentCache = new FeatureCache(alignments, this.genome);
|
|
26309
|
+
this.alignmentCache = new FeatureCache$1(alignments, this.genome);
|
|
26425
26310
|
}
|
|
26426
26311
|
|
|
26427
26312
|
fetchAlignments(chr, bpStart, bpEnd) {
|
|
26428
26313
|
const queryChr = this.header.chrAliasTable.hasOwnProperty(chr) ? this.header.chrAliasTable[chr] : chr;
|
|
26429
26314
|
const features = this.alignmentCache.queryFeatures(queryChr, bpStart, bpEnd);
|
|
26430
|
-
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.
|
|
26315
|
+
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config);
|
|
26431
26316
|
for (let feature of features) {
|
|
26432
26317
|
alignmentContainer.push(feature);
|
|
26433
26318
|
}
|
|
@@ -27418,9 +27303,7 @@ class BamReader {
|
|
|
27418
27303
|
const chrToIndex = await this.getChrIndex();
|
|
27419
27304
|
const queryChr = this.chrAliasTable.hasOwnProperty(chr) ? this.chrAliasTable[chr] : chr;
|
|
27420
27305
|
const chrId = chrToIndex[queryChr];
|
|
27421
|
-
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd,
|
|
27422
|
-
this.config.samplingWindowSize, this.config.samplingDepth,
|
|
27423
|
-
this.config.pairsSupported, this.config.alleleFreqThreshold);
|
|
27306
|
+
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config);
|
|
27424
27307
|
|
|
27425
27308
|
if (chrId === undefined) {
|
|
27426
27309
|
return alignmentContainer
|
|
@@ -27552,7 +27435,7 @@ class ShardedBamReader {
|
|
|
27552
27435
|
async readAlignments(chr, start, end) {
|
|
27553
27436
|
|
|
27554
27437
|
if (!this.bamReaders.hasOwnProperty(chr)) {
|
|
27555
|
-
return new AlignmentContainer(chr, start, end)
|
|
27438
|
+
return new AlignmentContainer(chr, start, end, this.config)
|
|
27556
27439
|
} else {
|
|
27557
27440
|
|
|
27558
27441
|
let reader = this.bamReaders[chr];
|
|
@@ -27643,7 +27526,7 @@ BamWebserviceReader.prototype.readAlignments = function (chr, bpStart, bpEnd) {
|
|
|
27643
27526
|
|
|
27644
27527
|
header.chrToIndex[queryChr];
|
|
27645
27528
|
|
|
27646
|
-
alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, self.
|
|
27529
|
+
alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, self.config);
|
|
27647
27530
|
|
|
27648
27531
|
BamUtils.decodeSamRecords(sam, alignmentContainer, queryChr, bpStart, bpEnd, self.filter);
|
|
27649
27532
|
|
|
@@ -27897,7 +27780,7 @@ class HtsgetBamReader extends HtsgetReader {
|
|
|
27897
27780
|
const ba = unbgzf(compressedData.buffer);
|
|
27898
27781
|
|
|
27899
27782
|
const chrIdx = this.header.chrToIndex[chr];
|
|
27900
|
-
const alignmentContainer = new AlignmentContainer(chr, start, end, this.
|
|
27783
|
+
const alignmentContainer = new AlignmentContainer(chr, start, end, this.config);
|
|
27901
27784
|
BamUtils.decodeBamRecords(ba, this.header.size, alignmentContainer, this.header.chrNames, chrIdx, start, end);
|
|
27902
27785
|
alignmentContainer.finish();
|
|
27903
27786
|
|
|
@@ -28063,8 +27946,7 @@ class CramReader {
|
|
|
28063
27946
|
const header = await this.getHeader();
|
|
28064
27947
|
const queryChr = header.chrAliasTable.hasOwnProperty(chr) ? header.chrAliasTable[chr] : chr;
|
|
28065
27948
|
const chrIdx = header.chrToIndex[queryChr];
|
|
28066
|
-
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd,
|
|
28067
|
-
this.samplingWindowSize, this.samplingDepth, this.pairsSupported, this.alleleFreqThreshold);
|
|
27949
|
+
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config);
|
|
28068
27950
|
|
|
28069
27951
|
if (chrIdx === undefined) {
|
|
28070
27952
|
return alignmentContainer
|
|
@@ -28680,7 +28562,7 @@ Ga4ghAlignmentReader.prototype.readAlignments = function (chr, bpStart, bpEnd) {
|
|
|
28680
28562
|
"pageSize": "10000"
|
|
28681
28563
|
},
|
|
28682
28564
|
decode: decodeGa4ghReads,
|
|
28683
|
-
results: new AlignmentContainer(chr, bpStart, bpEnd, self.
|
|
28565
|
+
results: new AlignmentContainer(chr, bpStart, bpEnd, self.config)
|
|
28684
28566
|
})
|
|
28685
28567
|
})
|
|
28686
28568
|
|
|
@@ -29013,7 +28895,6 @@ class BamSource {
|
|
|
29013
28895
|
|
|
29014
28896
|
this.config = config;
|
|
29015
28897
|
this.genome = genome;
|
|
29016
|
-
this.alignmentContainer = undefined;
|
|
29017
28898
|
|
|
29018
28899
|
if (isDataURL(config.url)) {
|
|
29019
28900
|
if ("cram" === config.format) {
|
|
@@ -29061,19 +28942,11 @@ class BamSource {
|
|
|
29061
28942
|
}
|
|
29062
28943
|
|
|
29063
28944
|
setViewAsPairs(bool) {
|
|
29064
|
-
|
|
29065
|
-
if (this.viewAsPairs !== bool) {
|
|
29066
|
-
this.viewAsPairs = bool;
|
|
29067
|
-
// if (this.alignmentContainer) {
|
|
29068
|
-
// this.alignmentContainer.setViewAsPairs(bool);
|
|
29069
|
-
// }
|
|
29070
|
-
}
|
|
28945
|
+
this.viewAsPairs = bool;
|
|
29071
28946
|
}
|
|
29072
28947
|
|
|
29073
28948
|
setShowSoftClips(bool) {
|
|
29074
|
-
|
|
29075
|
-
this.showSoftClips = bool;
|
|
29076
|
-
}
|
|
28949
|
+
this.showSoftClips = bool;
|
|
29077
28950
|
}
|
|
29078
28951
|
|
|
29079
28952
|
async getAlignments(chr, bpStart, bpEnd) {
|
|
@@ -29081,33 +28954,28 @@ class BamSource {
|
|
|
29081
28954
|
const genome = this.genome;
|
|
29082
28955
|
const showSoftClips = this.showSoftClips;
|
|
29083
28956
|
|
|
29084
|
-
|
|
29085
|
-
|
|
28957
|
+
const alignmentContainer = await this.bamReader.readAlignments(chr, bpStart, bpEnd);
|
|
28958
|
+
let alignments = alignmentContainer.alignments;
|
|
28959
|
+
if (!this.viewAsPairs) {
|
|
28960
|
+
alignments = unpairAlignments([{alignments: alignments}]);
|
|
28961
|
+
}
|
|
28962
|
+
const hasAlignments = alignments.length > 0;
|
|
28963
|
+
alignmentContainer.packedAlignmentRows = packAlignmentRows(alignments, alignmentContainer.start, alignmentContainer.end, showSoftClips);
|
|
29086
28964
|
|
|
29087
|
-
|
|
29088
|
-
|
|
29089
|
-
|
|
29090
|
-
|
|
29091
|
-
|
|
29092
|
-
|
|
29093
|
-
|
|
29094
|
-
|
|
29095
|
-
|
|
29096
|
-
|
|
29097
|
-
this.alignmentContainer = alignmentContainer;
|
|
29098
|
-
|
|
29099
|
-
if (hasAlignments) {
|
|
29100
|
-
const sequence = await genome.sequence.getSequence(chr, alignmentContainer.start, alignmentContainer.end);
|
|
29101
|
-
if (sequence) {
|
|
29102
|
-
alignmentContainer.coverageMap.refSeq = sequence; // TODO -- fix this
|
|
29103
|
-
alignmentContainer.sequence = sequence; // TODO -- fix this
|
|
29104
|
-
return alignmentContainer
|
|
29105
|
-
} else {
|
|
29106
|
-
console.error("No sequence for: " + chr + ":" + alignmentContainer.start + "-" + alignmentContainer.end);
|
|
29107
|
-
}
|
|
28965
|
+
this.alignmentContainer = alignmentContainer;
|
|
28966
|
+
|
|
28967
|
+
if (hasAlignments) {
|
|
28968
|
+
const sequence = await genome.sequence.getSequence(chr, alignmentContainer.start, alignmentContainer.end);
|
|
28969
|
+
if (sequence) {
|
|
28970
|
+
alignmentContainer.coverageMap.refSeq = sequence; // TODO -- fix this
|
|
28971
|
+
alignmentContainer.sequence = sequence; // TODO -- fix this
|
|
28972
|
+
return alignmentContainer
|
|
28973
|
+
} else {
|
|
28974
|
+
console.error("No sequence for: " + chr + ":" + alignmentContainer.start + "-" + alignmentContainer.end);
|
|
29108
28975
|
}
|
|
29109
|
-
return alignmentContainer
|
|
29110
28976
|
}
|
|
28977
|
+
return alignmentContainer
|
|
28978
|
+
|
|
29111
28979
|
}
|
|
29112
28980
|
}
|
|
29113
28981
|
|
|
@@ -29287,7 +29155,7 @@ class TrackBase {
|
|
|
29287
29155
|
return state
|
|
29288
29156
|
}
|
|
29289
29157
|
|
|
29290
|
-
supportsWholeGenome() {
|
|
29158
|
+
get supportsWholeGenome() {
|
|
29291
29159
|
return false
|
|
29292
29160
|
}
|
|
29293
29161
|
|
|
@@ -29425,7 +29293,7 @@ class TrackBase {
|
|
|
29425
29293
|
|
|
29426
29294
|
// We use the cached features rather than method to avoid async load. If the
|
|
29427
29295
|
// feature is not already loaded this won't work, but the user wouldn't be mousing over it either.
|
|
29428
|
-
if (!features) features = clickState.viewport.
|
|
29296
|
+
if (!features) features = clickState.viewport.cachedFeatures;
|
|
29429
29297
|
|
|
29430
29298
|
if (!features || features.length === 0) {
|
|
29431
29299
|
return []
|
|
@@ -29929,8 +29797,7 @@ class Locus {
|
|
|
29929
29797
|
}
|
|
29930
29798
|
|
|
29931
29799
|
overlaps(locus) {
|
|
29932
|
-
return locus.chr === this.chr
|
|
29933
|
-
&& !(locus.end < this.start || locus.start > this.end)
|
|
29800
|
+
return locus.chr === this.chr && !(locus.end < this.start || locus.start > this.end)
|
|
29934
29801
|
}
|
|
29935
29802
|
|
|
29936
29803
|
extend(l) {
|
|
@@ -31807,6 +31674,22 @@ function makeCircViewChromosomes(genome) {
|
|
|
31807
31674
|
return regions
|
|
31808
31675
|
}
|
|
31809
31676
|
|
|
31677
|
+
function sendChords(chords, track, refFrame, alpha) {
|
|
31678
|
+
|
|
31679
|
+
const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? track.color : getChrColor(refFrame.chr), alpha);
|
|
31680
|
+
const trackColor = IGVColor.addAlpha(track.color || 'rgb(0,0,255)', alpha);
|
|
31681
|
+
|
|
31682
|
+
// name the chord set to include locus and filtering information
|
|
31683
|
+
const encodedName = track.name.replaceAll(' ', '%20');
|
|
31684
|
+
const chordSetName = "all" === refFrame.chr ? encodedName :
|
|
31685
|
+
`${encodedName} ${refFrame.chr}:${refFrame.start}-${refFrame.end}`;
|
|
31686
|
+
track.browser.circularView.addChords(chords, {track: chordSetName, color: chordSetColor, trackColor: trackColor});
|
|
31687
|
+
|
|
31688
|
+
// show circular view if hidden
|
|
31689
|
+
if(!track.browser.circularViewVisible) track.browser.circularViewVisible = true;
|
|
31690
|
+
|
|
31691
|
+
}
|
|
31692
|
+
|
|
31810
31693
|
|
|
31811
31694
|
function createCircularView(el, browser) {
|
|
31812
31695
|
|
|
@@ -31816,42 +31699,105 @@ function createCircularView(el, browser) {
|
|
|
31816
31699
|
|
|
31817
31700
|
const f1 = feature.data;
|
|
31818
31701
|
const f2 = f1.mate;
|
|
31819
|
-
|
|
31702
|
+
addFrameForFeature(f1);
|
|
31703
|
+
addFrameForFeature(f2);
|
|
31820
31704
|
|
|
31821
|
-
|
|
31822
|
-
const l2 = new Locus({chr: browser.genome.getChromosomeName(f2.refName), start: f2.start, end: f2.end});
|
|
31705
|
+
function addFrameForFeature(feature) {
|
|
31823
31706
|
|
|
31824
|
-
|
|
31707
|
+
feature.chr = browser.genome.getChromosomeName(feature.refName);
|
|
31708
|
+
let frameFound = false;
|
|
31709
|
+
for (let referenceFrame of browser.referenceFrameList) {
|
|
31710
|
+
const l = Locus.fromLocusString(referenceFrame.getLocusString());
|
|
31711
|
+
if (l.contains(feature)) {
|
|
31712
|
+
frameFound = true;
|
|
31713
|
+
break
|
|
31714
|
+
} else if (l.overlaps(feature)) {
|
|
31715
|
+
referenceFrame.extend(feature);
|
|
31716
|
+
frameFound = true;
|
|
31717
|
+
break
|
|
31718
|
+
}
|
|
31719
|
+
}
|
|
31720
|
+
if (!frameFound) {
|
|
31721
|
+
const flanking = 2000;
|
|
31722
|
+
const center = (feature.start + feature.end) / 2;
|
|
31723
|
+
browser.addMultiLocusPanel(feature.chr, center - flanking, center + flanking);
|
|
31825
31724
|
|
|
31826
|
-
|
|
31725
|
+
}
|
|
31726
|
+
}
|
|
31727
|
+
}
|
|
31728
|
+
});
|
|
31827
31729
|
|
|
31828
|
-
|
|
31730
|
+
return circularView
|
|
31731
|
+
}
|
|
31829
31732
|
|
|
31830
|
-
|
|
31831
|
-
|
|
31832
|
-
|
|
31833
|
-
|
|
31834
|
-
|
|
31835
|
-
|
|
31836
|
-
|
|
31837
|
-
|
|
31838
|
-
|
|
31733
|
+
class PairedEndStats {
|
|
31734
|
+
|
|
31735
|
+
constructor(alignments, {minTLENPercentile, maxTLENPercentile}) {
|
|
31736
|
+
this.totalCount = 0;
|
|
31737
|
+
this.frCount = 0;
|
|
31738
|
+
this.rfCount = 0;
|
|
31739
|
+
this.ffCount = 0;
|
|
31740
|
+
this.sumF = 0;
|
|
31741
|
+
this.sumF2 = 0;
|
|
31742
|
+
this.lp = minTLENPercentile === undefined ? 0.1 : minTLENPercentile;
|
|
31743
|
+
this.up = maxTLENPercentile === undefined ? 99.5 : maxTLENPercentile;
|
|
31744
|
+
this.isizes = [];
|
|
31745
|
+
this.compute(alignments);
|
|
31746
|
+
}
|
|
31747
|
+
|
|
31748
|
+
compute(alignments) {
|
|
31749
|
+
|
|
31750
|
+
for (let alignment of alignments) {
|
|
31751
|
+
if (alignment.isProperPair()) {
|
|
31752
|
+
var tlen = Math.abs(alignment.fragmentLength);
|
|
31753
|
+
this.sumF += tlen;
|
|
31754
|
+
this.sumF2 += tlen * tlen;
|
|
31755
|
+
this.isizes.push(tlen);
|
|
31756
|
+
|
|
31757
|
+
var po = alignment.pairOrientation;
|
|
31758
|
+
|
|
31759
|
+
if (typeof po === "string" && po.length === 4) {
|
|
31760
|
+
var tmp = '' + po.charAt(0) + po.charAt(2);
|
|
31761
|
+
switch (tmp) {
|
|
31762
|
+
case 'FF':
|
|
31763
|
+
case 'RR':
|
|
31764
|
+
this.ffCount++;
|
|
31765
|
+
break
|
|
31766
|
+
case "FR":
|
|
31767
|
+
this.frCount++;
|
|
31768
|
+
break
|
|
31769
|
+
case"RF":
|
|
31770
|
+
this.rfCount++;
|
|
31839
31771
|
}
|
|
31840
31772
|
}
|
|
31841
|
-
|
|
31842
|
-
l1.start = Math.max(0, l1.start - flanking);
|
|
31843
|
-
l1.end += flanking;
|
|
31844
|
-
l2.start = Math.max(0, l2.start - flanking);
|
|
31845
|
-
l2.end += flanking;
|
|
31846
|
-
loci = [l1, l2];
|
|
31773
|
+
this.totalCount++;
|
|
31847
31774
|
}
|
|
31848
|
-
|
|
31849
|
-
const searchString = loci.map(l => l.getLocusString()).join(" ");
|
|
31850
|
-
browser.search(searchString);
|
|
31851
31775
|
}
|
|
31776
|
+
|
|
31777
|
+
if (this.ffCount / this.totalCount > 0.9) this.orienation = "ff";
|
|
31778
|
+
else if (this.frCount / this.totalCount > 0.9) this.orienation = "fr";
|
|
31779
|
+
else if (this.rfCount / this.totalCount > 0.9) this.orienation = "rf";
|
|
31780
|
+
|
|
31781
|
+
this.minTLEN = this.lp === 0 ? 0 : percentile(this.isizes, this.lp);
|
|
31782
|
+
this.maxTLEN = percentile(this.isizes, this.up);
|
|
31783
|
+
|
|
31784
|
+
// var fMean = this.sumF / this.totalCount
|
|
31785
|
+
// var stdDev = Math.sqrt((this.totalCount * this.sumF2 - this.sumF * this.sumF) / (this.totalCount * this.totalCount))
|
|
31786
|
+
// this.minTLEN = fMean - 3 * stdDev
|
|
31787
|
+
// this.maxTLEN = fMean + 3 * stdDev
|
|
31788
|
+
|
|
31789
|
+
}
|
|
31790
|
+
}
|
|
31791
|
+
|
|
31792
|
+
function percentile(array, p) {
|
|
31793
|
+
|
|
31794
|
+
if (array.length === 0) return undefined
|
|
31795
|
+
var k = Math.floor(array.length * (p / 100));
|
|
31796
|
+
array.sort(function (a, b) {
|
|
31797
|
+
return a - b
|
|
31852
31798
|
});
|
|
31799
|
+
return array[k]
|
|
31853
31800
|
|
|
31854
|
-
return circularView
|
|
31855
31801
|
}
|
|
31856
31802
|
|
|
31857
31803
|
/*
|
|
@@ -31918,8 +31864,6 @@ class BAMTrack extends TrackBase {
|
|
|
31918
31864
|
this.showMismatches = false !== config.showMismatches;
|
|
31919
31865
|
this.color = config.color;
|
|
31920
31866
|
this.coverageColor = config.coverageColor;
|
|
31921
|
-
this.minFragmentLength = config.minFragmentLength; // Optional, might be undefined
|
|
31922
|
-
this.maxFragmentLength = config.maxFragmentLength || 1000;
|
|
31923
31867
|
|
|
31924
31868
|
// The sort object can be an array in the case of multi-locus view, however if multiple sort positions
|
|
31925
31869
|
// are present for a given reference frame the last one will take precedence
|
|
@@ -31947,12 +31891,24 @@ class BAMTrack extends TrackBase {
|
|
|
31947
31891
|
return this._height
|
|
31948
31892
|
}
|
|
31949
31893
|
|
|
31894
|
+
get minTemplateLength() {
|
|
31895
|
+
const configMinTLEN = this.config.minTLEN !== undefined ? this.config.minTLEN : this.config.minFragmentLength;
|
|
31896
|
+
return (configMinTLEN !== undefined) ? configMinTLEN :
|
|
31897
|
+
this._pairedEndStats ? this._pairedEndStats.minTLEN : 0
|
|
31898
|
+
}
|
|
31899
|
+
|
|
31900
|
+
get maxTemplateLength() {
|
|
31901
|
+
const configMaxTLEN = this.config.maxTLEN !== undefined ? this.config.maxTLEN : this.config.maxFragmentLength;
|
|
31902
|
+
return (configMaxTLEN !== undefined) ? configMaxTLEN :
|
|
31903
|
+
this._pairedEndStats ? this._pairedEndStats.maxTLEN : 1000
|
|
31904
|
+
}
|
|
31905
|
+
|
|
31950
31906
|
sort(options) {
|
|
31951
31907
|
options = this.assignSort(options);
|
|
31952
31908
|
|
|
31953
31909
|
for (let vp of this.trackView.viewports) {
|
|
31954
31910
|
if (vp.containsPosition(options.chr, options.position)) {
|
|
31955
|
-
const alignmentContainer = vp.
|
|
31911
|
+
const alignmentContainer = vp.cachedFeatures;
|
|
31956
31912
|
if (alignmentContainer) {
|
|
31957
31913
|
sortAlignmentRows(options, alignmentContainer);
|
|
31958
31914
|
vp.repaint();
|
|
@@ -31987,14 +31943,13 @@ class BAMTrack extends TrackBase {
|
|
|
31987
31943
|
|
|
31988
31944
|
const alignmentContainer = await this.featureSource.getAlignments(chr, bpStart, bpEnd);
|
|
31989
31945
|
|
|
31990
|
-
if (alignmentContainer.
|
|
31991
|
-
|
|
31992
|
-
|
|
31993
|
-
|
|
31994
|
-
if (undefined === this.maxFragmentLength) {
|
|
31995
|
-
this.maxFragmentLength = alignmentContainer.pairedEndStats.upperFragmentLength;
|
|
31946
|
+
if (alignmentContainer.paired && !this._pairedEndStats && !this.config.maxFragmentLength) {
|
|
31947
|
+
const pairedEndStats = new PairedEndStats(alignmentContainer.alignments, this.config);
|
|
31948
|
+
if (pairedEndStats.totalCount > 99) {
|
|
31949
|
+
this._pairedEndStats = pairedEndStats;
|
|
31996
31950
|
}
|
|
31997
31951
|
}
|
|
31952
|
+
alignmentContainer.alignments = undefined; // Don't need to hold onto these anymore
|
|
31998
31953
|
|
|
31999
31954
|
const sort = this.sortObject;
|
|
32000
31955
|
if (sort) {
|
|
@@ -32091,7 +32046,7 @@ class BAMTrack extends TrackBase {
|
|
|
32091
32046
|
if (this.alignmentTrack.hasPairs) {
|
|
32092
32047
|
colorByMenuItems.push({key: 'firstOfPairStrand', label: 'first-of-pair strand'});
|
|
32093
32048
|
colorByMenuItems.push({key: 'pairOrientation', label: 'pair orientation'});
|
|
32094
|
-
colorByMenuItems.push({key: '
|
|
32049
|
+
colorByMenuItems.push({key: 'tlen', label: 'insert size (TLEN)'});
|
|
32095
32050
|
colorByMenuItems.push({key: 'unexpectedPair', label: 'pair orientation & insert size (TLEN)'});
|
|
32096
32051
|
}
|
|
32097
32052
|
const tagLabel = 'tag' + (this.alignmentTrack.colorByTag ? ' (' + this.alignmentTrack.colorByTag + ')' : '');
|
|
@@ -32197,32 +32152,17 @@ class BAMTrack extends TrackBase {
|
|
|
32197
32152
|
});
|
|
32198
32153
|
}
|
|
32199
32154
|
|
|
32200
|
-
//
|
|
32201
|
-
if (this.browser.circularView &&
|
|
32155
|
+
// Add chords to JBrowse circular view, if present
|
|
32156
|
+
if (this.browser.circularView &&
|
|
32202
32157
|
(this.alignmentTrack.hasPairs || this.alignmentTrack.hasSupplemental)) {
|
|
32203
32158
|
menuItems.push('<hr/>');
|
|
32204
32159
|
if (this.alignmentTrack.hasPairs) {
|
|
32205
32160
|
menuItems.push({
|
|
32206
32161
|
label: 'Add discordant pairs to circular view',
|
|
32207
32162
|
click: () => {
|
|
32208
|
-
const maxFragmentLength = this.maxFragmentLength;
|
|
32209
|
-
const inView = [];
|
|
32210
32163
|
for (let viewport of this.trackView.viewports) {
|
|
32211
|
-
|
|
32212
|
-
const referenceFrame = viewport.referenceFrame;
|
|
32213
|
-
if (a.end >= referenceFrame.start
|
|
32214
|
-
&& a.start <= referenceFrame.end
|
|
32215
|
-
&& a.mate
|
|
32216
|
-
&& a.mate.chr
|
|
32217
|
-
&& (a.mate.chr !== a.chr || Math.max(a.fragmentLength) > maxFragmentLength)) {
|
|
32218
|
-
inView.push(a);
|
|
32219
|
-
}
|
|
32220
|
-
}
|
|
32164
|
+
this.addPairedChordsForViewport(viewport);
|
|
32221
32165
|
}
|
|
32222
|
-
this.browser.circularViewVisible = true;
|
|
32223
|
-
const chords = makePairedAlignmentChords(inView);
|
|
32224
|
-
const color = IGVColor.addAlpha(this.color || 'rgb(0,0,255)', 0.02);
|
|
32225
|
-
this.browser.circularView.addChords(chords, {track: this.name, color: color});
|
|
32226
32166
|
}
|
|
32227
32167
|
});
|
|
32228
32168
|
}
|
|
@@ -32230,19 +32170,9 @@ class BAMTrack extends TrackBase {
|
|
|
32230
32170
|
menuItems.push({
|
|
32231
32171
|
label: 'Add split reads to circular view',
|
|
32232
32172
|
click: () => {
|
|
32233
|
-
const inView = [];
|
|
32234
32173
|
for (let viewport of this.trackView.viewports) {
|
|
32235
|
-
|
|
32236
|
-
const referenceFrame = viewport.referenceFrame;
|
|
32237
|
-
const sa = a.hasTag('SA');
|
|
32238
|
-
if (a.end >= referenceFrame.start && a.start <= referenceFrame.end && sa) {
|
|
32239
|
-
inView.push(a);
|
|
32240
|
-
}
|
|
32241
|
-
}
|
|
32174
|
+
this.addSplitChordsForViewport(viewport);
|
|
32242
32175
|
}
|
|
32243
|
-
const chords = makeSupplementalAlignmentChords(inView);
|
|
32244
|
-
const color = IGVColor.addAlpha(this.color || 'rgb(0,0,255)', 0.1);
|
|
32245
|
-
this.browser.circularView.addChords(chords, {track: this.name, color: color});
|
|
32246
32176
|
}
|
|
32247
32177
|
});
|
|
32248
32178
|
}
|
|
@@ -32354,7 +32284,7 @@ class BAMTrack extends TrackBase {
|
|
|
32354
32284
|
}
|
|
32355
32285
|
|
|
32356
32286
|
getCachedAlignmentContainers() {
|
|
32357
|
-
return this.trackView.viewports.map(vp => vp.
|
|
32287
|
+
return this.trackView.viewports.map(vp => vp.cachedFeatures)
|
|
32358
32288
|
}
|
|
32359
32289
|
|
|
32360
32290
|
get dataRange() {
|
|
@@ -32380,6 +32310,62 @@ class BAMTrack extends TrackBase {
|
|
|
32380
32310
|
set autoscale(autoscale) {
|
|
32381
32311
|
this.coverageTrack.autoscale = autoscale;
|
|
32382
32312
|
}
|
|
32313
|
+
|
|
32314
|
+
/**
|
|
32315
|
+
* Add chords to the circular view for the given viewport, represented by its reference frame
|
|
32316
|
+
* @param refFrame
|
|
32317
|
+
*/
|
|
32318
|
+
addPairedChordsForViewport(viewport) {
|
|
32319
|
+
|
|
32320
|
+
const maxTemplateLength = this.maxTemplateLength;
|
|
32321
|
+
const inView = [];
|
|
32322
|
+
const refFrame = viewport.referenceFrame;
|
|
32323
|
+
for (let a of viewport.cachedFeatures.allAlignments()) {
|
|
32324
|
+
if (a.end >= refFrame.start
|
|
32325
|
+
&& a.start <= refFrame.end
|
|
32326
|
+
&& a.mate
|
|
32327
|
+
&& a.mate.chr
|
|
32328
|
+
&& (a.mate.chr !== a.chr || Math.max(a.fragmentLength) > maxTemplateLength)) {
|
|
32329
|
+
inView.push(a);
|
|
32330
|
+
}
|
|
32331
|
+
}
|
|
32332
|
+
const chords = makePairedAlignmentChords(inView);
|
|
32333
|
+
sendChords(chords, this, refFrame, 0.02);
|
|
32334
|
+
|
|
32335
|
+
// const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? this.color : getChrColor(refFrame.chr), 0.02)
|
|
32336
|
+
// const trackColor = IGVColor.addAlpha(this.color || 'rgb(0,0,255)', 0.02)
|
|
32337
|
+
//
|
|
32338
|
+
// // name the chord set to include track name and locus
|
|
32339
|
+
// const encodedName = this.name.replaceAll(' ', '%20')
|
|
32340
|
+
// const chordSetName = "all" === refFrame.chr ? encodedName :
|
|
32341
|
+
// `${encodedName} (${refFrame.chr}:${refFrame.start}-${refFrame.end}`
|
|
32342
|
+
// this.browser.circularView.addChords(chords, {name: chordSetName, color: chordSetColor, trackColor: trackColor})
|
|
32343
|
+
}
|
|
32344
|
+
|
|
32345
|
+
addSplitChordsForViewport(viewport) {
|
|
32346
|
+
|
|
32347
|
+
const inView = [];
|
|
32348
|
+
const refFrame = viewport.referenceFrame;
|
|
32349
|
+
for (let a of viewport.cachedFeatures.allAlignments()) {
|
|
32350
|
+
|
|
32351
|
+
const sa = a.hasTag('SA');
|
|
32352
|
+
if (a.end >= refFrame.start && a.start <= refFrame.end && sa) {
|
|
32353
|
+
inView.push(a);
|
|
32354
|
+
}
|
|
32355
|
+
}
|
|
32356
|
+
|
|
32357
|
+
const chords = makeSupplementalAlignmentChords(inView);
|
|
32358
|
+
sendChords(chords, this, refFrame, 0.02);
|
|
32359
|
+
|
|
32360
|
+
// const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? this.color : getChrColor(refFrame.chr), 0.02)
|
|
32361
|
+
// const trackColor = IGVColor.addAlpha(this.color || 'rgb(0,0,255)', 0.02)
|
|
32362
|
+
//
|
|
32363
|
+
// // name the chord set to include track name and locus
|
|
32364
|
+
// const encodedName = this.name.replaceAll(' ', '%20')
|
|
32365
|
+
// const chordSetName = "all" === refFrame.chr ? encodedName :
|
|
32366
|
+
// `${encodedName} (${refFrame.chr}:${refFrame.start}-${refFrame.end}`
|
|
32367
|
+
// this.browser.circularView.addChords(chords, {name: chordSetName, color: chordSetColor, trackColor: trackColor})
|
|
32368
|
+
}
|
|
32383
32369
|
}
|
|
32384
32370
|
|
|
32385
32371
|
|
|
@@ -32500,7 +32486,7 @@ class CoverageTrack {
|
|
|
32500
32486
|
|
|
32501
32487
|
getClickedObject(clickState) {
|
|
32502
32488
|
|
|
32503
|
-
let features = clickState.viewport.
|
|
32489
|
+
let features = clickState.viewport.cachedFeatures;
|
|
32504
32490
|
if (!features || features.length === 0) return
|
|
32505
32491
|
|
|
32506
32492
|
const genomicLocation = Math.floor(clickState.genomicLocation);
|
|
@@ -32576,8 +32562,8 @@ class AlignmentTrack {
|
|
|
32576
32562
|
this.skippedColor = config.skippedColor || "rgb(150, 170, 170)";
|
|
32577
32563
|
this.pairConnectorColor = config.pairConnectorColor;
|
|
32578
32564
|
|
|
32579
|
-
this.
|
|
32580
|
-
this.
|
|
32565
|
+
this.smallTLENColor = config.smallTLENColor || config.smallFragmentLengthColor || "rgb(0, 0, 150)";
|
|
32566
|
+
this.largeTLENColor = config.largeTLENColor || config.largeFragmentLengthColor || "rgb(200, 0, 0)";
|
|
32581
32567
|
|
|
32582
32568
|
this.pairOrientation = config.pairOrienation || 'fr';
|
|
32583
32569
|
this.pairColors = {};
|
|
@@ -32585,7 +32571,7 @@ class AlignmentTrack {
|
|
|
32585
32571
|
this.pairColors["RR"] = config.rrColor || "rgb(20, 50, 200)";
|
|
32586
32572
|
this.pairColors["LL"] = config.llColor || "rgb(0, 150, 150)";
|
|
32587
32573
|
|
|
32588
|
-
this.colorBy = config.colorBy || "
|
|
32574
|
+
this.colorBy = config.colorBy || "unexpectedPair";
|
|
32589
32575
|
this.colorByTag = config.colorByTag ? config.colorByTag.toUpperCase() : undefined;
|
|
32590
32576
|
this.bamColorTag = config.bamColorTag === undefined ? "YC" : config.bamColorTag;
|
|
32591
32577
|
|
|
@@ -32692,7 +32678,7 @@ class AlignmentTrack {
|
|
|
32692
32678
|
for (let alignment of alignmentRow.alignments) {
|
|
32693
32679
|
|
|
32694
32680
|
this.hasPairs = this.hasPairs || alignment.isPaired();
|
|
32695
|
-
if (this.browser.circularView
|
|
32681
|
+
if (this.browser.circularView) {
|
|
32696
32682
|
// This is an expensive check, only do it if needed
|
|
32697
32683
|
this.hasSupplemental = this.hasSupplemental || alignment.hasTag('SA');
|
|
32698
32684
|
}
|
|
@@ -32977,7 +32963,7 @@ class AlignmentTrack {
|
|
|
32977
32963
|
direction: direction
|
|
32978
32964
|
};
|
|
32979
32965
|
this.parent.sortObject = newSortObject;
|
|
32980
|
-
sortAlignmentRows(newSortObject, viewport.
|
|
32966
|
+
sortAlignmentRows(newSortObject, viewport.cachedFeatures);
|
|
32981
32967
|
viewport.repaint();
|
|
32982
32968
|
};
|
|
32983
32969
|
list.push('<b>Sort by...</b>');
|
|
@@ -33007,7 +32993,7 @@ class AlignmentTrack {
|
|
|
33007
32993
|
};
|
|
33008
32994
|
this.sortByTag = tag;
|
|
33009
32995
|
this.parent.sortObject = newSortObject;
|
|
33010
|
-
sortAlignmentRows(newSortObject, viewport.
|
|
32996
|
+
sortAlignmentRows(newSortObject, viewport.cachedFeatures);
|
|
33011
32997
|
viewport.repaint();
|
|
33012
32998
|
}
|
|
33013
32999
|
}
|
|
@@ -33034,7 +33020,11 @@ class AlignmentTrack {
|
|
|
33034
33020
|
const referenceFrame = clickState.viewport.referenceFrame;
|
|
33035
33021
|
if (this.browser.genome.getChromosome(clickedAlignment.mate.chr)) {
|
|
33036
33022
|
this.highlightedAlignmentReadNamed = clickedAlignment.readName;
|
|
33037
|
-
this.browser.presentMultiLocusPanel(clickedAlignment, referenceFrame)
|
|
33023
|
+
//this.browser.presentMultiLocusPanel(clickedAlignment, referenceFrame)
|
|
33024
|
+
const bpWidth = referenceFrame.end - referenceFrame.start;
|
|
33025
|
+
const frameStart = clickedAlignment.mate.position - bpWidth / 2;
|
|
33026
|
+
const frameEnd = clickedAlignment.mate.position + bpWidth / 2;
|
|
33027
|
+
this.browser.addMultiLocusPanel(clickedAlignment.mate.chr, frameStart, frameEnd, referenceFrame);
|
|
33038
33028
|
} else {
|
|
33039
33029
|
Alert.presentAlert(`Reference does not contain chromosome: ${clickedAlignment.mate.chr}`);
|
|
33040
33030
|
}
|
|
@@ -33047,10 +33037,7 @@ class AlignmentTrack {
|
|
|
33047
33037
|
list.push({
|
|
33048
33038
|
label: 'View read sequence',
|
|
33049
33039
|
click: () => {
|
|
33050
|
-
const
|
|
33051
|
-
if (!alignment) return
|
|
33052
|
-
|
|
33053
|
-
const seqstring = alignment.seq; //.map(b => String.fromCharCode(b)).join("");
|
|
33040
|
+
const seqstring = clickedAlignment.seq; //.map(b => String.fromCharCode(b)).join("");
|
|
33054
33041
|
if (!seqstring || "*" === seqstring) {
|
|
33055
33042
|
Alert.presentAlert("Read sequence: *");
|
|
33056
33043
|
} else {
|
|
@@ -33062,11 +33049,16 @@ class AlignmentTrack {
|
|
|
33062
33049
|
if (isSecureContext()) {
|
|
33063
33050
|
list.push({
|
|
33064
33051
|
label: 'Copy read sequence',
|
|
33065
|
-
click: () => {
|
|
33066
|
-
const
|
|
33067
|
-
|
|
33068
|
-
|
|
33069
|
-
|
|
33052
|
+
click: async () => {
|
|
33053
|
+
const seq = clickedAlignment.seq; //.map(b => String.fromCharCode(b)).join("");
|
|
33054
|
+
try {
|
|
33055
|
+
//console.log(`seq: ${seq}`)
|
|
33056
|
+
await navigator.clipboard.writeText(seq);
|
|
33057
|
+
} catch (e) {
|
|
33058
|
+
console.error(e);
|
|
33059
|
+
Alert.presentAlert(`error copying sequence to clipboard ${e}`);
|
|
33060
|
+
}
|
|
33061
|
+
|
|
33070
33062
|
}
|
|
33071
33063
|
});
|
|
33072
33064
|
}
|
|
@@ -33076,25 +33068,12 @@ class AlignmentTrack {
|
|
|
33076
33068
|
}
|
|
33077
33069
|
|
|
33078
33070
|
// Experimental JBrowse feature
|
|
33079
|
-
if (this.browser.circularView &&
|
|
33080
|
-
&& (this.hasPairs || this.hasSupplemental)) {
|
|
33071
|
+
if (this.browser.circularView && (this.hasPairs || this.hasSupplemental)) {
|
|
33081
33072
|
if (this.hasPairs) {
|
|
33082
33073
|
list.push({
|
|
33083
33074
|
label: 'Add discordant pairs to circular view',
|
|
33084
33075
|
click: () => {
|
|
33085
|
-
|
|
33086
|
-
const {referenceFrame} = viewport;
|
|
33087
|
-
const inView = viewport.getCachedFeatures().allAlignments().filter(a => {
|
|
33088
|
-
return a.end >= referenceFrame.start
|
|
33089
|
-
&& a.start <= referenceFrame.end
|
|
33090
|
-
&& a.mate
|
|
33091
|
-
&& a.mate.chr
|
|
33092
|
-
&& (a.mate.chr !== a.chr || Math.max(a.fragmentLength) > maxFragmentLength)
|
|
33093
|
-
});
|
|
33094
|
-
this.browser.circularViewVisible = true;
|
|
33095
|
-
const chords = makePairedAlignmentChords(inView);
|
|
33096
|
-
const color = IGVColor.addAlpha(this.parent.color || 'rgb(0,0,255)', 0.02);
|
|
33097
|
-
this.browser.circularView.addChords(chords, {track: this.parent.name, color: color});
|
|
33076
|
+
this.parent.addPairedChordsForViewport(viewport);
|
|
33098
33077
|
}
|
|
33099
33078
|
});
|
|
33100
33079
|
}
|
|
@@ -33102,17 +33081,7 @@ class AlignmentTrack {
|
|
|
33102
33081
|
list.push({
|
|
33103
33082
|
label: 'Add split reads to circular view',
|
|
33104
33083
|
click: () => {
|
|
33105
|
-
|
|
33106
|
-
for (let a of viewport.getCachedFeatures().allAlignments()) {
|
|
33107
|
-
const referenceFrame = viewport.referenceFrame;
|
|
33108
|
-
const sa = a.hasTag('SA');
|
|
33109
|
-
if (a.end >= referenceFrame.start && a.start <= referenceFrame.end && sa) {
|
|
33110
|
-
inView.push(a);
|
|
33111
|
-
}
|
|
33112
|
-
}
|
|
33113
|
-
const chords = makeSupplementalAlignmentChords(inView);
|
|
33114
|
-
const color = IGVColor.addAlpha(this.color || 'rgb(0,0,255)', 0.1);
|
|
33115
|
-
this.browser.circularView.addChords(chords, {track: this.name, color: color});
|
|
33084
|
+
this.parent.addSplitChordsForViewport(viewport);
|
|
33116
33085
|
}
|
|
33117
33086
|
});
|
|
33118
33087
|
}
|
|
@@ -33131,7 +33100,7 @@ class AlignmentTrack {
|
|
|
33131
33100
|
|
|
33132
33101
|
const showSoftClips = this.parent.showSoftClips;
|
|
33133
33102
|
|
|
33134
|
-
let features = viewport.
|
|
33103
|
+
let features = viewport.cachedFeatures;
|
|
33135
33104
|
if (!features || features.length === 0) return
|
|
33136
33105
|
|
|
33137
33106
|
let packedAlignmentRows = features.packedAlignmentRows;
|
|
@@ -33222,14 +33191,17 @@ class AlignmentTrack {
|
|
|
33222
33191
|
break
|
|
33223
33192
|
}
|
|
33224
33193
|
|
|
33194
|
+
case "tlen":
|
|
33225
33195
|
case "fragmentLength":
|
|
33226
33196
|
|
|
33227
|
-
if (alignment.mate && alignment.isMateMapped()
|
|
33228
|
-
|
|
33229
|
-
|
|
33230
|
-
|
|
33231
|
-
|
|
33232
|
-
|
|
33197
|
+
if (alignment.mate && alignment.isMateMapped()) {
|
|
33198
|
+
if (alignment.mate.chr !== alignment.chr) {
|
|
33199
|
+
color = getChrColor(alignment.mate.chr);
|
|
33200
|
+
} else if (this.parent.minTemplateLength && Math.abs(alignment.fragmentLength) < this.parent.minTemplateLength) {
|
|
33201
|
+
color = this.smallTLENColor;
|
|
33202
|
+
} else if (this.parent.maxTemplateLength && Math.abs(alignment.fragmentLength) > this.parent.maxTemplateLength) {
|
|
33203
|
+
color = this.largeTLENColor;
|
|
33204
|
+
}
|
|
33233
33205
|
}
|
|
33234
33206
|
break
|
|
33235
33207
|
|
|
@@ -33271,11 +33243,6 @@ function sortAlignmentRows(options, alignmentContainer) {
|
|
|
33271
33243
|
return true === direction ? i : -i
|
|
33272
33244
|
});
|
|
33273
33245
|
|
|
33274
|
-
// For debugging
|
|
33275
|
-
// for(let r of alignmentContainer.packedAlignmentRows) {
|
|
33276
|
-
// console.log(r.score);
|
|
33277
|
-
// }
|
|
33278
|
-
|
|
33279
33246
|
}
|
|
33280
33247
|
|
|
33281
33248
|
function shadedBaseColor(qual, baseColor) {
|
|
@@ -33435,7 +33402,7 @@ class RulerViewport extends TrackViewport {
|
|
|
33435
33402
|
|
|
33436
33403
|
this.$rulerLabel.click(async () => {
|
|
33437
33404
|
|
|
33438
|
-
await this.browser.
|
|
33405
|
+
await this.browser.gotoMultilocusPanel(this.referenceFrame);
|
|
33439
33406
|
|
|
33440
33407
|
// const removals = this.browser.referenceFrameList.filter(r => this.referenceFrame !== r)
|
|
33441
33408
|
// for (let referenceFrame of removals) {
|
|
@@ -33558,7 +33525,9 @@ class RulerViewport extends TrackViewport {
|
|
|
33558
33525
|
currentViewport = this;
|
|
33559
33526
|
this.$tooltip.show();
|
|
33560
33527
|
} else if (currentViewport.guid !== this.guid) {
|
|
33561
|
-
currentViewport.$tooltip
|
|
33528
|
+
if (currentViewport.$tooltip) {
|
|
33529
|
+
currentViewport.$tooltip.hide();
|
|
33530
|
+
}
|
|
33562
33531
|
this.$tooltip.show();
|
|
33563
33532
|
currentViewport = this;
|
|
33564
33533
|
} else {
|
|
@@ -33585,7 +33554,9 @@ class RulerViewport extends TrackViewport {
|
|
|
33585
33554
|
|
|
33586
33555
|
// hide tooltip when movement stops
|
|
33587
33556
|
clearTimeout(timer);
|
|
33588
|
-
timer = setTimeout(() =>
|
|
33557
|
+
timer = setTimeout(() => {
|
|
33558
|
+
if (this.$tooltip) this.$tooltip.hide();
|
|
33559
|
+
}, toolTipTimeout);
|
|
33589
33560
|
|
|
33590
33561
|
}
|
|
33591
33562
|
|
|
@@ -33604,69 +33575,6 @@ class RulerViewport extends TrackViewport {
|
|
|
33604
33575
|
|
|
33605
33576
|
}
|
|
33606
33577
|
|
|
33607
|
-
const viewportColumnManager =
|
|
33608
|
-
{
|
|
33609
|
-
createColumns: (columnContainer, count) => {
|
|
33610
|
-
|
|
33611
|
-
for (let i = 0; i < count; i++) {
|
|
33612
|
-
if (0 === i) {
|
|
33613
|
-
createColumn(columnContainer, 'igv-column');
|
|
33614
|
-
} else {
|
|
33615
|
-
columnContainer.appendChild(div$1({class: 'igv-column-shim'}));
|
|
33616
|
-
createColumn(columnContainer, 'igv-column');
|
|
33617
|
-
}
|
|
33618
|
-
}
|
|
33619
|
-
|
|
33620
|
-
},
|
|
33621
|
-
|
|
33622
|
-
removeColumnAtIndex: (i, column) => {
|
|
33623
|
-
const shim = 0 === i ? column.nextElementSibling : column.previousElementSibling;
|
|
33624
|
-
column.remove();
|
|
33625
|
-
shim.remove();
|
|
33626
|
-
},
|
|
33627
|
-
|
|
33628
|
-
insertAfter: referenceElement => {
|
|
33629
|
-
|
|
33630
|
-
const shim = div$1({class: 'igv-column-shim'});
|
|
33631
|
-
insertElementAfter(shim, referenceElement);
|
|
33632
|
-
|
|
33633
|
-
const column = div$1({class: 'igv-column'});
|
|
33634
|
-
insertElementAfter(column, shim);
|
|
33635
|
-
|
|
33636
|
-
return column
|
|
33637
|
-
},
|
|
33638
|
-
|
|
33639
|
-
insertBefore: (referenceElement, count) => {
|
|
33640
|
-
|
|
33641
|
-
for (let i = 0; i < count; i++) {
|
|
33642
|
-
|
|
33643
|
-
const column = div$1({class: 'igv-column'});
|
|
33644
|
-
insertElementBefore(column, referenceElement);
|
|
33645
|
-
|
|
33646
|
-
if (count > 1 && i > 0) {
|
|
33647
|
-
const columnShim = div$1({class: 'igv-column-shim'});
|
|
33648
|
-
insertElementBefore(columnShim, column);
|
|
33649
|
-
}
|
|
33650
|
-
|
|
33651
|
-
}
|
|
33652
|
-
|
|
33653
|
-
},
|
|
33654
|
-
|
|
33655
|
-
indexOfColumn: (columnContainer, column) => {
|
|
33656
|
-
|
|
33657
|
-
const allColumns = columnContainer.querySelectorAll('.igv-column');
|
|
33658
|
-
|
|
33659
|
-
for (let i = 0; i < allColumns.length; i++) {
|
|
33660
|
-
const c = allColumns[ i ];
|
|
33661
|
-
if (c === column) {
|
|
33662
|
-
return i
|
|
33663
|
-
}
|
|
33664
|
-
}
|
|
33665
|
-
|
|
33666
|
-
return undefined
|
|
33667
|
-
},
|
|
33668
|
-
};
|
|
33669
|
-
|
|
33670
33578
|
/*
|
|
33671
33579
|
* The MIT License (MIT)
|
|
33672
33580
|
*
|
|
@@ -33700,61 +33608,28 @@ class IdeogramViewport extends TrackViewport {
|
|
|
33700
33608
|
|
|
33701
33609
|
initializationHelper() {
|
|
33702
33610
|
|
|
33703
|
-
this
|
|
33704
|
-
this
|
|
33705
|
-
|
|
33706
|
-
|
|
33707
|
-
this.ideogram_ctx = canvas.getContext('2d');
|
|
33708
|
-
|
|
33709
|
-
this.$canvas.remove();
|
|
33710
|
-
this.canvas = undefined;
|
|
33711
|
-
this.ctx = undefined;
|
|
33611
|
+
this.canvas = document.createElement('canvas');
|
|
33612
|
+
this.canvas.className = 'igv-ideogram-canvas';
|
|
33613
|
+
this.$content.append($$1(this.canvas));
|
|
33614
|
+
this.ideogram_ctx = this.canvas.getContext('2d');
|
|
33712
33615
|
|
|
33713
33616
|
this.addMouseHandlers();
|
|
33714
|
-
|
|
33715
33617
|
}
|
|
33716
33618
|
|
|
33717
33619
|
addMouseHandlers() {
|
|
33718
|
-
this.addBrowserObserver();
|
|
33719
33620
|
this.addViewportClickHandler(this.$viewport.get(0));
|
|
33720
33621
|
}
|
|
33721
33622
|
|
|
33722
|
-
removeMouseHandlers() {
|
|
33723
|
-
this.removeBrowserObserver();
|
|
33724
|
-
this.removeViewportClickHandler(this.$viewport.get(0));
|
|
33725
|
-
|
|
33726
|
-
}
|
|
33727
|
-
|
|
33728
|
-
addBrowserObserver() {
|
|
33729
|
-
|
|
33730
|
-
function observerHandler(referenceFrameList) {
|
|
33731
|
-
const column = this.$viewport.get(0).parentElement;
|
|
33732
|
-
if (null !== column) {
|
|
33733
|
-
const index = viewportColumnManager.indexOfColumn(this.browser.columnContainer, column);
|
|
33734
|
-
// console.log(`ideogram-viewport - locus-change-handler index(${ index }) ${ referenceFrameList[ index ].getLocusString() } ${ Date.now() } `)
|
|
33735
|
-
this.update(this.ideogram_ctx, this.$viewport.width(), this.$viewport.height(), referenceFrameList[ index ]);
|
|
33736
|
-
}
|
|
33737
|
-
}
|
|
33738
|
-
|
|
33739
|
-
this.boundObserverHandler = observerHandler.bind(this);
|
|
33740
|
-
this.browser.on('locuschange', this.boundObserverHandler);
|
|
33741
|
-
}
|
|
33742
|
-
|
|
33743
|
-
removeBrowserObserver() {
|
|
33744
|
-
this.browser.off('locuschange', this.boundObserverHandler);
|
|
33745
|
-
}
|
|
33746
|
-
|
|
33747
33623
|
addViewportClickHandler(viewport) {
|
|
33748
33624
|
|
|
33749
|
-
|
|
33625
|
+
this.boundClickHandler = clickHandler.bind(this);
|
|
33626
|
+
viewport.addEventListener('click', this.boundClickHandler);
|
|
33750
33627
|
|
|
33751
|
-
|
|
33752
|
-
const index = viewportColumnManager.indexOfColumn(this.browser.columnContainer, column);
|
|
33753
|
-
const referenceFrame = this.browser.referenceFrameList[ index ];
|
|
33628
|
+
function clickHandler(event) {
|
|
33754
33629
|
|
|
33755
33630
|
const {xNormalized, width} = translateMouseCoordinates$1(event, this.ideogram_ctx.canvas);
|
|
33756
|
-
const {bpLength} = this.browser.genome.getChromosome(referenceFrame.chr);
|
|
33757
|
-
const locusLength = referenceFrame.bpPerPixel * width;
|
|
33631
|
+
const {bpLength} = this.browser.genome.getChromosome(this.referenceFrame.chr);
|
|
33632
|
+
const locusLength = this.referenceFrame.bpPerPixel * width;
|
|
33758
33633
|
const chrCoveragePercentage = locusLength / bpLength;
|
|
33759
33634
|
|
|
33760
33635
|
let xPercentage = xNormalized;
|
|
@@ -33769,21 +33644,14 @@ class IdeogramViewport extends TrackViewport {
|
|
|
33769
33644
|
const ss = Math.round((xPercentage - (chrCoveragePercentage / 2.0)) * bpLength);
|
|
33770
33645
|
const ee = Math.round((xPercentage + (chrCoveragePercentage / 2.0)) * bpLength);
|
|
33771
33646
|
|
|
33772
|
-
referenceFrame.start = ss;
|
|
33773
|
-
referenceFrame.end = ee;
|
|
33774
|
-
referenceFrame.bpPerPixel = (ee - ss) / width;
|
|
33647
|
+
this.referenceFrame.start = ss;
|
|
33648
|
+
this.referenceFrame.end = ee;
|
|
33649
|
+
this.referenceFrame.bpPerPixel = (ee - ss) / width;
|
|
33775
33650
|
|
|
33776
|
-
this.browser.updateViews(referenceFrame, this.browser.trackViews, true);
|
|
33651
|
+
this.browser.updateViews(this.referenceFrame, this.browser.trackViews, true);
|
|
33777
33652
|
|
|
33778
33653
|
}
|
|
33779
33654
|
|
|
33780
|
-
this.boundClickHandler = clickHandler.bind(this);
|
|
33781
|
-
viewport.addEventListener('click', this.boundClickHandler);
|
|
33782
|
-
|
|
33783
|
-
}
|
|
33784
|
-
|
|
33785
|
-
removeViewportClickHandler(viewport) {
|
|
33786
|
-
viewport.removeEventListener('click', this.boundClickHandler);
|
|
33787
33655
|
}
|
|
33788
33656
|
|
|
33789
33657
|
setWidth(width) {
|
|
@@ -33804,10 +33672,20 @@ class IdeogramViewport extends TrackViewport {
|
|
|
33804
33672
|
context.restore();
|
|
33805
33673
|
}
|
|
33806
33674
|
|
|
33807
|
-
|
|
33808
|
-
this
|
|
33809
|
-
|
|
33810
|
-
|
|
33675
|
+
repaint() {
|
|
33676
|
+
this.draw({referenceFrame: this.referenceFrame});
|
|
33677
|
+
}
|
|
33678
|
+
|
|
33679
|
+
draw({referenceFrame}) {
|
|
33680
|
+
|
|
33681
|
+
IGVGraphics.configureHighDPICanvas(this.ideogram_ctx, this.$viewport.width(), this.$viewport.height());
|
|
33682
|
+
|
|
33683
|
+
this.trackView.track.draw({
|
|
33684
|
+
context: this.ideogram_ctx,
|
|
33685
|
+
referenceFrame,
|
|
33686
|
+
pixelWidth: this.$viewport.width(),
|
|
33687
|
+
pixelHeight: this.$viewport.height()
|
|
33688
|
+
});
|
|
33811
33689
|
}
|
|
33812
33690
|
|
|
33813
33691
|
startSpinner() {
|
|
@@ -33848,7 +33726,7 @@ function createViewport(trackView, column, referenceFrame, width) {
|
|
|
33848
33726
|
|
|
33849
33727
|
if ('ruler' === trackView.track.type) {
|
|
33850
33728
|
return new RulerViewport(trackView, column, referenceFrame, width)
|
|
33851
|
-
} else if ('ideogram' === trackView.track.
|
|
33729
|
+
} else if ('ideogram' === trackView.track.id) {
|
|
33852
33730
|
return new IdeogramViewport(trackView, column, referenceFrame, width)
|
|
33853
33731
|
} else {
|
|
33854
33732
|
return new TrackViewport(trackView, column, referenceFrame, width)
|
|
@@ -34371,15 +34249,10 @@ const colorPickerExclusionTypes = new Set(['ruler', 'sequence', 'ideogram']);
|
|
|
34371
34249
|
class TrackView {
|
|
34372
34250
|
|
|
34373
34251
|
constructor(browser, columnContainer, track) {
|
|
34374
|
-
|
|
34375
|
-
this.namespace = `trackview-${guid$2()}`;
|
|
34376
|
-
|
|
34377
34252
|
this.browser = browser;
|
|
34378
34253
|
this.track = track;
|
|
34379
34254
|
track.trackView = this;
|
|
34380
|
-
|
|
34381
34255
|
this.addDOMToColumnContainer(browser, columnContainer, browser.referenceFrameList);
|
|
34382
|
-
|
|
34383
34256
|
}
|
|
34384
34257
|
|
|
34385
34258
|
/**
|
|
@@ -34403,7 +34276,7 @@ class TrackView {
|
|
|
34403
34276
|
// Axis
|
|
34404
34277
|
this.axis = this.createAxis(browser, this.track);
|
|
34405
34278
|
|
|
34406
|
-
//
|
|
34279
|
+
// Create a viewport for each reference frame
|
|
34407
34280
|
this.viewports = [];
|
|
34408
34281
|
const viewportWidth = browser.calculateViewportWidth(referenceFrameList.length);
|
|
34409
34282
|
const viewportColumns = columnContainer.querySelectorAll('.igv-column');
|
|
@@ -34476,7 +34349,6 @@ class TrackView {
|
|
|
34476
34349
|
|
|
34477
34350
|
// Track Viewports
|
|
34478
34351
|
for (let viewport of this.viewports) {
|
|
34479
|
-
viewport.removeMouseHandlers();
|
|
34480
34352
|
viewport.$viewport.remove();
|
|
34481
34353
|
}
|
|
34482
34354
|
|
|
@@ -34542,19 +34414,14 @@ class TrackView {
|
|
|
34542
34414
|
if (false === colorPickerExclusionTypes.has(this.track.type)) {
|
|
34543
34415
|
|
|
34544
34416
|
const trackColors = [];
|
|
34545
|
-
|
|
34546
34417
|
const color = this.track.color || this.track.defaultColor;
|
|
34547
|
-
|
|
34548
34418
|
if (isString$3(color)) {
|
|
34549
34419
|
trackColors.push(color);
|
|
34550
34420
|
}
|
|
34551
|
-
|
|
34552
34421
|
if (this.track.altColor && isString$3(this.track.altColor)) {
|
|
34553
34422
|
trackColors.push(this.track.altColor);
|
|
34554
34423
|
}
|
|
34555
|
-
|
|
34556
34424
|
const defaultColors = trackColors.map(c => c.startsWith("#") ? c : c.startsWith("rgb(") ? IGVColor.rgbToHex(c) : IGVColor.colorNameToHex(c));
|
|
34557
|
-
|
|
34558
34425
|
const colorHandlers =
|
|
34559
34426
|
{
|
|
34560
34427
|
color: color => {
|
|
@@ -34567,7 +34434,6 @@ class TrackView {
|
|
|
34567
34434
|
}
|
|
34568
34435
|
|
|
34569
34436
|
};
|
|
34570
|
-
|
|
34571
34437
|
this.browser.genericColorPicker.configure(defaultColors, colorHandlers);
|
|
34572
34438
|
this.browser.genericColorPicker.setActiveColorHandler(key);
|
|
34573
34439
|
this.browser.genericColorPicker.show();
|
|
@@ -34581,7 +34447,6 @@ class TrackView {
|
|
|
34581
34447
|
if (this.track.minHeight) {
|
|
34582
34448
|
newHeight = Math.max(this.track.minHeight, newHeight);
|
|
34583
34449
|
}
|
|
34584
|
-
|
|
34585
34450
|
if (this.track.maxHeight) {
|
|
34586
34451
|
newHeight = Math.min(this.track.maxHeight, newHeight);
|
|
34587
34452
|
}
|
|
@@ -34601,9 +34466,8 @@ class TrackView {
|
|
|
34601
34466
|
|
|
34602
34467
|
this.sampleNameViewport.viewport.style.height = `${newHeight}px`;
|
|
34603
34468
|
|
|
34604
|
-
// If the track does not manage its own content height set it here
|
|
34469
|
+
// If the track does not manage its own content height set it equal to the viewport height here
|
|
34605
34470
|
if (typeof this.track.computePixelHeight !== "function") {
|
|
34606
|
-
|
|
34607
34471
|
for (let vp of this.viewports) {
|
|
34608
34472
|
vp.setContentHeight(newHeight);
|
|
34609
34473
|
}
|
|
@@ -34660,15 +34524,6 @@ class TrackView {
|
|
|
34660
34524
|
}
|
|
34661
34525
|
}
|
|
34662
34526
|
|
|
34663
|
-
resize(viewportWidth) {
|
|
34664
|
-
|
|
34665
|
-
for (let viewport of this.viewports) {
|
|
34666
|
-
viewport.setWidth(viewportWidth);
|
|
34667
|
-
}
|
|
34668
|
-
|
|
34669
|
-
this.updateViews(true);
|
|
34670
|
-
}
|
|
34671
|
-
|
|
34672
34527
|
/**
|
|
34673
34528
|
* Repaint all viewports without loading any new data. Use this for events that change visual aspect of data,
|
|
34674
34529
|
* e.g. color, sort order, etc, but do not change the genomic state.
|
|
@@ -34676,7 +34531,9 @@ class TrackView {
|
|
|
34676
34531
|
repaintViews() {
|
|
34677
34532
|
|
|
34678
34533
|
for (let viewport of this.viewports) {
|
|
34679
|
-
viewport.
|
|
34534
|
+
if (viewport.isVisible()) {
|
|
34535
|
+
viewport.repaint();
|
|
34536
|
+
}
|
|
34680
34537
|
}
|
|
34681
34538
|
|
|
34682
34539
|
if (typeof this.track.paintAxis === 'function') {
|
|
@@ -34685,7 +34542,6 @@ class TrackView {
|
|
|
34685
34542
|
|
|
34686
34543
|
// Repaint sample names last
|
|
34687
34544
|
this.repaintSamples();
|
|
34688
|
-
|
|
34689
34545
|
}
|
|
34690
34546
|
|
|
34691
34547
|
repaintSamples() {
|
|
@@ -34701,10 +34557,25 @@ class TrackView {
|
|
|
34701
34557
|
this.viewports.forEach(viewport => viewport.setTrackLabel(name));
|
|
34702
34558
|
}
|
|
34703
34559
|
|
|
34560
|
+
/**
|
|
34561
|
+
* Called in response to a window resize event, change in # of multilocus panels, or other event that changes
|
|
34562
|
+
* the width of the track view.
|
|
34563
|
+
*
|
|
34564
|
+
* @param viewportWidth The width of each viewport in this track view.
|
|
34565
|
+
*/
|
|
34566
|
+
resize(viewportWidth) {
|
|
34567
|
+
for (let viewport of this.viewports) {
|
|
34568
|
+
viewport.setWidth(viewportWidth);
|
|
34569
|
+
}
|
|
34570
|
+
}
|
|
34571
|
+
|
|
34704
34572
|
/**
|
|
34705
34573
|
* Update viewports to reflect current genomic state, possibly loading additional data.
|
|
34574
|
+
*
|
|
34575
|
+
* @param force - if true, force a repaint even if no new data is loaded
|
|
34576
|
+
* @returns {Promise<void>}
|
|
34706
34577
|
*/
|
|
34707
|
-
async updateViews(
|
|
34578
|
+
async updateViews() {
|
|
34708
34579
|
|
|
34709
34580
|
if (!(this.browser && this.browser.referenceFrameList)) return
|
|
34710
34581
|
|
|
@@ -34713,31 +34584,38 @@ class TrackView {
|
|
|
34713
34584
|
// Shift viewports left/right to current genomic state (pans canvas)
|
|
34714
34585
|
visibleViewports.forEach(viewport => viewport.shift());
|
|
34715
34586
|
|
|
34716
|
-
|
|
34717
|
-
|
|
34718
|
-
if (isDragging) {
|
|
34587
|
+
// If dragging (panning) return
|
|
34588
|
+
if (this.browser.dragObject) {
|
|
34719
34589
|
return
|
|
34720
34590
|
}
|
|
34721
34591
|
|
|
34722
|
-
//
|
|
34723
|
-
|
|
34592
|
+
// Get viewports to repaint
|
|
34593
|
+
let viewportsToRepaint = (this.track.autoscale || this.track.autoscaleGroup || this.track.type === 'ruler') ?
|
|
34594
|
+
visibleViewports :
|
|
34595
|
+
visibleViewports.filter(vp => vp.needsRepaint());
|
|
34596
|
+
|
|
34597
|
+
// Filter zoomed out views. This has the side effect or turning off or no the zoomed out notice
|
|
34598
|
+
viewportsToRepaint = viewportsToRepaint.filter(viewport => viewport.checkZoomIn());
|
|
34599
|
+
|
|
34600
|
+
// Get viewports that require a data load
|
|
34601
|
+
const viewportsToReload = viewportsToRepaint.filter(viewport => viewport.needsReload());
|
|
34724
34602
|
|
|
34725
34603
|
// Trigger viewport to load features needed to cover current genomic range
|
|
34726
34604
|
// NOTE: these must be loaded synchronously, do not user Promise.all, not all file readers are thread safe
|
|
34727
|
-
for (let viewport of
|
|
34605
|
+
for (let viewport of viewportsToReload) {
|
|
34728
34606
|
await viewport.loadFeatures();
|
|
34729
34607
|
}
|
|
34730
|
-
|
|
34608
|
+
|
|
34731
34609
|
if (this.disposed) return // Track was removed during load
|
|
34732
34610
|
|
|
34733
|
-
//
|
|
34611
|
+
// Special case for variant tracks in multilocus view. The # of rows to allocate to the variant (site)
|
|
34734
34612
|
// section depends on data from all the views. We only need to adjust this however if any data was loaded
|
|
34735
34613
|
// (i.e. reloadableViewports.length > 0)
|
|
34736
|
-
if (this.track && typeof this.track.variantRowCount === 'function' &&
|
|
34614
|
+
if (this.track && typeof this.track.variantRowCount === 'function' && viewportsToReload.length > 0) {
|
|
34737
34615
|
let maxRow = 0;
|
|
34738
34616
|
for (let viewport of this.viewports) {
|
|
34739
|
-
if (viewport.
|
|
34740
|
-
maxRow = Math.max(maxRow, viewport.
|
|
34617
|
+
if (viewport.featureCache && viewport.featureCache.features) {
|
|
34618
|
+
maxRow = Math.max(maxRow, viewport.featureCache.features.reduce((a, f) => Math.max(a, f.row || 0), 0));
|
|
34741
34619
|
}
|
|
34742
34620
|
}
|
|
34743
34621
|
const current = this.track.nVariantRows;
|
|
@@ -34749,19 +34627,18 @@ class TrackView {
|
|
|
34749
34627
|
}
|
|
34750
34628
|
}
|
|
34751
34629
|
|
|
34752
|
-
|
|
34753
34630
|
if (this.track.autoscale) {
|
|
34754
34631
|
let allFeatures = [];
|
|
34755
34632
|
for (let visibleViewport of visibleViewports) {
|
|
34756
34633
|
const referenceFrame = visibleViewport.referenceFrame;
|
|
34757
34634
|
const start = referenceFrame.start;
|
|
34758
34635
|
const end = start + referenceFrame.toBP($$1(visibleViewport.contentDiv).width());
|
|
34759
|
-
if (visibleViewport.
|
|
34760
|
-
if (typeof visibleViewport.
|
|
34761
|
-
const max = visibleViewport.
|
|
34636
|
+
if (visibleViewport.featureCache && visibleViewport.featureCache.features) {
|
|
34637
|
+
if (typeof visibleViewport.featureCache.features.getMax === 'function') {
|
|
34638
|
+
const max = visibleViewport.featureCache.features.getMax(start, end);
|
|
34762
34639
|
allFeatures.push({value: max});
|
|
34763
34640
|
} else {
|
|
34764
|
-
allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(visibleViewport.
|
|
34641
|
+
allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(visibleViewport.featureCache.features, start, end));
|
|
34765
34642
|
}
|
|
34766
34643
|
}
|
|
34767
34644
|
}
|
|
@@ -34772,15 +34649,8 @@ class TrackView {
|
|
|
34772
34649
|
}
|
|
34773
34650
|
}
|
|
34774
34651
|
|
|
34775
|
-
|
|
34776
|
-
|
|
34777
|
-
for (let visibleViewport of visibleViewports) {
|
|
34778
|
-
visibleViewport.repaint();
|
|
34779
|
-
}
|
|
34780
|
-
} else {
|
|
34781
|
-
for (let vp of reloadableViewports) {
|
|
34782
|
-
vp.repaint();
|
|
34783
|
-
}
|
|
34652
|
+
for (let vp of viewportsToRepaint) {
|
|
34653
|
+
vp.repaint();
|
|
34784
34654
|
}
|
|
34785
34655
|
|
|
34786
34656
|
this.adjustTrackHeight();
|
|
@@ -34808,34 +34678,29 @@ class TrackView {
|
|
|
34808
34678
|
}
|
|
34809
34679
|
|
|
34810
34680
|
/**
|
|
34811
|
-
* Return a promise to get all in-view features. Used for group autoscaling.
|
|
34681
|
+
* Return a promise to get all in-view features across all viewports. Used for group autoscaling.
|
|
34812
34682
|
*/
|
|
34813
|
-
async getInViewFeatures(
|
|
34683
|
+
async getInViewFeatures() {
|
|
34814
34684
|
|
|
34815
34685
|
if (!(this.browser && this.browser.referenceFrameList)) {
|
|
34816
34686
|
return []
|
|
34817
34687
|
}
|
|
34818
34688
|
|
|
34819
|
-
// List of viewports that need reloading
|
|
34820
|
-
const rpV = this.viewportsToReload(force);
|
|
34821
|
-
const promises = rpV.map(function (vp) {
|
|
34822
|
-
return vp.loadFeatures()
|
|
34823
|
-
});
|
|
34824
|
-
|
|
34825
|
-
await Promise.all(promises);
|
|
34826
|
-
|
|
34827
34689
|
let allFeatures = [];
|
|
34828
34690
|
for (let vp of this.viewports) {
|
|
34829
|
-
if (vp.
|
|
34691
|
+
if (vp.needsReload()) {
|
|
34692
|
+
await vp.loadFeatures();
|
|
34693
|
+
}
|
|
34694
|
+
if (vp.featureCache && vp.featureCache.features) {
|
|
34830
34695
|
const referenceFrame = vp.referenceFrame;
|
|
34831
34696
|
const start = referenceFrame.start;
|
|
34832
34697
|
const end = start + referenceFrame.toBP($$1(vp.contentDiv).width());
|
|
34833
34698
|
|
|
34834
|
-
if (typeof vp.
|
|
34835
|
-
const max = vp.
|
|
34699
|
+
if (typeof vp.featureCache.features.getMax === 'function') {
|
|
34700
|
+
const max = vp.featureCache.features.getMax(start, end);
|
|
34836
34701
|
allFeatures.push({value: max});
|
|
34837
34702
|
} else {
|
|
34838
|
-
allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(vp.
|
|
34703
|
+
allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(vp.featureCache.features, start, end));
|
|
34839
34704
|
}
|
|
34840
34705
|
}
|
|
34841
34706
|
}
|
|
@@ -34876,27 +34741,6 @@ class TrackView {
|
|
|
34876
34741
|
}
|
|
34877
34742
|
}
|
|
34878
34743
|
|
|
34879
|
-
viewportsToReload(force) {
|
|
34880
|
-
|
|
34881
|
-
// List of viewports that need reloading
|
|
34882
|
-
const viewports = this.viewports.filter(viewport => {
|
|
34883
|
-
if (!viewport.isVisible()) {
|
|
34884
|
-
return false
|
|
34885
|
-
}
|
|
34886
|
-
if (!viewport.checkZoomIn()) {
|
|
34887
|
-
return false
|
|
34888
|
-
} else {
|
|
34889
|
-
const referenceFrame = viewport.referenceFrame;
|
|
34890
|
-
const chr = viewport.referenceFrame.chr;
|
|
34891
|
-
const start = referenceFrame.start;
|
|
34892
|
-
const end = start + referenceFrame.toBP($$1(viewport.contentDiv).width());
|
|
34893
|
-
const bpPerPixel = referenceFrame.bpPerPixel;
|
|
34894
|
-
return force || (!viewport.tile || viewport.tile.invalidate || !viewport.tile.containsRange(chr, start, end, bpPerPixel))
|
|
34895
|
-
}
|
|
34896
|
-
});
|
|
34897
|
-
return viewports
|
|
34898
|
-
}
|
|
34899
|
-
|
|
34900
34744
|
createTrackScrollbar(browser) {
|
|
34901
34745
|
|
|
34902
34746
|
const outerScroll = div$1();
|
|
@@ -35009,7 +34853,7 @@ class TrackView {
|
|
|
35009
34853
|
|
|
35010
34854
|
addTrackDragMouseHandlers(browser) {
|
|
35011
34855
|
|
|
35012
|
-
if ('ideogram' === this.track.
|
|
34856
|
+
if ('ideogram' === this.track.id || 'ruler' === this.track.id) ; else {
|
|
35013
34857
|
|
|
35014
34858
|
let currentDragHandle = undefined;
|
|
35015
34859
|
|
|
@@ -35086,7 +34930,7 @@ class TrackView {
|
|
|
35086
34930
|
|
|
35087
34931
|
removeTrackDragMouseHandlers() {
|
|
35088
34932
|
|
|
35089
|
-
if ('ideogram' === this.track.
|
|
34933
|
+
if ('ideogram' === this.track.id || 'ruler' === this.track.id) ; else {
|
|
35090
34934
|
this.dragHandle.removeEventListener('mousedown', this.boundTrackDragMouseDownHandler);
|
|
35091
34935
|
document.removeEventListener('mouseup', this.boundDocumentTrackDragMouseUpHandler);
|
|
35092
34936
|
this.dragHandle.removeEventListener('mouseup', this.boundTrackDragMouseEnterHandler);
|
|
@@ -39674,7 +39518,7 @@ class TextFeatureSource {
|
|
|
39674
39518
|
mapProperties(features, config.mappings);
|
|
39675
39519
|
}
|
|
39676
39520
|
this.queryable = false;
|
|
39677
|
-
this.featureCache = new FeatureCache(features, genome);
|
|
39521
|
+
this.featureCache = new FeatureCache$1(features, genome);
|
|
39678
39522
|
} else if (config.reader) {
|
|
39679
39523
|
// Explicit reader implementation
|
|
39680
39524
|
this.reader = config.reader;
|
|
@@ -39841,14 +39685,14 @@ class TextFeatureSource {
|
|
|
39841
39685
|
}
|
|
39842
39686
|
|
|
39843
39687
|
// Note - replacing previous cache with new one. genomicInterval is optional (might be undefined => includes all features)
|
|
39844
|
-
this.featureCache = new FeatureCache(features, this.genome, genomicInterval);
|
|
39688
|
+
this.featureCache = new FeatureCache$1(features, this.genome, genomicInterval);
|
|
39845
39689
|
|
|
39846
39690
|
// If track is marked "searchable"< cache features by name -- use this with caution, memory intensive
|
|
39847
39691
|
if (this.config.searchable || this.config.searchableFields) {
|
|
39848
39692
|
this.addFeaturesToDB(features);
|
|
39849
39693
|
}
|
|
39850
39694
|
} else {
|
|
39851
|
-
this.featureCache = new FeatureCache([], genomicInterval); // Empty cache
|
|
39695
|
+
this.featureCache = new FeatureCache$1([], genomicInterval); // Empty cache
|
|
39852
39696
|
}
|
|
39853
39697
|
}
|
|
39854
39698
|
|
|
@@ -41592,7 +41436,7 @@ class TDFSource {
|
|
|
41592
41436
|
return features
|
|
41593
41437
|
}
|
|
41594
41438
|
|
|
41595
|
-
supportsWholeGenome() {
|
|
41439
|
+
get supportsWholeGenome() {
|
|
41596
41440
|
return true
|
|
41597
41441
|
}
|
|
41598
41442
|
}
|
|
@@ -41713,38 +41557,6 @@ function FeatureSource(config, genome) {
|
|
|
41713
41557
|
}
|
|
41714
41558
|
}
|
|
41715
41559
|
|
|
41716
|
-
const pairs =
|
|
41717
|
-
[
|
|
41718
|
-
['A', 'T'],
|
|
41719
|
-
['G', 'C'],
|
|
41720
|
-
['Y', 'R'],
|
|
41721
|
-
['W', 'S'],
|
|
41722
|
-
['K', 'M'],
|
|
41723
|
-
['D', 'H'],
|
|
41724
|
-
['B', 'V']
|
|
41725
|
-
];
|
|
41726
|
-
|
|
41727
|
-
const complements = new Map();
|
|
41728
|
-
for (let p of pairs) {
|
|
41729
|
-
const p1 = p[0];
|
|
41730
|
-
const p2 = p[1];
|
|
41731
|
-
complements.set(p1, p2);
|
|
41732
|
-
complements.set(p2, p1);
|
|
41733
|
-
complements.set(p1.toLowerCase(), p2.toLowerCase());
|
|
41734
|
-
complements.set(p2.toLowerCase(), p1.toLowerCase());
|
|
41735
|
-
}
|
|
41736
|
-
|
|
41737
|
-
function reverseComplementSequence(sequence) {
|
|
41738
|
-
|
|
41739
|
-
let comp = '';
|
|
41740
|
-
let idx = sequence.length;
|
|
41741
|
-
while (idx-- > 0) {
|
|
41742
|
-
const base = sequence[idx];
|
|
41743
|
-
comp += complements.has(base) ? complements.get(base) : base;
|
|
41744
|
-
}
|
|
41745
|
-
return comp
|
|
41746
|
-
}
|
|
41747
|
-
|
|
41748
41560
|
const GtexUtils = {
|
|
41749
41561
|
|
|
41750
41562
|
getTissueInfo: function (datasetId, baseURL) {
|
|
@@ -42288,7 +42100,7 @@ class FeatureTrack extends TrackBase {
|
|
|
42288
42100
|
|
|
42289
42101
|
}
|
|
42290
42102
|
|
|
42291
|
-
supportsWholeGenome() {
|
|
42103
|
+
get supportsWholeGenome() {
|
|
42292
42104
|
return (this.config.indexed === false || !this.config.indexURL) && this.config.supportsWholeGenome !== false
|
|
42293
42105
|
}
|
|
42294
42106
|
|
|
@@ -42437,21 +42249,18 @@ class FeatureTrack extends TrackBase {
|
|
|
42437
42249
|
const infoURL = this.infoURL || this.config.infoURL;
|
|
42438
42250
|
for (let fd of featureData) {
|
|
42439
42251
|
data.push(fd);
|
|
42440
|
-
if (infoURL
|
|
42441
|
-
|
|
42442
|
-
|
|
42443
|
-
|
|
42444
|
-
|
|
42445
|
-
|
|
42446
|
-
|
|
42447
|
-
|
|
42448
|
-
const url = this.infoURL || this.config.infoURL;
|
|
42449
|
-
const href = url.replace("$$", feature.name);
|
|
42450
|
-
data.push({name: "Info", value: `<a target="_blank" href=${href}>${fd.value}</a>`});
|
|
42451
|
-
}
|
|
42252
|
+
if (infoURL &&
|
|
42253
|
+
fd.name &&
|
|
42254
|
+
fd.name.toLowerCase() === "name" &&
|
|
42255
|
+
fd.value &&
|
|
42256
|
+
isString$3(fd.value) &&
|
|
42257
|
+
!fd.value.startsWith("<")) {
|
|
42258
|
+
const href = infoURL.replace("$$", feature.name);
|
|
42259
|
+
fd.value = `<a target=_blank href=${href}>${fd.value}</a>`;
|
|
42452
42260
|
}
|
|
42453
42261
|
}
|
|
42454
42262
|
|
|
42263
|
+
|
|
42455
42264
|
//Array.prototype.push.apply(data, featureData);
|
|
42456
42265
|
|
|
42457
42266
|
// If we have clicked over an exon number it.
|
|
@@ -42480,7 +42289,7 @@ class FeatureTrack extends TrackBase {
|
|
|
42480
42289
|
}
|
|
42481
42290
|
|
|
42482
42291
|
menuItemList() {
|
|
42483
|
-
|
|
42292
|
+
|
|
42484
42293
|
const menuItems = [];
|
|
42485
42294
|
|
|
42486
42295
|
if (this.render === renderSnp) {
|
|
@@ -42508,7 +42317,7 @@ class FeatureTrack extends TrackBase {
|
|
|
42508
42317
|
menuItems.push(
|
|
42509
42318
|
{
|
|
42510
42319
|
object: $$1(createCheckbox$1(lut[displayMode], displayMode === this.displayMode)),
|
|
42511
|
-
click:
|
|
42320
|
+
click: () => {
|
|
42512
42321
|
this.displayMode = displayMode;
|
|
42513
42322
|
this.config.displayMode = displayMode;
|
|
42514
42323
|
this.trackView.checkContentHeight();
|
|
@@ -42524,14 +42333,28 @@ class FeatureTrack extends TrackBase {
|
|
|
42524
42333
|
|
|
42525
42334
|
contextMenuItemList(clickState) {
|
|
42526
42335
|
|
|
42527
|
-
|
|
42528
|
-
|
|
42529
|
-
|
|
42530
|
-
|
|
42531
|
-
|
|
42532
|
-
|
|
42533
|
-
|
|
42534
|
-
|
|
42336
|
+
const features = this.clickedFeatures(clickState);
|
|
42337
|
+
if (features.length > 1) {
|
|
42338
|
+
features.sort((a, b) => (b.end - b.start) - (a.end - a.start));
|
|
42339
|
+
}
|
|
42340
|
+
const f = features[0]; // The shortest clicked feature
|
|
42341
|
+
|
|
42342
|
+
if ((f.end - f.start) <= 1000000) {
|
|
42343
|
+
const list = [{
|
|
42344
|
+
label: 'View feature sequence',
|
|
42345
|
+
click: async () => {
|
|
42346
|
+
let seq = await this.browser.genome.getSequence(f.chr, f.start, f.end);
|
|
42347
|
+
if (f.strand === '-') {
|
|
42348
|
+
seq = reverseComplementSequence(seq);
|
|
42349
|
+
}
|
|
42350
|
+
if (!seq) seq = "Unknown sequence";
|
|
42351
|
+
Alert.presentAlert(seq);
|
|
42352
|
+
|
|
42353
|
+
}
|
|
42354
|
+
}];
|
|
42355
|
+
|
|
42356
|
+
if (isSecureContext() && navigator.clipboard !== undefined) {
|
|
42357
|
+
list.push(
|
|
42535
42358
|
{
|
|
42536
42359
|
label: 'Copy feature sequence',
|
|
42537
42360
|
click: async () => {
|
|
@@ -42539,17 +42362,23 @@ class FeatureTrack extends TrackBase {
|
|
|
42539
42362
|
if (f.strand === '-') {
|
|
42540
42363
|
seq = reverseComplementSequence(seq);
|
|
42541
42364
|
}
|
|
42542
|
-
|
|
42365
|
+
try {
|
|
42366
|
+
await navigator.clipboard.writeText(seq);
|
|
42367
|
+
} catch (e) {
|
|
42368
|
+
console.error(e);
|
|
42369
|
+
Alert.presentAlert(`error copying sequence to clipboard ${e}`);
|
|
42370
|
+
}
|
|
42543
42371
|
}
|
|
42544
|
-
}
|
|
42545
|
-
|
|
42546
|
-
]
|
|
42372
|
+
}
|
|
42373
|
+
);
|
|
42547
42374
|
}
|
|
42548
|
-
|
|
42375
|
+
list.push('<hr/>');
|
|
42376
|
+
return list
|
|
42377
|
+
} else {
|
|
42549
42378
|
|
|
42550
|
-
|
|
42551
|
-
return undefined
|
|
42379
|
+
return undefined
|
|
42552
42380
|
|
|
42381
|
+
}
|
|
42553
42382
|
}
|
|
42554
42383
|
|
|
42555
42384
|
description() {
|
|
@@ -42570,7 +42399,7 @@ class FeatureTrack extends TrackBase {
|
|
|
42570
42399
|
desc += "</html>";
|
|
42571
42400
|
return desc
|
|
42572
42401
|
} else {
|
|
42573
|
-
return super.description()
|
|
42402
|
+
return super.description()
|
|
42574
42403
|
}
|
|
42575
42404
|
|
|
42576
42405
|
};
|
|
@@ -42651,13 +42480,11 @@ class WigTrack extends TrackBase {
|
|
|
42651
42480
|
this.paintAxis = paintAxis;
|
|
42652
42481
|
|
|
42653
42482
|
const format = config.format ? config.format.toLowerCase() : config.format;
|
|
42483
|
+
this.flipAxis = config.flipAxis ? config.flipAxis : false;
|
|
42484
|
+
this.logScale = config.logScale ? config.logScale : false;
|
|
42654
42485
|
if ("bigwig" === format) {
|
|
42655
|
-
this.flipAxis = config.flipAxis ? config.flipAxis : false;
|
|
42656
|
-
this.logScale = config.logScale ? config.logScale : false;
|
|
42657
42486
|
this.featureSource = new BWSource(config, this.browser.genome);
|
|
42658
42487
|
} else if ("tdf" === format) {
|
|
42659
|
-
this.flipAxis = config.flipAxis ? config.flipAxis : false;
|
|
42660
|
-
this.logScale = config.logScale ? config.logScale : false;
|
|
42661
42488
|
this.featureSource = new TDFSource(config, this.browser.genome);
|
|
42662
42489
|
} else {
|
|
42663
42490
|
this.featureSource = FeatureSource(config, this.browser.genome);
|
|
@@ -42709,7 +42536,7 @@ class WigTrack extends TrackBase {
|
|
|
42709
42536
|
let items = [];
|
|
42710
42537
|
if (this.flipAxis !== undefined) {
|
|
42711
42538
|
items.push({
|
|
42712
|
-
label:"Flip y-axis",
|
|
42539
|
+
label: "Flip y-axis",
|
|
42713
42540
|
click: () => {
|
|
42714
42541
|
this.flipAxis = !this.flipAxis;
|
|
42715
42542
|
this.trackView.repaintViews();
|
|
@@ -42893,7 +42720,7 @@ class WigTrack extends TrackBase {
|
|
|
42893
42720
|
}
|
|
42894
42721
|
}
|
|
42895
42722
|
|
|
42896
|
-
supportsWholeGenome() {
|
|
42723
|
+
get supportsWholeGenome() {
|
|
42897
42724
|
return !this.config.indexURL && this.config.supportsWholeGenome !== false
|
|
42898
42725
|
}
|
|
42899
42726
|
|
|
@@ -43437,7 +43264,7 @@ class SegTrack extends TrackBase {
|
|
|
43437
43264
|
|
|
43438
43265
|
const sortHandler = (sort) => {
|
|
43439
43266
|
const viewport = clickState.viewport;
|
|
43440
|
-
const features = viewport.
|
|
43267
|
+
const features = viewport.cachedFeatures;
|
|
43441
43268
|
this.sortSamples(sort.chr, sort.start, sort.end, sort.direction, features);
|
|
43442
43269
|
};
|
|
43443
43270
|
|
|
@@ -43465,7 +43292,7 @@ class SegTrack extends TrackBase {
|
|
|
43465
43292
|
|
|
43466
43293
|
}
|
|
43467
43294
|
|
|
43468
|
-
supportsWholeGenome() {
|
|
43295
|
+
get supportsWholeGenome() {
|
|
43469
43296
|
return (this.config.indexed === false || !this.config.indexURL) && this.config.supportsWholeGenome !== false
|
|
43470
43297
|
}
|
|
43471
43298
|
|
|
@@ -43555,10 +43382,16 @@ const MUT_COLORS = {
|
|
|
43555
43382
|
* THE SOFTWARE.
|
|
43556
43383
|
*/
|
|
43557
43384
|
|
|
43385
|
+
/**
|
|
43386
|
+
* Represents 2 or more wig tracks overlaid on a common viewport.
|
|
43387
|
+
*/
|
|
43558
43388
|
class MergedTrack extends TrackBase {
|
|
43559
43389
|
|
|
43560
43390
|
constructor(config, browser) {
|
|
43561
43391
|
super(config, browser);
|
|
43392
|
+
this.type = "merged";
|
|
43393
|
+
this.featureType = 'numeric';
|
|
43394
|
+
this.paintAxis = paintAxis;
|
|
43562
43395
|
}
|
|
43563
43396
|
|
|
43564
43397
|
init(config) {
|
|
@@ -43569,21 +43402,6 @@ class MergedTrack extends TrackBase {
|
|
|
43569
43402
|
super.init(config);
|
|
43570
43403
|
}
|
|
43571
43404
|
|
|
43572
|
-
get height() {
|
|
43573
|
-
return this._height
|
|
43574
|
-
}
|
|
43575
|
-
|
|
43576
|
-
set height(h) {
|
|
43577
|
-
this._height = h;
|
|
43578
|
-
if (this.tracks) {
|
|
43579
|
-
for (let t of this.tracks) {
|
|
43580
|
-
t.height = h;
|
|
43581
|
-
t.config.height = h;
|
|
43582
|
-
}
|
|
43583
|
-
}
|
|
43584
|
-
}
|
|
43585
|
-
|
|
43586
|
-
|
|
43587
43405
|
async postInit() {
|
|
43588
43406
|
|
|
43589
43407
|
this.tracks = [];
|
|
@@ -43603,11 +43421,55 @@ class MergedTrack extends TrackBase {
|
|
|
43603
43421
|
}
|
|
43604
43422
|
}
|
|
43605
43423
|
|
|
43606
|
-
this.
|
|
43424
|
+
this.flipAxis = this.config.flipAxis ? this.config.flipAxis : false;
|
|
43425
|
+
this.logScale = this.config.logScale ? this.config.logScale : false;
|
|
43426
|
+
this.autoscale = this.config.autoscale || this.config.max === undefined;
|
|
43427
|
+
if (!this.autoscale) {
|
|
43428
|
+
this.dataRange = {
|
|
43429
|
+
min: this.config.min || 0,
|
|
43430
|
+
max: this.config.max
|
|
43431
|
+
};
|
|
43432
|
+
}
|
|
43433
|
+
for (let t of this.tracks) {
|
|
43434
|
+
t.autoscale = false;
|
|
43435
|
+
t.dataRange = this.dataRange;
|
|
43436
|
+
}
|
|
43437
|
+
|
|
43438
|
+
this.height = this.config.height || 50;
|
|
43607
43439
|
|
|
43608
43440
|
return Promise.all(p)
|
|
43609
43441
|
}
|
|
43610
43442
|
|
|
43443
|
+
get height() {
|
|
43444
|
+
return this._height
|
|
43445
|
+
}
|
|
43446
|
+
|
|
43447
|
+
set height(h) {
|
|
43448
|
+
this._height = h;
|
|
43449
|
+
if (this.tracks) {
|
|
43450
|
+
for (let t of this.tracks) {
|
|
43451
|
+
t.height = h;
|
|
43452
|
+
t.config.height = h;
|
|
43453
|
+
}
|
|
43454
|
+
}
|
|
43455
|
+
}
|
|
43456
|
+
|
|
43457
|
+
menuItemList() {
|
|
43458
|
+
let items = [];
|
|
43459
|
+
if (this.flipAxis !== undefined) {
|
|
43460
|
+
items.push({
|
|
43461
|
+
label: "Flip y-axis",
|
|
43462
|
+
click: () => {
|
|
43463
|
+
this.flipAxis = !this.flipAxis;
|
|
43464
|
+
this.trackView.repaintViews();
|
|
43465
|
+
}
|
|
43466
|
+
});
|
|
43467
|
+
}
|
|
43468
|
+
|
|
43469
|
+
items = items.concat(MenuUtils.numericDataMenuItems(this.trackView));
|
|
43470
|
+
|
|
43471
|
+
return items
|
|
43472
|
+
}
|
|
43611
43473
|
|
|
43612
43474
|
async getFeatures(chr, bpStart, bpEnd, bpPerPixel) {
|
|
43613
43475
|
|
|
@@ -43617,44 +43479,26 @@ class MergedTrack extends TrackBase {
|
|
|
43617
43479
|
|
|
43618
43480
|
draw(options) {
|
|
43619
43481
|
|
|
43620
|
-
|
|
43621
|
-
|
|
43622
|
-
mergedFeatures = options.features; // Array of feature arrays, 1 for each track
|
|
43623
|
-
|
|
43624
|
-
dataRange = autoscale(options.referenceFrame.chr, mergedFeatures);
|
|
43482
|
+
const mergedFeatures = options.features; // Array of feature arrays, 1 for each track
|
|
43625
43483
|
|
|
43626
|
-
|
|
43627
|
-
|
|
43628
|
-
|
|
43484
|
+
if (this.autoscale) {
|
|
43485
|
+
this.dataRange = autoscale(options.referenceFrame.chr, mergedFeatures);
|
|
43486
|
+
}
|
|
43629
43487
|
|
|
43630
|
-
|
|
43488
|
+
for (let i = 0, len = this.tracks.length; i < len; i++) {
|
|
43489
|
+
const trackOptions = Object.assign({}, options);
|
|
43631
43490
|
trackOptions.features = mergedFeatures[i];
|
|
43632
|
-
this.tracks[i].dataRange = dataRange;
|
|
43491
|
+
this.tracks[i].dataRange = this.dataRange;
|
|
43492
|
+
this.tracks[i].flipAxis = this.flipAxis;
|
|
43493
|
+
this.tracks[i].logScale = this.logScale;
|
|
43494
|
+
this.tracks[i].graphType = this.graphType;
|
|
43633
43495
|
this.tracks[i].draw(trackOptions);
|
|
43634
43496
|
}
|
|
43635
|
-
|
|
43636
|
-
}
|
|
43637
|
-
|
|
43638
|
-
paintAxis(ctx, pixelWidth, pixelHeight) {
|
|
43639
|
-
|
|
43640
|
-
var i, len, autoscale, track;
|
|
43641
|
-
|
|
43642
|
-
autoscale = true; // Hardcoded for now
|
|
43643
|
-
|
|
43644
|
-
for (i = 0, len = this.tracks.length; i < len; i++) {
|
|
43645
|
-
|
|
43646
|
-
track = this.tracks[i];
|
|
43647
|
-
|
|
43648
|
-
if (typeof track.paintAxis === 'function') {
|
|
43649
|
-
track.paintAxis(ctx, pixelWidth, pixelHeight);
|
|
43650
|
-
if (autoscale) break
|
|
43651
|
-
}
|
|
43652
|
-
}
|
|
43653
43497
|
}
|
|
43654
43498
|
|
|
43655
43499
|
popupData(clickState, features) {
|
|
43656
43500
|
|
|
43657
|
-
const featuresArray = features || clickState.viewport.
|
|
43501
|
+
const featuresArray = features || clickState.viewport.cachedFeatures;
|
|
43658
43502
|
|
|
43659
43503
|
if (featuresArray && featuresArray.length === this.tracks.length) {
|
|
43660
43504
|
// Array of feature arrays, 1 for each track
|
|
@@ -43671,42 +43515,23 @@ class MergedTrack extends TrackBase {
|
|
|
43671
43515
|
}
|
|
43672
43516
|
|
|
43673
43517
|
|
|
43674
|
-
supportsWholeGenome() {
|
|
43675
|
-
|
|
43676
|
-
return b
|
|
43518
|
+
get supportsWholeGenome() {
|
|
43519
|
+
return this.tracks.every(track => track.supportsWholeGenome())
|
|
43677
43520
|
}
|
|
43678
43521
|
}
|
|
43679
43522
|
|
|
43680
43523
|
function autoscale(chr, featureArrays) {
|
|
43681
43524
|
|
|
43682
|
-
|
|
43683
|
-
|
|
43684
|
-
|
|
43685
|
-
|
|
43686
|
-
// if (chr === 'all') {
|
|
43687
|
-
// allValues = [];
|
|
43688
|
-
// featureArrays.forEach(function (features) {
|
|
43689
|
-
// features.forEach(function (f) {
|
|
43690
|
-
// if (!Number.isNaN(f.value)) {
|
|
43691
|
-
// allValues.push(f.value);
|
|
43692
|
-
// }
|
|
43693
|
-
// });
|
|
43694
|
-
// });
|
|
43695
|
-
//
|
|
43696
|
-
// min = Math.min(0, IGVMath.percentile(allValues, .1));
|
|
43697
|
-
// max = IGVMath.percentile(allValues, 99.9);
|
|
43698
|
-
//
|
|
43699
|
-
// }
|
|
43700
|
-
// else {
|
|
43701
|
-
featureArrays.forEach(function (features, i) {
|
|
43702
|
-
features.forEach(function (f) {
|
|
43525
|
+
let min = 0;
|
|
43526
|
+
let max = -Number.MAX_VALUE;
|
|
43527
|
+
for(let features of featureArrays) {
|
|
43528
|
+
for(let f of features) {
|
|
43703
43529
|
if (typeof f.value !== 'undefined' && !Number.isNaN(f.value)) {
|
|
43704
43530
|
min = Math.min(min, f.value);
|
|
43705
43531
|
max = Math.max(max, f.value);
|
|
43706
43532
|
}
|
|
43707
|
-
}
|
|
43708
|
-
}
|
|
43709
|
-
// }
|
|
43533
|
+
}
|
|
43534
|
+
}
|
|
43710
43535
|
return {min: min, max: max}
|
|
43711
43536
|
}
|
|
43712
43537
|
|
|
@@ -43815,7 +43640,7 @@ class InteractionTrack extends TrackBase {
|
|
|
43815
43640
|
return this
|
|
43816
43641
|
}
|
|
43817
43642
|
|
|
43818
|
-
supportsWholeGenome() {
|
|
43643
|
+
get supportsWholeGenome() {
|
|
43819
43644
|
return true
|
|
43820
43645
|
}
|
|
43821
43646
|
|
|
@@ -44201,7 +44026,7 @@ class InteractionTrack extends TrackBase {
|
|
|
44201
44026
|
items = items.concat(MenuUtils.numericDataMenuItems(this.trackView));
|
|
44202
44027
|
}
|
|
44203
44028
|
|
|
44204
|
-
if (this.browser.circularView
|
|
44029
|
+
if (this.browser.circularView) {
|
|
44205
44030
|
items.push('<hr/>');
|
|
44206
44031
|
items.push({
|
|
44207
44032
|
label: 'Add interactions to circular view',
|
|
@@ -44219,7 +44044,7 @@ class InteractionTrack extends TrackBase {
|
|
|
44219
44044
|
contextMenuItemList(clickState) {
|
|
44220
44045
|
|
|
44221
44046
|
// Experimental JBrowse feature
|
|
44222
|
-
if (this.browser.circularView
|
|
44047
|
+
if (this.browser.circularView ) {
|
|
44223
44048
|
const viewport = clickState.viewport;
|
|
44224
44049
|
const list = [];
|
|
44225
44050
|
|
|
@@ -44248,21 +44073,22 @@ class InteractionTrack extends TrackBase {
|
|
|
44248
44073
|
|
|
44249
44074
|
// inView features are simply features that have been drawn, i.e. have a drawState
|
|
44250
44075
|
const inView = cachedFeatures.filter(f => f.drawState);
|
|
44251
|
-
if(inView.length === 0)
|
|
44076
|
+
if(inView.length === 0) return;
|
|
44252
44077
|
|
|
44253
|
-
this.browser.circularViewVisible = true;
|
|
44254
44078
|
const chords = makeBedPEChords(inView);
|
|
44255
|
-
|
|
44256
|
-
//
|
|
44257
|
-
|
|
44258
|
-
|
|
44259
|
-
|
|
44260
|
-
//
|
|
44261
|
-
|
|
44262
|
-
|
|
44263
|
-
|
|
44264
|
-
|
|
44265
|
-
|
|
44079
|
+
sendChords(chords, this, refFrame, 0.5);
|
|
44080
|
+
//
|
|
44081
|
+
//
|
|
44082
|
+
// // for filtered set, distinguishing the chromosomes is more critical than tracks
|
|
44083
|
+
// const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? this.color : getChrColor(refFrame.chr), 0.5)
|
|
44084
|
+
// const trackColor = IGVColor.addAlpha(this.color, 0.5)
|
|
44085
|
+
//
|
|
44086
|
+
// // name the chord set to include locus and filtering information
|
|
44087
|
+
// const encodedName = this.name.replaceAll(' ', '%20')
|
|
44088
|
+
// const chordSetName = "all" === refFrame.chr ?
|
|
44089
|
+
// encodedName :
|
|
44090
|
+
// `${encodedName} (${refFrame.chr}:${refFrame.start}-${refFrame.end} ; range:${this.dataRange.min}-${this.dataRange.max})`
|
|
44091
|
+
// this.browser.circularView.addChords(chords, {track: chordSetName, color: chordSetColor, trackColor: trackColor})
|
|
44266
44092
|
}
|
|
44267
44093
|
|
|
44268
44094
|
doAutoscale(features) {
|
|
@@ -44327,7 +44153,7 @@ class InteractionTrack extends TrackBase {
|
|
|
44327
44153
|
|
|
44328
44154
|
// We use the cached features rather than method to avoid async load. If the
|
|
44329
44155
|
// feature is not already loaded this won't work, but the user wouldn't be mousing over it either.
|
|
44330
|
-
const featureList = features || clickState.viewport.
|
|
44156
|
+
const featureList = features || clickState.viewport.cachedFeatures;
|
|
44331
44157
|
const candidates = [];
|
|
44332
44158
|
if (featureList) {
|
|
44333
44159
|
const proportional = (this.arcType === "proportional" || this.arcType === "inView" || this.arcType === "partialInView");
|
|
@@ -44661,7 +44487,7 @@ class VariantTrack extends TrackBase {
|
|
|
44661
44487
|
|
|
44662
44488
|
}
|
|
44663
44489
|
|
|
44664
|
-
supportsWholeGenome() {
|
|
44490
|
+
get supportsWholeGenome() {
|
|
44665
44491
|
return this.config.indexed === false || this.config.supportsWholeGenome === true
|
|
44666
44492
|
}
|
|
44667
44493
|
|
|
@@ -44853,7 +44679,7 @@ class VariantTrack extends TrackBase {
|
|
|
44853
44679
|
}
|
|
44854
44680
|
|
|
44855
44681
|
} else if (this._color) {
|
|
44856
|
-
variantColor =
|
|
44682
|
+
variantColor = this.color;
|
|
44857
44683
|
} else if ("NONVARIANT" === v.type) {
|
|
44858
44684
|
variantColor = this.nonRefColor;
|
|
44859
44685
|
} else if ("MIXED" === v.type) {
|
|
@@ -44864,6 +44690,10 @@ class VariantTrack extends TrackBase {
|
|
|
44864
44690
|
return variantColor
|
|
44865
44691
|
}
|
|
44866
44692
|
|
|
44693
|
+
get color() {
|
|
44694
|
+
return this._color ? ((typeof this._color === "function") ? this._color(v) : this._color) : this.defaultColor
|
|
44695
|
+
}
|
|
44696
|
+
|
|
44867
44697
|
clickedFeatures(clickState, features) {
|
|
44868
44698
|
|
|
44869
44699
|
let featureList = super.clickedFeatures(clickState, features);
|
|
@@ -45090,25 +44920,15 @@ class VariantTrack extends TrackBase {
|
|
|
45090
44920
|
}
|
|
45091
44921
|
|
|
45092
44922
|
// Experimental JBrowse circular view integration
|
|
45093
|
-
if (this.browser.circularView
|
|
44923
|
+
if (this.browser.circularView) {
|
|
45094
44924
|
|
|
45095
44925
|
menuItems.push('<hr>');
|
|
45096
44926
|
menuItems.push({
|
|
45097
44927
|
label: 'Add SVs to circular view',
|
|
45098
44928
|
click: () => {
|
|
45099
|
-
const inView = [];
|
|
45100
44929
|
for (let viewport of this.trackView.viewports) {
|
|
45101
|
-
|
|
45102
|
-
for (let f of viewport.getCachedFeatures()) {
|
|
45103
|
-
if (f.end >= refFrame.start && f.start <= refFrame.end) {
|
|
45104
|
-
inView.push(f);
|
|
45105
|
-
}
|
|
45106
|
-
}
|
|
44930
|
+
this.sendChordsForViewport(viewport);
|
|
45107
44931
|
}
|
|
45108
|
-
|
|
45109
|
-
const chords = makeVCFChords(inView);
|
|
45110
|
-
const color = IGVColor.addAlpha(this._color || this.defaultColor, 0.5);
|
|
45111
|
-
this.browser.circularView.addChords(chords, {track: this.name, color: color});
|
|
45112
44932
|
}
|
|
45113
44933
|
});
|
|
45114
44934
|
}
|
|
@@ -45120,20 +44940,14 @@ class VariantTrack extends TrackBase {
|
|
|
45120
44940
|
contextMenuItemList(clickState) {
|
|
45121
44941
|
|
|
45122
44942
|
// Experimental JBrowse circular view integration
|
|
45123
|
-
if (this.browser.circularView
|
|
44943
|
+
if (this.browser.circularView) {
|
|
45124
44944
|
const viewport = clickState.viewport;
|
|
45125
44945
|
const list = [];
|
|
45126
44946
|
|
|
45127
44947
|
list.push({
|
|
45128
44948
|
label: 'Add SVs to Circular View',
|
|
45129
44949
|
click: () => {
|
|
45130
|
-
|
|
45131
|
-
const inView = "all" === refFrame.chr ?
|
|
45132
|
-
this.featureSource.getAllFeatures() :
|
|
45133
|
-
this.featureSource.featureCache.queryFeatures(refFrame.chr, refFrame.start, refFrame.end);
|
|
45134
|
-
const chords = makeVCFChords(inView);
|
|
45135
|
-
const color = IGVColor.addAlpha(this._color || this.defaultColor, 0.5);
|
|
45136
|
-
this.browser.circularView.addChords(chords, {track: this.name, color: color});
|
|
44950
|
+
this.sendChordsForViewport(viewport);
|
|
45137
44951
|
}
|
|
45138
44952
|
});
|
|
45139
44953
|
|
|
@@ -45143,6 +44957,15 @@ class VariantTrack extends TrackBase {
|
|
|
45143
44957
|
}
|
|
45144
44958
|
|
|
45145
44959
|
|
|
44960
|
+
sendChordsForViewport(viewport) {
|
|
44961
|
+
const refFrame = viewport.referenceFrame;
|
|
44962
|
+
const inView = "all" === refFrame.chr ?
|
|
44963
|
+
this.featureSource.getAllFeatures() :
|
|
44964
|
+
this.featureSource.featureCache.queryFeatures(refFrame.chr, refFrame.start, refFrame.end);
|
|
44965
|
+
const chords = makeVCFChords(inView);
|
|
44966
|
+
sendChords(chords, this, refFrame, 0.5);
|
|
44967
|
+
}
|
|
44968
|
+
|
|
45146
44969
|
/**
|
|
45147
44970
|
* Create a "color by" checkbox menu item, optionally initially checked
|
|
45148
44971
|
* @param menuItem
|
|
@@ -45439,7 +45262,7 @@ class EqtlTrack extends TrackBase {
|
|
|
45439
45262
|
*/
|
|
45440
45263
|
popupData(clickState) {
|
|
45441
45264
|
|
|
45442
|
-
let features = clickState.viewport.
|
|
45265
|
+
let features = clickState.viewport.cachedFeatures;
|
|
45443
45266
|
if (!features || features.length === 0) return []
|
|
45444
45267
|
|
|
45445
45268
|
const tolerance = 3;
|
|
@@ -45658,7 +45481,7 @@ class GWASTrack extends TrackBase {
|
|
|
45658
45481
|
}
|
|
45659
45482
|
|
|
45660
45483
|
|
|
45661
|
-
supportsWholeGenome() {
|
|
45484
|
+
get supportsWholeGenome() {
|
|
45662
45485
|
return true
|
|
45663
45486
|
}
|
|
45664
45487
|
|
|
@@ -45769,7 +45592,7 @@ class GWASTrack extends TrackBase {
|
|
|
45769
45592
|
|
|
45770
45593
|
let data = [];
|
|
45771
45594
|
const track = clickState.viewport.trackView.track;
|
|
45772
|
-
const features = clickState.viewport.
|
|
45595
|
+
const features = clickState.viewport.cachedFeatures;
|
|
45773
45596
|
|
|
45774
45597
|
if (features) {
|
|
45775
45598
|
let count = 0;
|
|
@@ -46172,7 +45995,7 @@ class GCNVTrack extends TrackBase {
|
|
|
46172
45995
|
return items
|
|
46173
45996
|
}
|
|
46174
45997
|
|
|
46175
|
-
supportsWholeGenome() {
|
|
45998
|
+
get supportsWholeGenome() {
|
|
46176
45999
|
return false
|
|
46177
46000
|
}
|
|
46178
46001
|
}
|
|
@@ -46452,7 +46275,7 @@ class RNAFeatureSource {
|
|
|
46452
46275
|
|
|
46453
46276
|
const data = await igvxhr.loadString(this.config.url, options);
|
|
46454
46277
|
|
|
46455
|
-
this.featureCache = new FeatureCache(parseBP(data), genome);
|
|
46278
|
+
this.featureCache = new FeatureCache$1(parseBP(data), genome);
|
|
46456
46279
|
|
|
46457
46280
|
return this.featureCache.queryFeatures(chr, start, end)
|
|
46458
46281
|
|
|
@@ -46559,21 +46382,18 @@ class RNAFeatureSource {
|
|
|
46559
46382
|
* THE SOFTWARE.
|
|
46560
46383
|
*/
|
|
46561
46384
|
|
|
46385
|
+
/**
|
|
46386
|
+
* Class represents an ideogram of a chromsome cytobands. It is used for the header of a track panel.
|
|
46387
|
+
*
|
|
46388
|
+
*/
|
|
46562
46389
|
class IdeogramTrack {
|
|
46563
46390
|
constructor(browser) {
|
|
46564
|
-
|
|
46565
46391
|
this.browser = browser;
|
|
46566
|
-
|
|
46567
46392
|
this.type = 'ideogram';
|
|
46568
|
-
this.id = this.type;
|
|
46569
|
-
|
|
46570
46393
|
this.height = 16;
|
|
46571
|
-
|
|
46572
46394
|
this.order = Number.MIN_SAFE_INTEGER;
|
|
46573
|
-
|
|
46574
46395
|
this.disableButtons = true;
|
|
46575
46396
|
this.ignoreTrackMenu = true;
|
|
46576
|
-
|
|
46577
46397
|
}
|
|
46578
46398
|
|
|
46579
46399
|
async getFeatures(chr, start, end) {
|
|
@@ -46840,7 +46660,7 @@ class SpliceJunctionTrack extends TrackBase {
|
|
|
46840
46660
|
|
|
46841
46661
|
}
|
|
46842
46662
|
|
|
46843
|
-
supportsWholeGenome() {
|
|
46663
|
+
get supportsWholeGenome() {
|
|
46844
46664
|
return false
|
|
46845
46665
|
}
|
|
46846
46666
|
|
|
@@ -47734,6 +47554,15 @@ class ReferenceFrame {
|
|
|
47734
47554
|
this.id = guid$2();
|
|
47735
47555
|
}
|
|
47736
47556
|
|
|
47557
|
+
extend(locus) {
|
|
47558
|
+
const newStart = Math.min(locus.start, this.start);
|
|
47559
|
+
const newEnd = Math.max(locus.end, this.end);
|
|
47560
|
+
const ratio = (newEnd - newStart) / (this.end - this.start);
|
|
47561
|
+
this.start = newStart;
|
|
47562
|
+
this.end = newEnd;
|
|
47563
|
+
this.bpPerPixel *= ratio;
|
|
47564
|
+
}
|
|
47565
|
+
|
|
47737
47566
|
calculateEnd(pixels) {
|
|
47738
47567
|
return this.start + this.bpPerPixel * pixels
|
|
47739
47568
|
}
|
|
@@ -47820,7 +47649,7 @@ class ReferenceFrame {
|
|
|
47820
47649
|
|
|
47821
47650
|
const viewChanged = start !== this.start || bpPerPixel !== this.bpPerPixel;
|
|
47822
47651
|
if (viewChanged) {
|
|
47823
|
-
await browser.updateViews(
|
|
47652
|
+
await browser.updateViews(true);
|
|
47824
47653
|
}
|
|
47825
47654
|
|
|
47826
47655
|
}
|
|
@@ -47896,30 +47725,6 @@ function createReferenceFrameList(loci, genome, browserFlanking, minimumBases, v
|
|
|
47896
47725
|
})
|
|
47897
47726
|
}
|
|
47898
47727
|
|
|
47899
|
-
function adjustReferenceFrame(scaleFactor, referenceFrame, viewportWidth, alignmentStart, alignmentLength) {
|
|
47900
|
-
|
|
47901
|
-
referenceFrame.bpPerPixel *= scaleFactor;
|
|
47902
|
-
|
|
47903
|
-
const alignmentEE = alignmentStart + alignmentLength;
|
|
47904
|
-
const alignmentCC = (alignmentStart + alignmentEE) / 2;
|
|
47905
|
-
|
|
47906
|
-
referenceFrame.start = alignmentCC - (referenceFrame.bpPerPixel * (viewportWidth / 2));
|
|
47907
|
-
referenceFrame.end = referenceFrame.start + (referenceFrame.bpPerPixel * viewportWidth);
|
|
47908
|
-
referenceFrame.locusSearchString = referenceFrame.getLocusString();
|
|
47909
|
-
}
|
|
47910
|
-
|
|
47911
|
-
function createReferenceFrameWithAlignment(genome, chromosomeName, bpp, viewportWidth, alignmentStart, alignmentLength) {
|
|
47912
|
-
|
|
47913
|
-
const alignmentEE = alignmentStart + alignmentLength;
|
|
47914
|
-
const alignmentCC = (alignmentStart + alignmentEE) / 2;
|
|
47915
|
-
|
|
47916
|
-
const ss = alignmentCC - (bpp * (viewportWidth / 2));
|
|
47917
|
-
const ee = ss + (bpp * viewportWidth);
|
|
47918
|
-
|
|
47919
|
-
return new ReferenceFrame(genome, chromosomeName, ss, ee, bpp)
|
|
47920
|
-
|
|
47921
|
-
}
|
|
47922
|
-
|
|
47923
47728
|
const defaultNucleotideColors = {
|
|
47924
47729
|
"A": "rgb( 0, 200, 0)",
|
|
47925
47730
|
"C": "rgb( 0,0,200)",
|
|
@@ -49019,6 +48824,69 @@ const SVGSaveControl = function (parent, browser) {
|
|
|
49019
48824
|
button.addEventListener('click', () => browser.saveSVGtoFile({}));
|
|
49020
48825
|
};
|
|
49021
48826
|
|
|
48827
|
+
const viewportColumnManager =
|
|
48828
|
+
{
|
|
48829
|
+
createColumns: (columnContainer, count) => {
|
|
48830
|
+
|
|
48831
|
+
for (let i = 0; i < count; i++) {
|
|
48832
|
+
if (0 === i) {
|
|
48833
|
+
createColumn(columnContainer, 'igv-column');
|
|
48834
|
+
} else {
|
|
48835
|
+
columnContainer.appendChild(div$1({class: 'igv-column-shim'}));
|
|
48836
|
+
createColumn(columnContainer, 'igv-column');
|
|
48837
|
+
}
|
|
48838
|
+
}
|
|
48839
|
+
|
|
48840
|
+
},
|
|
48841
|
+
|
|
48842
|
+
removeColumnAtIndex: (i, column) => {
|
|
48843
|
+
const shim = 0 === i ? column.nextElementSibling : column.previousElementSibling;
|
|
48844
|
+
column.remove();
|
|
48845
|
+
shim.remove();
|
|
48846
|
+
},
|
|
48847
|
+
|
|
48848
|
+
insertAfter: referenceElement => {
|
|
48849
|
+
|
|
48850
|
+
const shim = div$1({class: 'igv-column-shim'});
|
|
48851
|
+
insertElementAfter(shim, referenceElement);
|
|
48852
|
+
|
|
48853
|
+
const column = div$1({class: 'igv-column'});
|
|
48854
|
+
insertElementAfter(column, shim);
|
|
48855
|
+
|
|
48856
|
+
return column
|
|
48857
|
+
},
|
|
48858
|
+
|
|
48859
|
+
insertBefore: (referenceElement, count) => {
|
|
48860
|
+
|
|
48861
|
+
for (let i = 0; i < count; i++) {
|
|
48862
|
+
|
|
48863
|
+
const column = div$1({class: 'igv-column'});
|
|
48864
|
+
insertElementBefore(column, referenceElement);
|
|
48865
|
+
|
|
48866
|
+
if (count > 1 && i > 0) {
|
|
48867
|
+
const columnShim = div$1({class: 'igv-column-shim'});
|
|
48868
|
+
insertElementBefore(columnShim, column);
|
|
48869
|
+
}
|
|
48870
|
+
|
|
48871
|
+
}
|
|
48872
|
+
|
|
48873
|
+
},
|
|
48874
|
+
|
|
48875
|
+
indexOfColumn: (columnContainer, column) => {
|
|
48876
|
+
|
|
48877
|
+
const allColumns = columnContainer.querySelectorAll('.igv-column');
|
|
48878
|
+
|
|
48879
|
+
for (let i = 0; i < allColumns.length; i++) {
|
|
48880
|
+
const c = allColumns[ i ];
|
|
48881
|
+
if (c === column) {
|
|
48882
|
+
return i
|
|
48883
|
+
}
|
|
48884
|
+
}
|
|
48885
|
+
|
|
48886
|
+
return undefined
|
|
48887
|
+
},
|
|
48888
|
+
};
|
|
48889
|
+
|
|
49022
48890
|
/*
|
|
49023
48891
|
* The MIT License (MIT)
|
|
49024
48892
|
*
|
|
@@ -49251,7 +49119,7 @@ class RulerTrack {
|
|
|
49251
49119
|
}
|
|
49252
49120
|
}
|
|
49253
49121
|
|
|
49254
|
-
supportsWholeGenome() {
|
|
49122
|
+
get supportsWholeGenome() {
|
|
49255
49123
|
return true
|
|
49256
49124
|
};
|
|
49257
49125
|
|
|
@@ -49622,8 +49490,8 @@ class Browser {
|
|
|
49622
49490
|
this.svgSaveControl = new SVGSaveControl($toggle_button_container.get(0), this);
|
|
49623
49491
|
}
|
|
49624
49492
|
|
|
49625
|
-
if(config.customButtons) {
|
|
49626
|
-
for(let b of config.customButtons) {
|
|
49493
|
+
if (config.customButtons) {
|
|
49494
|
+
for (let b of config.customButtons) {
|
|
49627
49495
|
new CustomButton($toggle_button_container.get(0), this, b);
|
|
49628
49496
|
}
|
|
49629
49497
|
}
|
|
@@ -49784,7 +49652,6 @@ class Browser {
|
|
|
49784
49652
|
return undefined
|
|
49785
49653
|
}
|
|
49786
49654
|
}
|
|
49787
|
-
|
|
49788
49655
|
}
|
|
49789
49656
|
}
|
|
49790
49657
|
|
|
@@ -49829,7 +49696,9 @@ class Browser {
|
|
|
49829
49696
|
// Create ideogram and ruler track. Really this belongs in browser initialization, but creation is
|
|
49830
49697
|
// deferred because ideogram and ruler are treated as "tracks", and tracks require a reference frame
|
|
49831
49698
|
if (false !== session.showIdeogram) {
|
|
49832
|
-
|
|
49699
|
+
const ideogramTrack = new IdeogramTrack(this);
|
|
49700
|
+
ideogramTrack.id = 'ideogram';
|
|
49701
|
+
this.trackViews.push(new TrackView(this, this.columnContainer, ideogramTrack));
|
|
49833
49702
|
}
|
|
49834
49703
|
|
|
49835
49704
|
if (false !== session.showRuler) {
|
|
@@ -49874,6 +49743,11 @@ class Browser {
|
|
|
49874
49743
|
|
|
49875
49744
|
await this.loadTrackList(trackConfigurations);
|
|
49876
49745
|
|
|
49746
|
+
// The ruler and ideogram tracks are not explicitly loaded, but needs updated nonetheless.
|
|
49747
|
+
for (let rtv of this.trackViews.filter((tv) => tv.track.type === 'ruler' || tv.track.type === 'ideogram')) {
|
|
49748
|
+
rtv.updateViews();
|
|
49749
|
+
}
|
|
49750
|
+
|
|
49877
49751
|
this.updateUIWithReferenceFrameList();
|
|
49878
49752
|
|
|
49879
49753
|
}
|
|
@@ -50052,23 +49926,19 @@ class Browser {
|
|
|
50052
49926
|
|
|
50053
49927
|
async loadTrackList(configList) {
|
|
50054
49928
|
|
|
50055
|
-
|
|
50056
|
-
|
|
50057
|
-
|
|
50058
|
-
|
|
50059
|
-
}
|
|
49929
|
+
const promises = [];
|
|
49930
|
+
for (let config of configList) {
|
|
49931
|
+
promises.push(this.loadTrack(config));
|
|
49932
|
+
}
|
|
50060
49933
|
|
|
50061
|
-
|
|
50062
|
-
|
|
50063
|
-
|
|
50064
|
-
|
|
50065
|
-
|
|
50066
|
-
|
|
50067
|
-
}
|
|
50068
|
-
return loadedTracks
|
|
50069
|
-
} finally {
|
|
50070
|
-
await this.resize();
|
|
49934
|
+
const loadedTracks = await Promise.all(promises);
|
|
49935
|
+
const groupAutoscaleViews = this.trackViews.filter(function (trackView) {
|
|
49936
|
+
return trackView.track.autoscaleGroup
|
|
49937
|
+
});
|
|
49938
|
+
if (groupAutoscaleViews.length > 0) {
|
|
49939
|
+
this.updateViews();
|
|
50071
49940
|
}
|
|
49941
|
+
return loadedTracks
|
|
50072
49942
|
}
|
|
50073
49943
|
|
|
50074
49944
|
async loadROI(config) {
|
|
@@ -50082,6 +49952,8 @@ class Browser {
|
|
|
50082
49952
|
} else {
|
|
50083
49953
|
this.roi.push(new ROI(config, this.genome));
|
|
50084
49954
|
}
|
|
49955
|
+
// Force reload all views (force = true) to insure ROI features are loaded. Wasteful but this function is
|
|
49956
|
+
// rarely called.
|
|
50085
49957
|
await this.updateViews(true);
|
|
50086
49958
|
}
|
|
50087
49959
|
|
|
@@ -50093,14 +49965,14 @@ class Browser {
|
|
|
50093
49965
|
}
|
|
50094
49966
|
}
|
|
50095
49967
|
for (let tv of this.trackViews) {
|
|
50096
|
-
tv.
|
|
49968
|
+
tv.repaintViews();
|
|
50097
49969
|
}
|
|
50098
49970
|
}
|
|
50099
49971
|
|
|
50100
49972
|
clearROIs() {
|
|
50101
49973
|
this.roi = [];
|
|
50102
49974
|
for (let tv of this.trackViews) {
|
|
50103
|
-
tv.
|
|
49975
|
+
tv.repaintViews();
|
|
50104
49976
|
}
|
|
50105
49977
|
}
|
|
50106
49978
|
|
|
@@ -50112,24 +49984,12 @@ class Browser {
|
|
|
50112
49984
|
/**
|
|
50113
49985
|
* Return a promise to load a track.
|
|
50114
49986
|
*
|
|
50115
|
-
* Each track is associated with the following DOM elements
|
|
50116
|
-
*
|
|
50117
|
-
* leftHandGutter - div on the left for track controls and legend
|
|
50118
|
-
* contentDiv - a div element wrapping all the track content. Height can be > viewportDiv height
|
|
50119
|
-
* viewportDiv - a div element through which the track is viewed. This might have a vertical scrollbar
|
|
50120
|
-
* canvas - canvas element upon which the track is drawn. Child of contentDiv
|
|
50121
|
-
*
|
|
50122
|
-
* The width of all elements should be equal. Height of the viewportDiv is controlled by the user, but never
|
|
50123
|
-
* greater than the contentDiv height. Height of contentDiv and canvas are equal, and governed by the data
|
|
50124
|
-
* loaded.
|
|
50125
|
-
*
|
|
50126
|
-
*
|
|
50127
49987
|
* @param config
|
|
50128
49988
|
* @param doResize - undefined by default
|
|
50129
49989
|
* @returns {*}
|
|
50130
49990
|
*/
|
|
50131
49991
|
|
|
50132
|
-
async loadTrack(config
|
|
49992
|
+
async loadTrack(config) {
|
|
50133
49993
|
|
|
50134
49994
|
|
|
50135
49995
|
// config might be json
|
|
@@ -50197,11 +50057,6 @@ class Browser {
|
|
|
50197
50057
|
}
|
|
50198
50058
|
msg += (": " + config.url);
|
|
50199
50059
|
Alert.presentAlert(new Error(msg), undefined);
|
|
50200
|
-
} finally {
|
|
50201
|
-
// TODO: If loadTrack() is called individually - not via loadTrackList() - call this.resize()
|
|
50202
|
-
if (false === doResize) ; else {
|
|
50203
|
-
await this.resize();
|
|
50204
|
-
}
|
|
50205
50060
|
}
|
|
50206
50061
|
}
|
|
50207
50062
|
|
|
@@ -50425,44 +50280,11 @@ class Browser {
|
|
|
50425
50280
|
this.navbarManager.navbarDidResize(this.$navigation.width(), isWGV);
|
|
50426
50281
|
}
|
|
50427
50282
|
|
|
50428
|
-
|
|
50429
|
-
|
|
50430
|
-
|
|
50431
|
-
async resize() {
|
|
50432
|
-
|
|
50433
|
-
const viewportWidth = this.calculateViewportWidth(this.referenceFrameList.length);
|
|
50434
|
-
|
|
50435
|
-
for (let referenceFrame of this.referenceFrameList) {
|
|
50436
|
-
|
|
50437
|
-
const index = this.referenceFrameList.indexOf(referenceFrame);
|
|
50438
|
-
|
|
50439
|
-
const {chr, genome} = referenceFrame;
|
|
50440
|
-
|
|
50441
|
-
const {bpLength} = genome.getChromosome(referenceFrame.chr);
|
|
50442
|
-
|
|
50443
|
-
const viewportWidthBP = referenceFrame.toBP(viewportWidth);
|
|
50444
|
-
|
|
50445
|
-
// viewportWidthBP > bpLength occurs when locus is full chromosome and user widens browser
|
|
50446
|
-
if (GenomeUtils.isWholeGenomeView(chr) || viewportWidthBP > bpLength) {
|
|
50447
|
-
// console.log(`${ Date.now() } Recalc referenceFrame(${ index }) bpp. viewport ${ StringUtils.numberFormatter(viewportWidthBP) } > ${ StringUtils.numberFormatter(bpLength) }.`)
|
|
50448
|
-
referenceFrame.bpPerPixel = bpLength / viewportWidth;
|
|
50449
|
-
} else {
|
|
50450
|
-
// console.log(`${ Date.now() } Recalc referenceFrame(${ index }) end.`)
|
|
50451
|
-
referenceFrame.end = referenceFrame.start + referenceFrame.toBP(viewportWidth);
|
|
50452
|
-
}
|
|
50453
|
-
|
|
50454
|
-
for (let {viewports} of this.trackViews) {
|
|
50455
|
-
viewports[index].setWidth(viewportWidth);
|
|
50456
|
-
}
|
|
50457
|
-
|
|
50458
|
-
}
|
|
50459
|
-
|
|
50460
|
-
await this.updateViews(true);
|
|
50461
|
-
|
|
50462
|
-
this.updateUIWithReferenceFrameList();
|
|
50283
|
+
resize.call(this);
|
|
50284
|
+
await this.updateViews();
|
|
50463
50285
|
}
|
|
50464
50286
|
|
|
50465
|
-
async updateViews(
|
|
50287
|
+
async updateViews() {
|
|
50466
50288
|
|
|
50467
50289
|
const trackViews = this.trackViews;
|
|
50468
50290
|
|
|
@@ -50475,7 +50297,7 @@ class Browser {
|
|
|
50475
50297
|
// Don't autoscale while dragging.
|
|
50476
50298
|
if (this.dragObject) {
|
|
50477
50299
|
for (let trackView of trackViews) {
|
|
50478
|
-
await trackView.updateViews(
|
|
50300
|
+
await trackView.updateViews();
|
|
50479
50301
|
}
|
|
50480
50302
|
} else {
|
|
50481
50303
|
// Group autoscale
|
|
@@ -50520,14 +50342,14 @@ class Browser {
|
|
|
50520
50342
|
for (let trackView of groupTrackViews) {
|
|
50521
50343
|
trackView.track.dataRange = dataRange;
|
|
50522
50344
|
trackView.track.autoscale = false;
|
|
50523
|
-
p.push(trackView.updateViews(
|
|
50345
|
+
p.push(trackView.updateViews());
|
|
50524
50346
|
}
|
|
50525
50347
|
await Promise.all(p);
|
|
50526
50348
|
}
|
|
50527
50349
|
|
|
50528
50350
|
}
|
|
50529
50351
|
|
|
50530
|
-
await Promise.all(otherTracks.map(tv => tv.updateViews(
|
|
50352
|
+
await Promise.all(otherTracks.map(tv => tv.updateViews()));
|
|
50531
50353
|
// for (let trackView of otherTracks) {
|
|
50532
50354
|
// await trackView.updateViews(force);
|
|
50533
50355
|
// }
|
|
@@ -50535,6 +50357,12 @@ class Browser {
|
|
|
50535
50357
|
|
|
50536
50358
|
}
|
|
50537
50359
|
|
|
50360
|
+
repaintViews() {
|
|
50361
|
+
for (let trackView of this.trackViews) {
|
|
50362
|
+
trackView.repaintViews();
|
|
50363
|
+
}
|
|
50364
|
+
}
|
|
50365
|
+
|
|
50538
50366
|
updateLocusSearchWidget() {
|
|
50539
50367
|
|
|
50540
50368
|
const referenceFrameList = this.referenceFrameList;
|
|
@@ -50599,49 +50427,50 @@ class Browser {
|
|
|
50599
50427
|
}
|
|
50600
50428
|
}
|
|
50601
50429
|
|
|
50602
|
-
|
|
50430
|
+
/**
|
|
50431
|
+
* Add a new multi-locus panel for the specified region
|
|
50432
|
+
* @param chr
|
|
50433
|
+
* @param start
|
|
50434
|
+
* @param end
|
|
50435
|
+
* @param referenceFrameLeft - optional, if supplied new panel should be placed to the immediate right
|
|
50436
|
+
*/
|
|
50437
|
+
async addMultiLocusPanel(chr, start, end, referenceFrameLeft) {
|
|
50603
50438
|
|
|
50604
50439
|
// account for reduced viewport width as a result of adding right mate pair panel
|
|
50605
50440
|
const viewportWidth = this.calculateViewportWidth(1 + this.referenceFrameList.length);
|
|
50606
|
-
|
|
50607
50441
|
const scaleFactor = this.calculateViewportWidth(this.referenceFrameList.length) / this.calculateViewportWidth(1 + this.referenceFrameList.length);
|
|
50608
|
-
|
|
50609
|
-
|
|
50610
|
-
|
|
50611
|
-
const mateChrName = this.genome.getChromosomeName(alignment.mate.chr);
|
|
50612
|
-
|
|
50613
|
-
const referenceFrameRight = createReferenceFrameWithAlignment(this.genome, mateChrName, referenceFrameLeft.bpPerPixel, viewportWidth, alignment.mate.position, alignment.lengthOnRef);
|
|
50442
|
+
for (let refFrame of this.referenceFrameList) {
|
|
50443
|
+
refFrame.bpPerPixel *= scaleFactor;
|
|
50444
|
+
}
|
|
50614
50445
|
|
|
50615
|
-
|
|
50616
|
-
const
|
|
50617
|
-
const
|
|
50446
|
+
const bpp = (end - start) / viewportWidth;
|
|
50447
|
+
const newReferenceFrame = new ReferenceFrame(this.genome, chr, start, end, bpp);
|
|
50448
|
+
const indexLeft = referenceFrameLeft ? this.referenceFrameList.indexOf(referenceFrameLeft) : this.referenceFrameList.length - 1;
|
|
50449
|
+
const indexRight = 1 + indexLeft;
|
|
50618
50450
|
|
|
50451
|
+
// TODO -- this is really ugly
|
|
50619
50452
|
const {$viewport} = this.trackViews[0].viewports[indexLeft];
|
|
50620
50453
|
const viewportColumn = viewportColumnManager.insertAfter($viewport.get(0).parentElement);
|
|
50621
50454
|
|
|
50622
50455
|
if (indexRight === this.referenceFrameList.length) {
|
|
50623
|
-
|
|
50624
|
-
this.referenceFrameList.push(referenceFrameRight);
|
|
50625
|
-
|
|
50456
|
+
this.referenceFrameList.push(newReferenceFrame);
|
|
50626
50457
|
for (let trackView of this.trackViews) {
|
|
50627
|
-
const viewport = createViewport(trackView, viewportColumn,
|
|
50458
|
+
const viewport = createViewport(trackView, viewportColumn, newReferenceFrame);
|
|
50628
50459
|
trackView.viewports.push(viewport);
|
|
50629
50460
|
}
|
|
50630
|
-
|
|
50631
50461
|
} else {
|
|
50632
|
-
|
|
50633
|
-
this.referenceFrameList.splice(indexRight, 0, referenceFrameRight);
|
|
50634
|
-
|
|
50462
|
+
this.referenceFrameList.splice(indexRight, 0, newReferenceFrame);
|
|
50635
50463
|
for (let trackView of this.trackViews) {
|
|
50636
|
-
const viewport = createViewport(trackView, viewportColumn,
|
|
50464
|
+
const viewport = createViewport(trackView, viewportColumn, newReferenceFrame);
|
|
50637
50465
|
trackView.viewports.splice(indexRight, 0, viewport);
|
|
50638
50466
|
}
|
|
50639
|
-
|
|
50640
50467
|
}
|
|
50641
50468
|
|
|
50469
|
+
|
|
50642
50470
|
this.centerLineList = this.createCenterLineList(this.columnContainer);
|
|
50643
50471
|
|
|
50644
|
-
|
|
50472
|
+
resize.call(this);
|
|
50473
|
+
await this.updateViews(true);
|
|
50645
50474
|
}
|
|
50646
50475
|
|
|
50647
50476
|
async removeMultiLocusPanel(referenceFrame) {
|
|
@@ -50670,7 +50499,13 @@ class Browser {
|
|
|
50670
50499
|
|
|
50671
50500
|
}
|
|
50672
50501
|
|
|
50673
|
-
|
|
50502
|
+
/**
|
|
50503
|
+
* Goto the locus represented by the selected referenceFrame, discarding all other panels
|
|
50504
|
+
*
|
|
50505
|
+
* @param referenceFrame
|
|
50506
|
+
* @returns {Promise<void>}
|
|
50507
|
+
*/
|
|
50508
|
+
async gotoMultilocusPanel(referenceFrame) {
|
|
50674
50509
|
|
|
50675
50510
|
const referenceFrameIndex = this.referenceFrameList.indexOf(referenceFrame);
|
|
50676
50511
|
|
|
@@ -50724,7 +50559,7 @@ class Browser {
|
|
|
50724
50559
|
|
|
50725
50560
|
this.updateUIWithReferenceFrameList();
|
|
50726
50561
|
|
|
50727
|
-
await this.updateViews(
|
|
50562
|
+
await this.updateViews();
|
|
50728
50563
|
|
|
50729
50564
|
}
|
|
50730
50565
|
|
|
@@ -50947,7 +50782,6 @@ class Browser {
|
|
|
50947
50782
|
}
|
|
50948
50783
|
|
|
50949
50784
|
|
|
50950
|
-
|
|
50951
50785
|
json["tracks"] = trackJson;
|
|
50952
50786
|
|
|
50953
50787
|
return json // This is an object, not a json string
|
|
@@ -50966,16 +50800,6 @@ class Browser {
|
|
|
50966
50800
|
return surl
|
|
50967
50801
|
}
|
|
50968
50802
|
|
|
50969
|
-
currentLoci() {
|
|
50970
|
-
const loci = [];
|
|
50971
|
-
const anyTrackView = this.trackViews[0];
|
|
50972
|
-
for (let {referenceFrame} of anyTrackView.viewports) {
|
|
50973
|
-
const locusString = referenceFrame.getLocusString();
|
|
50974
|
-
loci.push(locusString);
|
|
50975
|
-
}
|
|
50976
|
-
return loci
|
|
50977
|
-
}
|
|
50978
|
-
|
|
50979
50803
|
/**
|
|
50980
50804
|
* Record a mouse click on a specific viewport. This might be the start of a drag operation. Dragging
|
|
50981
50805
|
* (panning) is handled here so that the mouse can move out of a specific viewport (e.g. stray into another
|
|
@@ -51013,12 +50837,22 @@ class Browser {
|
|
|
51013
50837
|
|
|
51014
50838
|
}
|
|
51015
50839
|
|
|
50840
|
+
/**
|
|
50841
|
+
* Track drag here refers to vertical dragging to reorder tracks, not horizontal panning.
|
|
50842
|
+
*
|
|
50843
|
+
* @param trackView
|
|
50844
|
+
*/
|
|
51016
50845
|
startTrackDrag(trackView) {
|
|
51017
50846
|
|
|
51018
50847
|
this.dragTrack = trackView;
|
|
51019
50848
|
|
|
51020
50849
|
}
|
|
51021
50850
|
|
|
50851
|
+
/**
|
|
50852
|
+
* Track drag here refers to vertical dragging to reorder tracks, not horizontal panning.
|
|
50853
|
+
*
|
|
50854
|
+
* @param dragDestination
|
|
50855
|
+
*/
|
|
51022
50856
|
updateTrackDrag(dragDestination) {
|
|
51023
50857
|
|
|
51024
50858
|
if (dragDestination && this.dragTrack) {
|
|
@@ -51093,12 +50927,9 @@ class Browser {
|
|
|
51093
50927
|
}
|
|
51094
50928
|
|
|
51095
50929
|
addWindowResizeHandler() {
|
|
51096
|
-
this.
|
|
50930
|
+
// Create a copy of the prototype "resize" function bound to this instance. Neccessary to support removing.
|
|
50931
|
+
this.boundWindowResizeHandler = resize.bind(this);
|
|
51097
50932
|
window.addEventListener('resize', this.boundWindowResizeHandler);
|
|
51098
|
-
|
|
51099
|
-
function windowResizeHandler() {
|
|
51100
|
-
this.resize();
|
|
51101
|
-
}
|
|
51102
50933
|
}
|
|
51103
50934
|
|
|
51104
50935
|
removeWindowResizeHandler() {
|
|
@@ -51181,7 +51012,7 @@ class Browser {
|
|
|
51181
51012
|
id: this.genome.id,
|
|
51182
51013
|
chromosomes: makeCircViewChromosomes(this.genome)
|
|
51183
51014
|
});
|
|
51184
|
-
this.circularViewVisible = show
|
|
51015
|
+
this.circularViewVisible = show;
|
|
51185
51016
|
|
|
51186
51017
|
}
|
|
51187
51018
|
|
|
@@ -51197,6 +51028,50 @@ class Browser {
|
|
|
51197
51028
|
}
|
|
51198
51029
|
}
|
|
51199
51030
|
|
|
51031
|
+
/**
|
|
51032
|
+
* Function called win window is resized, or visibility changed (e.g. "show" from a tab). This is a function rather
|
|
51033
|
+
* than class method because it needs to be copied and bound to specific instances of browser to support listener
|
|
51034
|
+
* removal
|
|
51035
|
+
*
|
|
51036
|
+
* @returns {Promise<void>}
|
|
51037
|
+
*/
|
|
51038
|
+
async function resize() {
|
|
51039
|
+
|
|
51040
|
+
const viewportWidth = this.calculateViewportWidth(this.referenceFrameList.length);
|
|
51041
|
+
|
|
51042
|
+
for (let referenceFrame of this.referenceFrameList) {
|
|
51043
|
+
|
|
51044
|
+
const index = this.referenceFrameList.indexOf(referenceFrame);
|
|
51045
|
+
|
|
51046
|
+
const {chr, genome} = referenceFrame;
|
|
51047
|
+
|
|
51048
|
+
const {bpLength} = genome.getChromosome(referenceFrame.chr);
|
|
51049
|
+
|
|
51050
|
+
const viewportWidthBP = referenceFrame.toBP(viewportWidth);
|
|
51051
|
+
|
|
51052
|
+
// viewportWidthBP > bpLength occurs when locus is full chromosome and user widens browser
|
|
51053
|
+
if (GenomeUtils.isWholeGenomeView(chr) || viewportWidthBP > bpLength) {
|
|
51054
|
+
// console.log(`${ Date.now() } Recalc referenceFrame(${ index }) bpp. viewport ${ StringUtils.numberFormatter(viewportWidthBP) } > ${ StringUtils.numberFormatter(bpLength) }.`)
|
|
51055
|
+
referenceFrame.bpPerPixel = bpLength / viewportWidth;
|
|
51056
|
+
} else {
|
|
51057
|
+
// console.log(`${ Date.now() } Recalc referenceFrame(${ index }) end.`)
|
|
51058
|
+
referenceFrame.end = referenceFrame.start + referenceFrame.toBP(viewportWidth);
|
|
51059
|
+
}
|
|
51060
|
+
|
|
51061
|
+
for (let {viewports} of this.trackViews) {
|
|
51062
|
+
viewports[index].setWidth(viewportWidth);
|
|
51063
|
+
}
|
|
51064
|
+
|
|
51065
|
+
}
|
|
51066
|
+
|
|
51067
|
+
this.updateUIWithReferenceFrameList();
|
|
51068
|
+
|
|
51069
|
+
//TODO -- update view only if needed. Reducing size never needed. Increasing size maybe
|
|
51070
|
+
|
|
51071
|
+
await this.updateViews(true);
|
|
51072
|
+
}
|
|
51073
|
+
|
|
51074
|
+
|
|
51200
51075
|
function handleMouseMove(e) {
|
|
51201
51076
|
|
|
51202
51077
|
e.preventDefault();
|