igv 3.0.1 → 3.0.3
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 +1804 -1753
- package/dist/igv.esm.min.js +10 -10
- package/dist/igv.esm.min.js.map +1 -1
- package/dist/igv.js +1804 -1753
- package/dist/igv.min.js +10 -10
- package/dist/igv.min.js.map +1 -1
- package/package.json +1 -1
package/dist/igv.js
CHANGED
|
@@ -19021,13 +19021,13 @@
|
|
|
19021
19021
|
if (Array.isArray(trackViewOrTrackViewList)) {
|
|
19022
19022
|
dataRange = { min: Number.MAX_SAFE_INTEGER, max:-Number.MAX_SAFE_INTEGER };
|
|
19023
19023
|
for (const trackView of trackViewOrTrackViewList) {
|
|
19024
|
-
if (trackView.dataRange) {
|
|
19025
|
-
dataRange.min = Math.min(trackView.dataRange.min, dataRange.min);
|
|
19026
|
-
dataRange.max = Math.max(trackView.dataRange.max, dataRange.max);
|
|
19024
|
+
if (trackView.track.dataRange) {
|
|
19025
|
+
dataRange.min = Math.min(trackView.track.dataRange.min, dataRange.min);
|
|
19026
|
+
dataRange.max = Math.max(trackView.track.dataRange.max, dataRange.max);
|
|
19027
19027
|
}
|
|
19028
19028
|
}
|
|
19029
19029
|
} else {
|
|
19030
|
-
dataRange = trackViewOrTrackViewList.dataRange;
|
|
19030
|
+
dataRange = trackViewOrTrackViewList.track.dataRange;
|
|
19031
19031
|
}
|
|
19032
19032
|
|
|
19033
19033
|
if (dataRange) {
|
|
@@ -19069,7 +19069,7 @@
|
|
|
19069
19069
|
} else {
|
|
19070
19070
|
const list = Array.isArray(trackViewOfTrackViewList) ? trackViewOfTrackViewList : [ trackViewOfTrackViewList ];
|
|
19071
19071
|
for (const trackView of list) {
|
|
19072
|
-
trackView.
|
|
19072
|
+
trackView.track.setDataRange({ min, max });
|
|
19073
19073
|
}
|
|
19074
19074
|
|
|
19075
19075
|
}
|
|
@@ -26702,6 +26702,14 @@
|
|
|
26702
26702
|
return menuItems
|
|
26703
26703
|
}
|
|
26704
26704
|
|
|
26705
|
+
setDataRange({ min, max }) {
|
|
26706
|
+
|
|
26707
|
+
this.dataRange = { min, max };
|
|
26708
|
+
this.autoscale = false;
|
|
26709
|
+
this.autoscaleGroup = undefined;
|
|
26710
|
+
this.trackView.repaintViews();
|
|
26711
|
+
}
|
|
26712
|
+
|
|
26705
26713
|
/**
|
|
26706
26714
|
* Return the first feature in this track whose start position is > position
|
|
26707
26715
|
* @param chr
|
|
@@ -27069,7 +27077,7 @@
|
|
|
27069
27077
|
this.filter = tokens[6];
|
|
27070
27078
|
this.info = {};
|
|
27071
27079
|
const infoStr = tokens[7];
|
|
27072
|
-
if (infoStr) {
|
|
27080
|
+
if (infoStr && infoStr !== '.') {
|
|
27073
27081
|
for (let elem of infoStr.split(';')) {
|
|
27074
27082
|
var element = elem.split('=');
|
|
27075
27083
|
this.info[element[0]] = element[1];
|
|
@@ -27228,9 +27236,10 @@
|
|
|
27228
27236
|
}
|
|
27229
27237
|
}
|
|
27230
27238
|
|
|
27231
|
-
|
|
27239
|
+
const infoKeys = Object.keys(this.info);
|
|
27240
|
+
if (this.info && infoKeys.length > 0) {
|
|
27232
27241
|
fields.push({html: '<hr style="border-top: dotted 1px;border-color: #c9c3ba" />'});
|
|
27233
|
-
for (let key of
|
|
27242
|
+
for (let key of infoKeys) {
|
|
27234
27243
|
fields.push({name: key, value: arrayToString(decodeURIComponent(this.info[key]))});
|
|
27235
27244
|
}
|
|
27236
27245
|
}
|
|
@@ -27297,7 +27306,7 @@
|
|
|
27297
27306
|
}
|
|
27298
27307
|
|
|
27299
27308
|
getAttributeValue(key) {
|
|
27300
|
-
|
|
27309
|
+
return this.mate.getAttributeValue(key)
|
|
27301
27310
|
}
|
|
27302
27311
|
|
|
27303
27312
|
getInfo(tag) {
|
|
@@ -27320,7 +27329,7 @@
|
|
|
27320
27329
|
popupData.push({name: 'Pos', value: `${numberFormatter$1(this.pos)}`});
|
|
27321
27330
|
popupData.push({html: '<hr style="border-top: dotted 1px;border-color: #c9c3ba" />'});
|
|
27322
27331
|
popupData.push("SV");
|
|
27323
|
-
popupData.push(...
|
|
27332
|
+
popupData.push(...this.mate.popupData(genomicLocation, genomeId));
|
|
27324
27333
|
|
|
27325
27334
|
return popupData
|
|
27326
27335
|
}
|
|
@@ -30741,346 +30750,746 @@
|
|
|
30741
30750
|
|
|
30742
30751
|
}
|
|
30743
30752
|
|
|
30744
|
-
|
|
30745
|
-
|
|
30746
|
-
/**
|
|
30747
|
-
* feature source for "bed like" files (tab or whitespace delimited files with 1 feature per line: bed, gff, vcf, etc)
|
|
30753
|
+
/*
|
|
30754
|
+
* The MIT License (MIT)
|
|
30748
30755
|
*
|
|
30749
|
-
*
|
|
30750
|
-
*
|
|
30756
|
+
* Copyright (c) 2016 University of California San Diego
|
|
30757
|
+
* Author: Jim Robinson
|
|
30758
|
+
*
|
|
30759
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
30760
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
30761
|
+
* in the Software without restriction, including without limitation the rights
|
|
30762
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
30763
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
30764
|
+
* furnished to do so, subject to the following conditions:
|
|
30765
|
+
*
|
|
30766
|
+
* The above copyright notice and this permission notice shall be included in
|
|
30767
|
+
* all copies or substantial portions of the Software.
|
|
30768
|
+
*
|
|
30769
|
+
*
|
|
30770
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
30771
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
30772
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
30773
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
30774
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
30775
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
30776
|
+
* THE SOFTWARE.
|
|
30751
30777
|
*/
|
|
30752
|
-
class TextFeatureSource extends BaseFeatureSource {
|
|
30753
30778
|
|
|
30754
|
-
|
|
30779
|
+
const GZIP_FLAG = 0x1;
|
|
30755
30780
|
|
|
30756
|
-
|
|
30781
|
+
class TDFReader {
|
|
30757
30782
|
|
|
30758
|
-
|
|
30783
|
+
constructor(config, genome) {
|
|
30784
|
+
this.config = config;
|
|
30759
30785
|
this.genome = genome;
|
|
30760
|
-
this.
|
|
30761
|
-
this.
|
|
30762
|
-
this.
|
|
30786
|
+
this.path = config.url;
|
|
30787
|
+
this.groupCache = {};
|
|
30788
|
+
this.datasetCache = {};
|
|
30789
|
+
}
|
|
30763
30790
|
|
|
30764
|
-
const queryableFormats = new Set(["bigwig", "bw", "bigbed", "bb", "biginteract", "biggenepred", "bignarrowpeak", "tdf"]);
|
|
30765
30791
|
|
|
30766
|
-
|
|
30767
|
-
|
|
30768
|
-
|
|
30769
|
-
this
|
|
30770
|
-
this.queryable = config.queryable !== false;
|
|
30771
|
-
} else if (config.sourceType === "ga4gh") {
|
|
30772
|
-
throw Error("Unsupported source type 'ga4gh'")
|
|
30773
|
-
} else if ((config.type === "eqtl" || config.type === "qtl") && config.sourceType === "gtex-ws") {
|
|
30774
|
-
this.reader = new GtexReader(config);
|
|
30775
|
-
this.queryable = true;
|
|
30776
|
-
} else if ("htsget" === config.sourceType) {
|
|
30777
|
-
this.reader = new HtsgetVariantReader(config, genome);
|
|
30778
|
-
this.queryable = true;
|
|
30779
|
-
} else if (config.sourceType === 'ucscservice') {
|
|
30780
|
-
this.reader = new UCSCServiceReader(config.source);
|
|
30781
|
-
this.queryable = true;
|
|
30782
|
-
} else if (config.sourceType === 'custom') {
|
|
30783
|
-
this.reader = new CustomServiceReader(config.source);
|
|
30784
|
-
this.queryable = false !== config.source.queryable;
|
|
30785
|
-
} else if ('service' === config.sourceType) {
|
|
30786
|
-
this.reader = new FeatureFileReader(config, genome);
|
|
30787
|
-
this.queryable = true;
|
|
30788
|
-
} else {
|
|
30789
|
-
// File of some type (i.e. not a webservice)
|
|
30790
|
-
this.reader = new FeatureFileReader(config, genome);
|
|
30791
|
-
if (config.queryable !== undefined) {
|
|
30792
|
-
this.queryable = config.queryable;
|
|
30793
|
-
} else if (queryableFormats.has(config.format) || this.reader.indexed) {
|
|
30794
|
-
this.queryable = true;
|
|
30795
|
-
} else ;
|
|
30792
|
+
async readHeader() {
|
|
30793
|
+
|
|
30794
|
+
if (this.magic !== undefined) {
|
|
30795
|
+
return this // Already read
|
|
30796
30796
|
}
|
|
30797
30797
|
|
|
30798
|
-
|
|
30799
|
-
|
|
30798
|
+
let data = await igvxhr.loadArrayBuffer(this.path, buildOptions(this.config, {range: {start: 0, size: 64000}}));
|
|
30799
|
+
let binaryParser = new BinaryParser$1(new DataView(data));
|
|
30800
|
+
this.magic = binaryParser.getInt();
|
|
30801
|
+
this.version = binaryParser.getInt();
|
|
30802
|
+
this.indexPos = binaryParser.getLong();
|
|
30803
|
+
this.indexSize = binaryParser.getInt();
|
|
30804
|
+
binaryParser.getInt();
|
|
30800
30805
|
|
|
30801
|
-
}
|
|
30802
30806
|
|
|
30803
|
-
|
|
30804
|
-
|
|
30805
|
-
|
|
30807
|
+
if (this.version >= 2) {
|
|
30808
|
+
let nWindowFunctions = binaryParser.getInt();
|
|
30809
|
+
this.windowFunctions = [];
|
|
30810
|
+
while (nWindowFunctions-- > 0) {
|
|
30811
|
+
this.windowFunctions.push(binaryParser.getString());
|
|
30812
|
+
}
|
|
30806
30813
|
}
|
|
30807
|
-
}
|
|
30808
30814
|
|
|
30809
|
-
|
|
30810
|
-
|
|
30811
|
-
if (header) {
|
|
30812
|
-
return header.type
|
|
30813
|
-
} else {
|
|
30814
|
-
return undefined // Convention for unknown or unspecified
|
|
30815
|
-
}
|
|
30816
|
-
}
|
|
30815
|
+
this.trackType = binaryParser.getString();
|
|
30816
|
+
this.trackLine = binaryParser.getString();
|
|
30817
30817
|
|
|
30818
|
-
|
|
30819
|
-
|
|
30818
|
+
let nTracks = binaryParser.getInt();
|
|
30819
|
+
this.trackNames = [];
|
|
30820
|
+
while (nTracks-- > 0) {
|
|
30821
|
+
this.trackNames.push(binaryParser.getString());
|
|
30822
|
+
}
|
|
30823
|
+
this.genomeID = binaryParser.getString();
|
|
30824
|
+
this.flags = binaryParser.getInt();
|
|
30825
|
+
this.compressed = (this.flags & GZIP_FLAG) !== 0;
|
|
30820
30826
|
|
|
30821
|
-
|
|
30822
|
-
|
|
30823
|
-
|
|
30824
|
-
|
|
30825
|
-
|
|
30826
|
-
this.config.format = header.format;
|
|
30827
|
-
}
|
|
30828
|
-
} else {
|
|
30829
|
-
this.header = {};
|
|
30830
|
-
}
|
|
30831
|
-
} else {
|
|
30832
|
-
this.header = {};
|
|
30827
|
+
// Now read index
|
|
30828
|
+
data = await igvxhr.loadArrayBuffer(this.path, buildOptions(this.config, {
|
|
30829
|
+
range: {
|
|
30830
|
+
start: this.indexPos,
|
|
30831
|
+
size: this.indexSize
|
|
30833
30832
|
}
|
|
30833
|
+
}));
|
|
30834
|
+
binaryParser = new BinaryParser$1(new DataView(data));
|
|
30835
|
+
this.datasetIndex = {};
|
|
30836
|
+
let nEntries = binaryParser.getInt();
|
|
30837
|
+
while (nEntries-- > 0) {
|
|
30838
|
+
const name = binaryParser.getString();
|
|
30839
|
+
const pos = binaryParser.getLong();
|
|
30840
|
+
const size = binaryParser.getInt();
|
|
30841
|
+
this.datasetIndex[name] = {position: pos, size: size};
|
|
30834
30842
|
}
|
|
30835
|
-
|
|
30843
|
+
|
|
30844
|
+
this.groupIndex = {};
|
|
30845
|
+
nEntries = binaryParser.getInt();
|
|
30846
|
+
while (nEntries-- > 0) {
|
|
30847
|
+
const name = binaryParser.getString();
|
|
30848
|
+
const pos = binaryParser.getLong();
|
|
30849
|
+
const size = binaryParser.getInt();
|
|
30850
|
+
this.groupIndex[name] = {position: pos, size: size};
|
|
30851
|
+
}
|
|
30852
|
+
|
|
30853
|
+
return this
|
|
30836
30854
|
}
|
|
30837
30855
|
|
|
30838
|
-
|
|
30839
|
-
* Required function for all data source objects. Fetches features for the
|
|
30840
|
-
* range requested.
|
|
30841
|
-
*
|
|
30842
|
-
* This function is quite complex due to the variety of reader types backing it, some indexed, some queryable,
|
|
30843
|
-
* some not.
|
|
30844
|
-
*
|
|
30845
|
-
* @param chr
|
|
30846
|
-
* @param start
|
|
30847
|
-
* @param end
|
|
30848
|
-
* @param bpPerPixel
|
|
30849
|
-
*/
|
|
30850
|
-
async getFeatures({chr, start, end, bpPerPixel, visibilityWindow}) {
|
|
30856
|
+
async readDataset(chr, windowFunction, zoom) {
|
|
30851
30857
|
|
|
30852
|
-
const
|
|
30858
|
+
const key = chr + "_" + windowFunction + "_" + zoom;
|
|
30853
30859
|
|
|
30854
|
-
|
|
30855
|
-
|
|
30860
|
+
if (this.datasetCache[key]) {
|
|
30861
|
+
return this.datasetCache[key]
|
|
30856
30862
|
|
|
30857
|
-
|
|
30858
|
-
|
|
30859
|
-
|
|
30860
|
-
|
|
30861
|
-
// const containsRange = this.featureCache.containsRange(new GenomicInterval(queryChr, start, end))
|
|
30862
|
-
if ((isWholeGenome && !this.wgFeatures && this.supportsWholeGenome()) ||
|
|
30863
|
-
this.config.disableCache ||
|
|
30864
|
-
!this.featureCache ||
|
|
30865
|
-
!this.featureCache.containsRange(new GenomicInterval(chr, start, end))) {
|
|
30866
|
-
await this.loadFeatures(chr, start, end, visibilityWindow);
|
|
30867
|
-
}
|
|
30863
|
+
} else {
|
|
30864
|
+
await this.readHeader();
|
|
30865
|
+
const wf = (this.version < 2) ? "" : "/" + windowFunction;
|
|
30866
|
+
const zoomString = (chr.toLowerCase() === "all" || zoom === undefined) ? "0" : zoom.toString();
|
|
30868
30867
|
|
|
30869
|
-
|
|
30870
|
-
if (
|
|
30871
|
-
|
|
30872
|
-
|
|
30873
|
-
|
|
30874
|
-
|
|
30868
|
+
let dsName;
|
|
30869
|
+
if (windowFunction === "raw") {
|
|
30870
|
+
dsName = "/" + chr + "/raw";
|
|
30871
|
+
} else {
|
|
30872
|
+
dsName = "/" + chr + "/z" + zoomString + wf;
|
|
30873
|
+
}
|
|
30874
|
+
const indexEntry = this.datasetIndex[dsName];
|
|
30875
|
+
|
|
30876
|
+
if (indexEntry === undefined) {
|
|
30877
|
+
return undefined
|
|
30878
|
+
}
|
|
30879
|
+
|
|
30880
|
+
const data = await igvxhr.loadArrayBuffer(this.path, buildOptions(this.config, {
|
|
30881
|
+
range: {
|
|
30882
|
+
start: indexEntry.position,
|
|
30883
|
+
size: indexEntry.size
|
|
30875
30884
|
}
|
|
30885
|
+
}));
|
|
30886
|
+
|
|
30887
|
+
if (!data) {
|
|
30888
|
+
return undefined
|
|
30876
30889
|
}
|
|
30877
|
-
return this.wgFeatures
|
|
30878
|
-
} else {
|
|
30879
|
-
return this.featureCache.queryFeatures(chr, start, end)
|
|
30880
|
-
}
|
|
30881
|
-
}
|
|
30882
30890
|
|
|
30883
|
-
|
|
30884
|
-
|
|
30885
|
-
|
|
30891
|
+
const binaryParser = new BinaryParser$1(new DataView(data));
|
|
30892
|
+
let nAttributes = binaryParser.getInt();
|
|
30893
|
+
const attributes = {};
|
|
30894
|
+
while (nAttributes-- > 0) {
|
|
30895
|
+
attributes[binaryParser.getString()] = binaryParser.getString();
|
|
30896
|
+
}
|
|
30897
|
+
const dataType = binaryParser.getString();
|
|
30898
|
+
const tileWidth = binaryParser.getFloat();
|
|
30899
|
+
let nTiles = binaryParser.getInt();
|
|
30900
|
+
const tiles = [];
|
|
30901
|
+
while (nTiles-- > 0) {
|
|
30902
|
+
tiles.push({position: binaryParser.getLong(), size: binaryParser.getInt()});
|
|
30903
|
+
}
|
|
30886
30904
|
|
|
30887
|
-
|
|
30888
|
-
|
|
30889
|
-
|
|
30905
|
+
const dataset = {
|
|
30906
|
+
name: dsName,
|
|
30907
|
+
attributes: attributes,
|
|
30908
|
+
dataType: dataType,
|
|
30909
|
+
tileWidth: tileWidth,
|
|
30910
|
+
tiles: tiles
|
|
30911
|
+
};
|
|
30890
30912
|
|
|
30891
|
-
|
|
30892
|
-
|
|
30893
|
-
if (this.queryable || !this.featureCache) { // queryable sources don't support all features
|
|
30894
|
-
return []
|
|
30895
|
-
} else {
|
|
30896
|
-
return this.featureCache.getAllFeatures()
|
|
30913
|
+
this.datasetCache[key] = dataset;
|
|
30914
|
+
return dataset
|
|
30897
30915
|
}
|
|
30898
30916
|
}
|
|
30899
30917
|
|
|
30918
|
+
async readRootGroup() {
|
|
30900
30919
|
|
|
30901
|
-
|
|
30920
|
+
const genome = this.genome;
|
|
30921
|
+
const rootGroup = this.groupCache["/"];
|
|
30922
|
+
if (rootGroup) {
|
|
30923
|
+
return rootGroup
|
|
30924
|
+
} else {
|
|
30902
30925
|
|
|
30903
|
-
|
|
30926
|
+
const group = await this.readGroup("/");
|
|
30927
|
+
const names = group["chromosomes"];
|
|
30928
|
+
const maxZoomString = group["maxZoom"];
|
|
30904
30929
|
|
|
30905
|
-
|
|
30906
|
-
|
|
30907
|
-
|
|
30930
|
+
// Now parse out interesting attributes.
|
|
30931
|
+
if (maxZoomString) {
|
|
30932
|
+
this.maxZoom = Number(maxZoomString);
|
|
30933
|
+
}
|
|
30908
30934
|
|
|
30909
|
-
|
|
30910
|
-
|
|
30911
|
-
|
|
30912
|
-
|
|
30913
|
-
}
|
|
30914
|
-
if (this.chrAliasManager) {
|
|
30915
|
-
queryChr = await this.chrAliasManager.getAliasName(chr);
|
|
30916
|
-
}
|
|
30935
|
+
const totalCountString = group["totalCount"];
|
|
30936
|
+
if (totalCountString) {
|
|
30937
|
+
group.totalCount = Number(totalCountString);
|
|
30938
|
+
}
|
|
30917
30939
|
|
|
30918
|
-
|
|
30919
|
-
|
|
30920
|
-
|
|
30921
|
-
|
|
30922
|
-
|
|
30923
|
-
|
|
30924
|
-
|
|
30925
|
-
intervalEnd = Math.max(chromosome ? chromosome.bpLength : Number.MAX_SAFE_INTEGER, end);
|
|
30926
|
-
} else if (visibilityWindow > (end - start) && this.config.expandQuery !== false) {
|
|
30927
|
-
let expansionWindow = Math.min(4.1 * (end - start), visibilityWindow);
|
|
30928
|
-
if(this.config.minQuerySize && expansionWindow < this.config.minQuerySize) {
|
|
30929
|
-
expansionWindow = this.config.minQuerySize;
|
|
30940
|
+
// Chromosome names
|
|
30941
|
+
const chrAliasTable = {};
|
|
30942
|
+
if (names) {
|
|
30943
|
+
names.split(",").forEach(function (chr) {
|
|
30944
|
+
const canonicalName = genome.getChromosomeName(chr);
|
|
30945
|
+
chrAliasTable[canonicalName] = chr;
|
|
30946
|
+
});
|
|
30930
30947
|
}
|
|
30931
|
-
|
|
30932
|
-
intervalEnd = intervalStart + expansionWindow;
|
|
30933
|
-
}
|
|
30948
|
+
this.chrAliasTable = chrAliasTable;
|
|
30934
30949
|
|
|
30935
|
-
|
|
30936
|
-
|
|
30937
|
-
this.queryable = reader.indexed;
|
|
30950
|
+
this.groupCache["/"] = group;
|
|
30951
|
+
return group
|
|
30938
30952
|
}
|
|
30953
|
+
}
|
|
30939
30954
|
|
|
30940
|
-
|
|
30941
|
-
new GenomicInterval(chr, intervalStart, intervalEnd) :
|
|
30942
|
-
undefined;
|
|
30955
|
+
async readGroup(name) {
|
|
30943
30956
|
|
|
30944
|
-
|
|
30957
|
+
const group = this.groupCache[name];
|
|
30958
|
+
if (group) {
|
|
30959
|
+
return group
|
|
30960
|
+
} else {
|
|
30945
30961
|
|
|
30946
|
-
|
|
30947
|
-
|
|
30948
|
-
|
|
30949
|
-
|
|
30962
|
+
await this.readHeader();
|
|
30963
|
+
const indexEntry = this.groupIndex[name];
|
|
30964
|
+
if (indexEntry === undefined) {
|
|
30965
|
+
return undefined
|
|
30950
30966
|
}
|
|
30951
30967
|
|
|
30952
|
-
|
|
30953
|
-
|
|
30968
|
+
const data = await igvxhr.loadArrayBuffer(this.path, buildOptions(this.config, {
|
|
30969
|
+
range: {
|
|
30970
|
+
start: indexEntry.position,
|
|
30971
|
+
size: indexEntry.size
|
|
30972
|
+
}
|
|
30973
|
+
}));
|
|
30954
30974
|
|
|
30955
|
-
|
|
30956
|
-
|
|
30957
|
-
this.addFeaturesToDB(features, this.config);
|
|
30975
|
+
if (!data) {
|
|
30976
|
+
return undefined
|
|
30958
30977
|
}
|
|
30959
|
-
} else {
|
|
30960
|
-
this.featureCache = new FeatureCache$1([], genomicInterval); // Empty cache
|
|
30961
|
-
}
|
|
30962
|
-
}
|
|
30963
30978
|
|
|
30964
|
-
|
|
30965
|
-
|
|
30966
|
-
|
|
30967
|
-
|
|
30968
|
-
|
|
30969
|
-
|
|
30970
|
-
|
|
30971
|
-
let key;
|
|
30972
|
-
if (typeof feature.getAttributeValue === 'function') {
|
|
30973
|
-
key = feature.getAttributeValue(field);
|
|
30974
|
-
}
|
|
30975
|
-
if (key) {
|
|
30976
|
-
key = key.replaceAll(' ', '+').toUpperCase();
|
|
30977
|
-
// If feature is already present keep largest one
|
|
30978
|
-
if (this.featureMap.has(key)) {
|
|
30979
|
-
const f2 = this.featureMap.get(key);
|
|
30980
|
-
if (feature.end - feature.start < f2.end - f2.start) {
|
|
30981
|
-
continue
|
|
30982
|
-
}
|
|
30983
|
-
}
|
|
30984
|
-
this.featureMap.set(key, feature);
|
|
30985
|
-
}
|
|
30979
|
+
const binaryParser = new BinaryParser$1(new DataView(data));
|
|
30980
|
+
const group = {name: name};
|
|
30981
|
+
let nAttributes = binaryParser.getInt();
|
|
30982
|
+
while (nAttributes-- > 0) {
|
|
30983
|
+
const key = binaryParser.getString();
|
|
30984
|
+
const value = binaryParser.getString();
|
|
30985
|
+
group[key] = value;
|
|
30986
30986
|
}
|
|
30987
|
+
this.groupCache[name] = group;
|
|
30988
|
+
return group
|
|
30987
30989
|
}
|
|
30988
30990
|
}
|
|
30989
30991
|
|
|
30990
|
-
|
|
30991
|
-
if (this.featureMap) {
|
|
30992
|
-
return this.featureMap.get(term.toUpperCase())
|
|
30993
|
-
}
|
|
30994
|
-
|
|
30995
|
-
}
|
|
30996
|
-
}
|
|
30997
|
-
|
|
30998
|
-
/**
|
|
30999
|
-
* A ChromTree parses a UCSC bigbed/bigwig "chromosomeTree" section of the header to produce 2 maps,
|
|
31000
|
-
* (1) ID -> chromosome names, and its
|
|
31001
|
-
* (2) chromsome name -> ID
|
|
31002
|
-
*
|
|
31003
|
-
* Both maps are needed by IGV
|
|
31004
|
-
*
|
|
31005
|
-
* The chromosome tree is a B+ index, but is located continguously in memory in the header section of the file. This
|
|
31006
|
-
* makes it feasible to parse the whole tree with data from a single fetch. In the end the tree is discarded
|
|
31007
|
-
* leaving only the mapps.
|
|
31008
|
-
*/
|
|
31009
|
-
class ChromTree {
|
|
30992
|
+
async readTiles(tileIndeces, nTracks) {
|
|
31010
30993
|
|
|
31011
|
-
|
|
31012
|
-
|
|
31013
|
-
|
|
31014
|
-
this.idToName = valueToKey;
|
|
31015
|
-
this.sumLengths = sumLengths;
|
|
31016
|
-
}
|
|
30994
|
+
tileIndeces.sort(function (a, b) {
|
|
30995
|
+
return a.position - b.position
|
|
30996
|
+
});
|
|
31017
30997
|
|
|
31018
|
-
|
|
31019
|
-
|
|
31020
|
-
|
|
31021
|
-
const blockSize = binaryParser.getInt();
|
|
31022
|
-
const keySize = binaryParser.getInt();
|
|
31023
|
-
const valSize = binaryParser.getInt();
|
|
31024
|
-
const itemCount = binaryParser.getLong();
|
|
31025
|
-
const reserved = binaryParser.getLong();
|
|
30998
|
+
tileIndeces = tileIndeces.filter(function (idx) {
|
|
30999
|
+
return idx.size > 0
|
|
31000
|
+
});
|
|
31026
31001
|
|
|
31027
|
-
|
|
31028
|
-
|
|
31029
|
-
|
|
31030
|
-
let sumLengths = 0;
|
|
31031
|
-
const readTreeNode = (offset) => {
|
|
31002
|
+
if (tileIndeces.length === 0) {
|
|
31003
|
+
return []
|
|
31004
|
+
}
|
|
31032
31005
|
|
|
31033
|
-
|
|
31034
|
-
const type = binaryParser.getByte();
|
|
31035
|
-
binaryParser.getByte();
|
|
31036
|
-
const count = binaryParser.getUShort();
|
|
31006
|
+
const tiles = [];
|
|
31037
31007
|
|
|
31038
|
-
|
|
31039
|
-
// Leaf node
|
|
31040
|
-
for (let i = 0; i < count; i++) {
|
|
31041
|
-
let key = binaryParser.getFixedLengthString(keySize);
|
|
31042
|
-
let value;
|
|
31043
|
-
if (valSize === 8) {
|
|
31044
|
-
value = binaryParser.getInt();
|
|
31045
|
-
const chromSize = binaryParser.getInt();
|
|
31046
|
-
sumLengths += chromSize;
|
|
31047
|
-
if (genome) key = genome.getChromosomeName(key); // Translate to canonical chr name
|
|
31048
|
-
nameToId.set(key, value);
|
|
31049
|
-
idToName[value] = key;
|
|
31008
|
+
for (let indexEntry of tileIndeces) {
|
|
31050
31009
|
|
|
31051
|
-
|
|
31052
|
-
|
|
31053
|
-
|
|
31054
|
-
|
|
31055
|
-
} else {
|
|
31056
|
-
// non-leaf
|
|
31057
|
-
for (let i = 0; i < count; i++) {
|
|
31058
|
-
binaryParser.getFixedLengthString(keySize);
|
|
31059
|
-
const childOffset = binaryParser.getLong();
|
|
31060
|
-
const bufferOffset = childOffset - startOffset;
|
|
31061
|
-
const currOffset = binaryParser.position;
|
|
31062
|
-
readTreeNode(bufferOffset);
|
|
31063
|
-
binaryParser.position = currOffset;
|
|
31064
|
-
}
|
|
31010
|
+
const data = await igvxhr.loadArrayBuffer(this.path, buildOptions(this.config, {
|
|
31011
|
+
range: {
|
|
31012
|
+
start: indexEntry.position,
|
|
31013
|
+
size: indexEntry.size
|
|
31065
31014
|
}
|
|
31066
|
-
};
|
|
31015
|
+
}));
|
|
31067
31016
|
|
|
31068
|
-
|
|
31069
|
-
|
|
31017
|
+
let tileData;
|
|
31018
|
+
try {
|
|
31019
|
+
tileData = this.compressed ? inflate_1$3(data).buffer : data;
|
|
31020
|
+
} catch (e) {
|
|
31021
|
+
console.error(e);
|
|
31022
|
+
continue
|
|
31023
|
+
}
|
|
31070
31024
|
|
|
31071
|
-
|
|
31025
|
+
const binaryParser = new BinaryParser$1(new DataView(tileData));
|
|
31026
|
+
const type = binaryParser.getString();
|
|
31027
|
+
let tile;
|
|
31028
|
+
switch (type) {
|
|
31029
|
+
case "fixedStep":
|
|
31030
|
+
tile = createFixedStep(binaryParser, nTracks);
|
|
31031
|
+
break
|
|
31032
|
+
case "variableStep":
|
|
31033
|
+
tile = createVariableStep(binaryParser, nTracks);
|
|
31034
|
+
break
|
|
31035
|
+
case "bed":
|
|
31036
|
+
case "bedWithName":
|
|
31037
|
+
tile = createBed(binaryParser, nTracks, type);
|
|
31038
|
+
break
|
|
31039
|
+
default:
|
|
31040
|
+
throw "Unknown tile type: " + type
|
|
31041
|
+
}
|
|
31042
|
+
tiles.push(tile);
|
|
31072
31043
|
}
|
|
31044
|
+
return tiles
|
|
31073
31045
|
}
|
|
31074
31046
|
|
|
31075
|
-
|
|
31076
|
-
|
|
31077
|
-
const RPTREE_HEADER_SIZE = 48;
|
|
31078
|
-
const RPTREE_NODE_LEAF_ITEM_SIZE = 32; // leaf item size
|
|
31079
|
-
const RPTREE_NODE_CHILD_ITEM_SIZE = 24; // child item size
|
|
31047
|
+
async readTile(indexEntry, nTracks) {
|
|
31080
31048
|
|
|
31081
|
-
|
|
31049
|
+
let data = await igvxhr.loadArrayBuffer(this.path, buildOptions(this.config, {
|
|
31050
|
+
range: {
|
|
31051
|
+
start: indexEntry.position,
|
|
31052
|
+
size: indexEntry.size
|
|
31053
|
+
}
|
|
31054
|
+
}));
|
|
31082
31055
|
|
|
31083
|
-
|
|
31056
|
+
if (this.compressed) {
|
|
31057
|
+
const plain = inflate_1$3(data);
|
|
31058
|
+
data = plain.buffer;
|
|
31059
|
+
}
|
|
31060
|
+
|
|
31061
|
+
const binaryParser = new BinaryParser$1(new DataView(data));
|
|
31062
|
+
const type = binaryParser.getString();
|
|
31063
|
+
switch (type) {
|
|
31064
|
+
case "fixedStep":
|
|
31065
|
+
return createFixedStep(binaryParser, nTracks)
|
|
31066
|
+
case "variableStep":
|
|
31067
|
+
return createVariableStep(binaryParser, nTracks)
|
|
31068
|
+
case "bed":
|
|
31069
|
+
case "bedWithName":
|
|
31070
|
+
return createBed(binaryParser, nTracks, type)
|
|
31071
|
+
default:
|
|
31072
|
+
throw "Unknown tile type: " + type
|
|
31073
|
+
}
|
|
31074
|
+
}
|
|
31075
|
+
|
|
31076
|
+
}
|
|
31077
|
+
|
|
31078
|
+
function createFixedStep(binaryParser, nTracks) {
|
|
31079
|
+
const nPositions = binaryParser.getInt();
|
|
31080
|
+
const start = binaryParser.getInt();
|
|
31081
|
+
const span = binaryParser.getFloat();
|
|
31082
|
+
|
|
31083
|
+
const data = [];
|
|
31084
|
+
let nt = nTracks;
|
|
31085
|
+
while (nt-- > 0) {
|
|
31086
|
+
let np = nPositions;
|
|
31087
|
+
const dtrack = [];
|
|
31088
|
+
while (np-- > 0) {
|
|
31089
|
+
dtrack.push(binaryParser.getFloat());
|
|
31090
|
+
}
|
|
31091
|
+
data.push(dtrack);
|
|
31092
|
+
}
|
|
31093
|
+
|
|
31094
|
+
return {
|
|
31095
|
+
type: "fixedStep",
|
|
31096
|
+
start: start,
|
|
31097
|
+
span: span,
|
|
31098
|
+
data: data,
|
|
31099
|
+
nTracks: nTracks,
|
|
31100
|
+
nPositions: nPositions
|
|
31101
|
+
}
|
|
31102
|
+
}
|
|
31103
|
+
|
|
31104
|
+
function createVariableStep(binaryParser, nTracks) {
|
|
31105
|
+
|
|
31106
|
+
const tileStart = binaryParser.getInt();
|
|
31107
|
+
const span = binaryParser.getFloat();
|
|
31108
|
+
const nPositions = binaryParser.getInt();
|
|
31109
|
+
const start = [];
|
|
31110
|
+
|
|
31111
|
+
let np = nPositions;
|
|
31112
|
+
while (np-- > 0) {
|
|
31113
|
+
start.push(binaryParser.getInt());
|
|
31114
|
+
}
|
|
31115
|
+
binaryParser.getInt(); // # of samples, ignored but should === nTracks
|
|
31116
|
+
|
|
31117
|
+
const data = [];
|
|
31118
|
+
let nt = nTracks;
|
|
31119
|
+
while (nt-- > 0) {
|
|
31120
|
+
np = nPositions;
|
|
31121
|
+
const dtrack = [];
|
|
31122
|
+
while (np-- > 0) {
|
|
31123
|
+
dtrack.push(binaryParser.getFloat());
|
|
31124
|
+
}
|
|
31125
|
+
data.push(dtrack);
|
|
31126
|
+
}
|
|
31127
|
+
|
|
31128
|
+
return {
|
|
31129
|
+
type: "variableStep",
|
|
31130
|
+
tileStart: tileStart,
|
|
31131
|
+
span: span,
|
|
31132
|
+
start: start,
|
|
31133
|
+
data: data,
|
|
31134
|
+
nTracks: nTracks,
|
|
31135
|
+
nPositions: nPositions
|
|
31136
|
+
}
|
|
31137
|
+
}
|
|
31138
|
+
|
|
31139
|
+
function createBed(binaryParser, nTracks, type) {
|
|
31140
|
+
|
|
31141
|
+
const nPositions = binaryParser.getInt();
|
|
31142
|
+
|
|
31143
|
+
let n = nPositions;
|
|
31144
|
+
const start = [];
|
|
31145
|
+
while (n-- > 0) {
|
|
31146
|
+
start.push(binaryParser.getInt());
|
|
31147
|
+
}
|
|
31148
|
+
|
|
31149
|
+
n = nPositions;
|
|
31150
|
+
const end = [];
|
|
31151
|
+
while (n-- > 0) {
|
|
31152
|
+
end.push(binaryParser.getInt());
|
|
31153
|
+
}
|
|
31154
|
+
|
|
31155
|
+
binaryParser.getInt(); // # of samples, ignored but should === nTracks
|
|
31156
|
+
const data = [];
|
|
31157
|
+
let nt = nTracks;
|
|
31158
|
+
while (nt-- > 0) {
|
|
31159
|
+
let np = nPositions;
|
|
31160
|
+
const dtrack = [];
|
|
31161
|
+
while (np-- > 0) {
|
|
31162
|
+
dtrack.push(binaryParser.getFloat());
|
|
31163
|
+
}
|
|
31164
|
+
data.push(dtrack);
|
|
31165
|
+
}
|
|
31166
|
+
|
|
31167
|
+
if (type === "bedWithName") {
|
|
31168
|
+
n = nPositions;
|
|
31169
|
+
const name = [];
|
|
31170
|
+
while (n-- > 0) {
|
|
31171
|
+
name.push(binaryParser.getString());
|
|
31172
|
+
}
|
|
31173
|
+
}
|
|
31174
|
+
|
|
31175
|
+
return {
|
|
31176
|
+
type: type,
|
|
31177
|
+
start: start,
|
|
31178
|
+
end: end,
|
|
31179
|
+
data: data,
|
|
31180
|
+
nTracks: nTracks,
|
|
31181
|
+
nPositions: nPositions
|
|
31182
|
+
}
|
|
31183
|
+
}
|
|
31184
|
+
|
|
31185
|
+
/*
|
|
31186
|
+
* The MIT License (MIT)
|
|
31187
|
+
*
|
|
31188
|
+
* Copyright (c) 2016 University of California San Diego
|
|
31189
|
+
* Author: Jim Robinson
|
|
31190
|
+
*
|
|
31191
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
31192
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
31193
|
+
* in the Software without restriction, including without limitation the rights
|
|
31194
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
31195
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
31196
|
+
* furnished to do so, subject to the following conditions:
|
|
31197
|
+
*
|
|
31198
|
+
* The above copyright notice and this permission notice shall be included in
|
|
31199
|
+
* all copies or substantial portions of the Software.
|
|
31200
|
+
*
|
|
31201
|
+
*
|
|
31202
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
31203
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
31204
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
31205
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
31206
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
31207
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
31208
|
+
* THE SOFTWARE.
|
|
31209
|
+
*/
|
|
31210
|
+
|
|
31211
|
+
class TDFSource extends BaseFeatureSource {
|
|
31212
|
+
|
|
31213
|
+
#wgValues = {}
|
|
31214
|
+
searchable = false
|
|
31215
|
+
|
|
31216
|
+
|
|
31217
|
+
constructor(config, genome) {
|
|
31218
|
+
super(genome);
|
|
31219
|
+
this.genome = genome;
|
|
31220
|
+
this.reader = new TDFReader(config, genome);
|
|
31221
|
+
this.queryable = true;
|
|
31222
|
+
}
|
|
31223
|
+
|
|
31224
|
+
async getFeatures({chr, start, end, bpPerPixel, windowFunction = "mean"}) {
|
|
31225
|
+
|
|
31226
|
+
if (chr.toLowerCase() === "all") {
|
|
31227
|
+
return this.getWGValues(windowFunction, bpPerPixel)
|
|
31228
|
+
} else {
|
|
31229
|
+
return this._getFeatures(chr, start, end, bpPerPixel, windowFunction)
|
|
31230
|
+
}
|
|
31231
|
+
}
|
|
31232
|
+
async _getFeatures(chr, start, end, bpPerPixel, windowFunction) {
|
|
31233
|
+
const genomicInterval = new GenomicInterval(chr, start, end);
|
|
31234
|
+
const genome = this.genome;
|
|
31235
|
+
|
|
31236
|
+
|
|
31237
|
+
if (!this.rootGroup) {
|
|
31238
|
+
this.rootGroup = await this.reader.readRootGroup();
|
|
31239
|
+
if (!this.normalizationFactor) {
|
|
31240
|
+
const totalCount = this.rootGroup.totalCount;
|
|
31241
|
+
if (totalCount) {
|
|
31242
|
+
this.normalizationFactor = 1.0e6 / totalCount;
|
|
31243
|
+
}
|
|
31244
|
+
}
|
|
31245
|
+
}
|
|
31246
|
+
|
|
31247
|
+
genomicInterval.bpPerPixel = bpPerPixel;
|
|
31248
|
+
const zoom = zoomLevelForScale$1(chr, bpPerPixel, genome);
|
|
31249
|
+
let queryChr = this.reader.chrAliasTable[chr];
|
|
31250
|
+
let maxZoom = this.reader.maxZoom;
|
|
31251
|
+
if (queryChr === undefined) queryChr = chr;
|
|
31252
|
+
if (maxZoom === undefined) maxZoom = -1;
|
|
31253
|
+
|
|
31254
|
+
const wf = zoom > maxZoom ? "raw" : windowFunction;
|
|
31255
|
+
const dataset = await this.reader.readDataset(queryChr, wf, zoom);
|
|
31256
|
+
if (dataset == null) {
|
|
31257
|
+
return []
|
|
31258
|
+
}
|
|
31259
|
+
|
|
31260
|
+
const tileWidth = dataset.tileWidth;
|
|
31261
|
+
const startTile = Math.floor(start / tileWidth);
|
|
31262
|
+
const endTile = Math.floor(end / tileWidth);
|
|
31263
|
+
const NTRACKS = 1; // TODO read this
|
|
31264
|
+
const tiles = await this.reader.readTiles(dataset.tiles.slice(startTile, endTile + 1), NTRACKS);
|
|
31265
|
+
const features = [];
|
|
31266
|
+
for (let tile of tiles) {
|
|
31267
|
+
switch (tile.type) {
|
|
31268
|
+
case "bed":
|
|
31269
|
+
decodeBedTile(tile, chr, start, end, bpPerPixel, features);
|
|
31270
|
+
break
|
|
31271
|
+
case "variableStep":
|
|
31272
|
+
decodeVaryTile(tile, chr, start, end, bpPerPixel, features);
|
|
31273
|
+
break
|
|
31274
|
+
case "fixedStep":
|
|
31275
|
+
decodeFixedTile(tile, chr, start, end, bpPerPixel, features);
|
|
31276
|
+
break
|
|
31277
|
+
default:
|
|
31278
|
+
throw ("Unknown tile type: " + tile.type)
|
|
31279
|
+
}
|
|
31280
|
+
}
|
|
31281
|
+
features.sort(function (a, b) {
|
|
31282
|
+
return a.start - b.start
|
|
31283
|
+
});
|
|
31284
|
+
|
|
31285
|
+
return features
|
|
31286
|
+
}
|
|
31287
|
+
|
|
31288
|
+
get supportsWholeGenome() {
|
|
31289
|
+
return true
|
|
31290
|
+
}
|
|
31291
|
+
|
|
31292
|
+
get windowFunctions() {
|
|
31293
|
+
return this.reader.windowFunctions
|
|
31294
|
+
}
|
|
31295
|
+
|
|
31296
|
+
async getWGValues(windowFunction, bpPerPixel) {
|
|
31297
|
+
|
|
31298
|
+
const cached = this.#wgValues[windowFunction];
|
|
31299
|
+
if (cached && cached.bpPerPixel > 0.8 * bpPerPixel && cached.bpPerPixel < 1.2 * bpPerPixel) {
|
|
31300
|
+
return cached.values
|
|
31301
|
+
} else {
|
|
31302
|
+
const wgFeatures = [];
|
|
31303
|
+
const genome = this.genome;
|
|
31304
|
+
const chrNames = this.genome.wgChromosomeNames;
|
|
31305
|
+
if (chrNames) {
|
|
31306
|
+
for (let c of genome.wgChromosomeNames) {
|
|
31307
|
+
const len = genome.getChromosome(c).bpLength;
|
|
31308
|
+
bpPerPixel = len / 1000;
|
|
31309
|
+
const chrFeatures = await this._getFeatures(c, 0, len, bpPerPixel, windowFunction);
|
|
31310
|
+
if (chrFeatures) {
|
|
31311
|
+
for (let f of chrFeatures) {
|
|
31312
|
+
const wg = Object.assign({}, f);
|
|
31313
|
+
wg.chr = "all";
|
|
31314
|
+
wg.start = genome.getGenomeCoordinate(f.chr, f.start);
|
|
31315
|
+
wg.end = genome.getGenomeCoordinate(f.chr, f.end);
|
|
31316
|
+
wg._f = f;
|
|
31317
|
+
wgFeatures.push(wg);
|
|
31318
|
+
}
|
|
31319
|
+
}
|
|
31320
|
+
}
|
|
31321
|
+
}
|
|
31322
|
+
this.#wgValues[windowFunction] = {values: wgFeatures, bpPerPixel};
|
|
31323
|
+
return wgFeatures
|
|
31324
|
+
}
|
|
31325
|
+
}
|
|
31326
|
+
|
|
31327
|
+
}
|
|
31328
|
+
|
|
31329
|
+
function decodeBedTile(tile, chr, bpStart, bpEnd, bpPerPixel, features) {
|
|
31330
|
+
|
|
31331
|
+
const nPositions = tile.nPositions;
|
|
31332
|
+
const starts = tile.start;
|
|
31333
|
+
const ends = tile.end;
|
|
31334
|
+
const data = tile.data[0]; // Single track for now
|
|
31335
|
+
for (let i = 0; i < nPositions; i++) {
|
|
31336
|
+
const s = starts[i];
|
|
31337
|
+
const e = ends[i];
|
|
31338
|
+
if (e < bpStart) continue
|
|
31339
|
+
if (s > bpEnd) break
|
|
31340
|
+
features.push({
|
|
31341
|
+
chr: chr,
|
|
31342
|
+
start: s,
|
|
31343
|
+
end: e,
|
|
31344
|
+
value: data[i]
|
|
31345
|
+
});
|
|
31346
|
+
}
|
|
31347
|
+
}
|
|
31348
|
+
|
|
31349
|
+
function decodeVaryTile(tile, chr, bpStart, bpEnd, bpPerPixel, features) {
|
|
31350
|
+
|
|
31351
|
+
const nPositions = tile.nPositions;
|
|
31352
|
+
const starts = tile.start;
|
|
31353
|
+
const span = tile.span;
|
|
31354
|
+
const data = tile.data[0]; // Single track for now
|
|
31355
|
+
for (let i = 0; i < nPositions; i++) {
|
|
31356
|
+
const s = starts[i];
|
|
31357
|
+
const e = s + span;
|
|
31358
|
+
if (e < bpStart) continue
|
|
31359
|
+
if (s > bpEnd) break
|
|
31360
|
+
features.push({
|
|
31361
|
+
chr: chr,
|
|
31362
|
+
start: s,
|
|
31363
|
+
end: e,
|
|
31364
|
+
value: data[i]
|
|
31365
|
+
});
|
|
31366
|
+
}
|
|
31367
|
+
}
|
|
31368
|
+
|
|
31369
|
+
function decodeFixedTile(tile, chr, bpStart, bpEnd, bpPerPixel, features) {
|
|
31370
|
+
|
|
31371
|
+
const nPositions = tile.nPositions;
|
|
31372
|
+
let s = tile.start;
|
|
31373
|
+
const span = tile.span;
|
|
31374
|
+
const data = tile.data[0]; // Single track for now
|
|
31375
|
+
|
|
31376
|
+
for (let i = 0; i < nPositions; i++) {
|
|
31377
|
+
const e = s + span;
|
|
31378
|
+
if (s > bpEnd) break
|
|
31379
|
+
if (e >= bpStart) {
|
|
31380
|
+
if (!Number.isNaN(data[i])) {
|
|
31381
|
+
features.push({
|
|
31382
|
+
chr: chr,
|
|
31383
|
+
start: s,
|
|
31384
|
+
end: e,
|
|
31385
|
+
value: data[i]
|
|
31386
|
+
});
|
|
31387
|
+
}
|
|
31388
|
+
}
|
|
31389
|
+
s = e;
|
|
31390
|
+
}
|
|
31391
|
+
}
|
|
31392
|
+
|
|
31393
|
+
|
|
31394
|
+
var log2 = Math.log(2);
|
|
31395
|
+
|
|
31396
|
+
function zoomLevelForScale$1(chr, bpPerPixel, genome) {
|
|
31397
|
+
|
|
31398
|
+
// Convert bpPerPixel to IGV "zoom" level. This is a bit convoluted, TDF is computed zoom levels assuming
|
|
31399
|
+
// display in a 700 pixel window. The fully zoomed out view of a chromosome is zoom level "0".
|
|
31400
|
+
// Zoom level 1 is magnified 2X, and so forth
|
|
31401
|
+
|
|
31402
|
+
var chrSize = genome.getChromosome(chr).bpLength;
|
|
31403
|
+
|
|
31404
|
+
return Math.ceil(Math.log(Math.max(0, (chrSize / (bpPerPixel * 700)))) / log2)
|
|
31405
|
+
}
|
|
31406
|
+
|
|
31407
|
+
/**
|
|
31408
|
+
* A ChromTree parses a UCSC bigbed/bigwig "chromosomeTree" section of the header to produce 2 maps,
|
|
31409
|
+
* (1) ID -> chromosome names, and its
|
|
31410
|
+
* (2) chromsome name -> ID
|
|
31411
|
+
*
|
|
31412
|
+
* Both maps are needed by IGV
|
|
31413
|
+
*
|
|
31414
|
+
* The chromosome tree is a B+ index, but is located continguously in memory in the header section of the file. This
|
|
31415
|
+
* makes it feasible to parse the whole tree with data from a single fetch. In the end the tree is discarded
|
|
31416
|
+
* leaving only the mapps.
|
|
31417
|
+
*/
|
|
31418
|
+
class ChromTree {
|
|
31419
|
+
|
|
31420
|
+
constructor(header, nameToID, valueToKey, sumLengths) {
|
|
31421
|
+
this.header = header;
|
|
31422
|
+
this.nameToId = nameToID;
|
|
31423
|
+
this.idToName = valueToKey;
|
|
31424
|
+
this.sumLengths = sumLengths;
|
|
31425
|
+
}
|
|
31426
|
+
|
|
31427
|
+
static parseTree(binaryParser, startOffset, genome = false) {
|
|
31428
|
+
{
|
|
31429
|
+
const magic = binaryParser.getInt();
|
|
31430
|
+
const blockSize = binaryParser.getInt();
|
|
31431
|
+
const keySize = binaryParser.getInt();
|
|
31432
|
+
const valSize = binaryParser.getInt();
|
|
31433
|
+
const itemCount = binaryParser.getLong();
|
|
31434
|
+
const reserved = binaryParser.getLong();
|
|
31435
|
+
|
|
31436
|
+
const header = {magic, blockSize, keySize, valSize, itemCount, reserved};
|
|
31437
|
+
const nameToId = new Map();
|
|
31438
|
+
const idToName = [];
|
|
31439
|
+
let sumLengths = 0;
|
|
31440
|
+
const readTreeNode = (offset) => {
|
|
31441
|
+
|
|
31442
|
+
if (offset >= 0) binaryParser.position = offset;
|
|
31443
|
+
const type = binaryParser.getByte();
|
|
31444
|
+
binaryParser.getByte();
|
|
31445
|
+
const count = binaryParser.getUShort();
|
|
31446
|
+
|
|
31447
|
+
if (type === 1) {
|
|
31448
|
+
// Leaf node
|
|
31449
|
+
for (let i = 0; i < count; i++) {
|
|
31450
|
+
let key = binaryParser.getFixedLengthString(keySize);
|
|
31451
|
+
let value;
|
|
31452
|
+
if (valSize === 8) {
|
|
31453
|
+
value = binaryParser.getInt();
|
|
31454
|
+
const chromSize = binaryParser.getInt();
|
|
31455
|
+
sumLengths += chromSize;
|
|
31456
|
+
if (genome) key = genome.getChromosomeName(key); // Translate to canonical chr name
|
|
31457
|
+
nameToId.set(key, value);
|
|
31458
|
+
idToName[value] = key;
|
|
31459
|
+
|
|
31460
|
+
} else {
|
|
31461
|
+
throw Error(`Unexpected "valSize" value in chromosome tree. Expected 8, actual value is ${valSize}`)
|
|
31462
|
+
}
|
|
31463
|
+
}
|
|
31464
|
+
} else {
|
|
31465
|
+
// non-leaf
|
|
31466
|
+
for (let i = 0; i < count; i++) {
|
|
31467
|
+
binaryParser.getFixedLengthString(keySize);
|
|
31468
|
+
const childOffset = binaryParser.getLong();
|
|
31469
|
+
const bufferOffset = childOffset - startOffset;
|
|
31470
|
+
const currOffset = binaryParser.position;
|
|
31471
|
+
readTreeNode(bufferOffset);
|
|
31472
|
+
binaryParser.position = currOffset;
|
|
31473
|
+
}
|
|
31474
|
+
}
|
|
31475
|
+
};
|
|
31476
|
+
|
|
31477
|
+
// Recursively walk tree to populate dictionary
|
|
31478
|
+
readTreeNode(binaryParser);
|
|
31479
|
+
|
|
31480
|
+
return new ChromTree(header, nameToId, idToName, sumLengths)
|
|
31481
|
+
}
|
|
31482
|
+
}
|
|
31483
|
+
|
|
31484
|
+
}
|
|
31485
|
+
|
|
31486
|
+
const RPTREE_HEADER_SIZE = 48;
|
|
31487
|
+
const RPTREE_NODE_LEAF_ITEM_SIZE = 32; // leaf item size
|
|
31488
|
+
const RPTREE_NODE_CHILD_ITEM_SIZE = 24; // child item size
|
|
31489
|
+
|
|
31490
|
+
class RPTree {
|
|
31491
|
+
|
|
31492
|
+
static magic = 610839776
|
|
31084
31493
|
littleEndian = true
|
|
31085
31494
|
nodeCache = new Map()
|
|
31086
31495
|
|
|
@@ -31216,7 +31625,7 @@
|
|
|
31216
31625
|
|
|
31217
31626
|
function getDecoder(definedFieldCount, fieldCount, autoSql, format) {
|
|
31218
31627
|
|
|
31219
|
-
if ("biginteract" === format || (autoSql && ('chromatinInteract' === autoSql.table
|
|
31628
|
+
if ("biginteract" === format || (autoSql && ('chromatinInteract' === autoSql.table || 'interact' === autoSql.table))) {
|
|
31220
31629
|
return decodeInteract
|
|
31221
31630
|
} else {
|
|
31222
31631
|
const standardFieldCount = definedFieldCount - 3;
|
|
@@ -31566,7 +31975,7 @@
|
|
|
31566
31975
|
if (this.type === "bigwig") {
|
|
31567
31976
|
// Select a biwig "zoom level" appropriate for the current resolution.
|
|
31568
31977
|
const zoomLevelHeaders = await this.getZoomHeaders();
|
|
31569
|
-
let zoomLevelHeader = bpPerPixel ? zoomLevelForScale
|
|
31978
|
+
let zoomLevelHeader = bpPerPixel ? zoomLevelForScale(bpPerPixel, zoomLevelHeaders) : undefined;
|
|
31570
31979
|
if (zoomLevelHeader) {
|
|
31571
31980
|
treeOffset = zoomLevelHeader.indexOffset;
|
|
31572
31981
|
decodeFunction = decodeZoomData;
|
|
@@ -32022,7 +32431,7 @@
|
|
|
32022
32431
|
}
|
|
32023
32432
|
}
|
|
32024
32433
|
|
|
32025
|
-
function zoomLevelForScale
|
|
32434
|
+
function zoomLevelForScale(bpPerPixel, zoomLevelHeaders) {
|
|
32026
32435
|
let level;
|
|
32027
32436
|
for (let i = 0; i < zoomLevelHeaders.length; i++) {
|
|
32028
32437
|
const zl = zoomLevelHeaders[i];
|
|
@@ -32210,7 +32619,7 @@
|
|
|
32210
32619
|
class BWSource extends BaseFeatureSource {
|
|
32211
32620
|
|
|
32212
32621
|
queryable = true
|
|
32213
|
-
wgValues = {}
|
|
32622
|
+
#wgValues = {}
|
|
32214
32623
|
windowFunctions = ["mean", "min", "max"]
|
|
32215
32624
|
|
|
32216
32625
|
constructor(config, genome) {
|
|
@@ -32222,12 +32631,15 @@
|
|
|
32222
32631
|
|
|
32223
32632
|
async getFeatures({chr, start, end, bpPerPixel, windowFunction}) {
|
|
32224
32633
|
|
|
32225
|
-
await
|
|
32634
|
+
await this.reader.loadHeader();
|
|
32226
32635
|
const isBigWig = this.reader.type === "bigwig";
|
|
32227
32636
|
|
|
32228
|
-
|
|
32229
|
-
|
|
32230
|
-
await this.
|
|
32637
|
+
let features;
|
|
32638
|
+
if ("all" === chr.toLowerCase()) {
|
|
32639
|
+
features = isBigWig ? await this.getWGValues(windowFunction, bpPerPixel) : [];
|
|
32640
|
+
} else {
|
|
32641
|
+
features = await this.reader.readFeatures(chr, start, chr, end, bpPerPixel, windowFunction);
|
|
32642
|
+
}
|
|
32231
32643
|
|
|
32232
32644
|
if (!isBigWig) {
|
|
32233
32645
|
pack(features);
|
|
@@ -32243,26 +32655,25 @@
|
|
|
32243
32655
|
if (this.reader.type === "bigwig") {
|
|
32244
32656
|
return -1
|
|
32245
32657
|
} else {
|
|
32246
|
-
return this.reader.featureDensity ?
|
|
32658
|
+
return this.reader.featureDensity ? Math.floor(10000 / this.reader.featureDensity) : -1
|
|
32247
32659
|
}
|
|
32248
32660
|
|
|
32249
32661
|
}
|
|
32250
32662
|
|
|
32251
|
-
async getWGValues(windowFunction) {
|
|
32663
|
+
async getWGValues(windowFunction, bpPerPixel) {
|
|
32252
32664
|
|
|
32253
|
-
const numberOfBins = 1000; // This doesn't need to be precise
|
|
32254
32665
|
const genome = this.genome;
|
|
32255
|
-
|
|
32256
|
-
if (
|
|
32257
|
-
return
|
|
32666
|
+
const cached = this.#wgValues[windowFunction];
|
|
32667
|
+
if (cached && cached.bpPerPixel > 0.8 * bpPerPixel && cached.bpPerPixel < 1.2 * bpPerPixel) {
|
|
32668
|
+
return cached.values
|
|
32258
32669
|
} else {
|
|
32259
32670
|
|
|
32260
|
-
const bpPerPixel = genome.getGenomeLength() / numberOfBins;
|
|
32261
32671
|
const features = await this.reader.readWGFeatures(bpPerPixel, windowFunction);
|
|
32262
32672
|
let wgValues = [];
|
|
32263
32673
|
for (let f of features) {
|
|
32264
32674
|
const chr = f.chr;
|
|
32265
32675
|
const offset = genome.getCumulativeOffset(chr);
|
|
32676
|
+
if (undefined === offset) continue
|
|
32266
32677
|
const wgFeature = Object.assign({}, f);
|
|
32267
32678
|
wgFeature.chr = "all";
|
|
32268
32679
|
wgFeature.start = offset + f.start;
|
|
@@ -32271,7 +32682,7 @@
|
|
|
32271
32682
|
wgValues.push(wgFeature);
|
|
32272
32683
|
}
|
|
32273
32684
|
wgValues.sort((a, b) => a.start - b.start);
|
|
32274
|
-
this
|
|
32685
|
+
this.#wgValues[windowFunction] = {values: wgValues, bpPerPixel};
|
|
32275
32686
|
return wgValues
|
|
32276
32687
|
}
|
|
32277
32688
|
}
|
|
@@ -32293,658 +32704,780 @@
|
|
|
32293
32704
|
}
|
|
32294
32705
|
}
|
|
32295
32706
|
|
|
32296
|
-
|
|
32297
|
-
|
|
32298
|
-
|
|
32299
|
-
|
|
32300
|
-
* Author: Jim Robinson
|
|
32301
|
-
*
|
|
32302
|
-
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
32303
|
-
* of this software and associated documentation files (the "Software"), to deal
|
|
32304
|
-
* in the Software without restriction, including without limitation the rights
|
|
32305
|
-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
32306
|
-
* copies of the Software, and to permit persons to whom the Software is
|
|
32307
|
-
* furnished to do so, subject to the following conditions:
|
|
32308
|
-
*
|
|
32309
|
-
* The above copyright notice and this permission notice shall be included in
|
|
32310
|
-
* all copies or substantial portions of the Software.
|
|
32311
|
-
*
|
|
32312
|
-
*
|
|
32313
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
32314
|
-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
32315
|
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
32316
|
-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
32317
|
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
32318
|
-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
32319
|
-
* THE SOFTWARE.
|
|
32320
|
-
*/
|
|
32321
|
-
|
|
32322
|
-
const GZIP_FLAG = 0x1;
|
|
32707
|
+
const shim = .01;
|
|
32708
|
+
const colorStripWidth = 4;
|
|
32709
|
+
const axesXOffset = colorStripWidth + 1;
|
|
32710
|
+
function paintAxis(ctx, width, height, colorOrUndefined) {
|
|
32323
32711
|
|
|
32324
|
-
|
|
32712
|
+
if (undefined === this.dataRange || undefined === this.dataRange.max || undefined === this.dataRange.min) {
|
|
32713
|
+
return
|
|
32714
|
+
}
|
|
32325
32715
|
|
|
32326
|
-
|
|
32327
|
-
|
|
32328
|
-
|
|
32329
|
-
this.path = config.url;
|
|
32330
|
-
this.groupCache = {};
|
|
32331
|
-
this.datasetCache = {};
|
|
32716
|
+
IGVGraphics.fillRect(ctx, 0, 0, width, height, { fillStyle: 'white' });
|
|
32717
|
+
if (colorOrUndefined) {
|
|
32718
|
+
IGVGraphics.fillRect(ctx, width - colorStripWidth - 2, 0, colorStripWidth, height, { fillStyle: colorOrUndefined });
|
|
32332
32719
|
}
|
|
32333
32720
|
|
|
32721
|
+
const flipAxis = (undefined === this.flipAxis) ? false : this.flipAxis;
|
|
32334
32722
|
|
|
32335
|
-
|
|
32723
|
+
const xTickStart = 0.95 * width - 8 - axesXOffset;
|
|
32724
|
+
const xTickEnd = 0.95 * width - axesXOffset;
|
|
32336
32725
|
|
|
32337
|
-
|
|
32338
|
-
|
|
32339
|
-
|
|
32726
|
+
const properties =
|
|
32727
|
+
{
|
|
32728
|
+
font: 'normal 10px Arial',
|
|
32729
|
+
textAlign: 'right',
|
|
32730
|
+
fillStyle: 'black',
|
|
32731
|
+
strokeStyle: 'black',
|
|
32732
|
+
};
|
|
32340
32733
|
|
|
32341
|
-
|
|
32342
|
-
|
|
32343
|
-
|
|
32344
|
-
this.version = binaryParser.getInt();
|
|
32345
|
-
this.indexPos = binaryParser.getLong();
|
|
32346
|
-
this.indexSize = binaryParser.getInt();
|
|
32347
|
-
binaryParser.getInt();
|
|
32734
|
+
// tick
|
|
32735
|
+
IGVGraphics.strokeLine(ctx, xTickStart, shim * height, xTickEnd, shim * height, properties);
|
|
32736
|
+
IGVGraphics.fillText(ctx, prettyPrint(flipAxis ? this.dataRange.min : this.dataRange.max), xTickStart + 4, shim * height + 12, properties);
|
|
32348
32737
|
|
|
32738
|
+
const y = (1.0 - shim) * height;
|
|
32349
32739
|
|
|
32350
|
-
|
|
32351
|
-
|
|
32352
|
-
|
|
32353
|
-
while (nWindowFunctions-- > 0) {
|
|
32354
|
-
this.windowFunctions.push(binaryParser.getString());
|
|
32355
|
-
}
|
|
32356
|
-
}
|
|
32740
|
+
// tick
|
|
32741
|
+
IGVGraphics.strokeLine(ctx, xTickStart, y, xTickEnd, y, properties);
|
|
32742
|
+
IGVGraphics.fillText(ctx, prettyPrint(flipAxis ? this.dataRange.max : this.dataRange.min), xTickStart + 4, y - 4, properties);
|
|
32357
32743
|
|
|
32358
|
-
|
|
32359
|
-
|
|
32744
|
+
// vertical axis
|
|
32745
|
+
IGVGraphics.strokeLine(ctx, xTickEnd, shim * height, xTickEnd, y, properties);
|
|
32360
32746
|
|
|
32361
|
-
|
|
32362
|
-
this.trackNames = [];
|
|
32363
|
-
while (nTracks-- > 0) {
|
|
32364
|
-
this.trackNames.push(binaryParser.getString());
|
|
32365
|
-
}
|
|
32366
|
-
this.genomeID = binaryParser.getString();
|
|
32367
|
-
this.flags = binaryParser.getInt();
|
|
32368
|
-
this.compressed = (this.flags & GZIP_FLAG) !== 0;
|
|
32747
|
+
function prettyPrint(number) {
|
|
32369
32748
|
|
|
32370
|
-
|
|
32371
|
-
|
|
32372
|
-
|
|
32373
|
-
|
|
32374
|
-
|
|
32375
|
-
|
|
32376
|
-
}))
|
|
32377
|
-
|
|
32378
|
-
|
|
32379
|
-
|
|
32380
|
-
while (nEntries-- > 0) {
|
|
32381
|
-
const name = binaryParser.getString();
|
|
32382
|
-
const pos = binaryParser.getLong();
|
|
32383
|
-
const size = binaryParser.getInt();
|
|
32384
|
-
this.datasetIndex[name] = {position: pos, size: size};
|
|
32749
|
+
if (number === 0) {
|
|
32750
|
+
return "0"
|
|
32751
|
+
} else if (Math.abs(number) >= 10) {
|
|
32752
|
+
return number.toFixed()
|
|
32753
|
+
} else if (Math.abs(number) >= 1) {
|
|
32754
|
+
return number.toFixed(1)
|
|
32755
|
+
} else if (Math.abs(number) >= 0.1) {
|
|
32756
|
+
return number.toFixed(2)
|
|
32757
|
+
} else {
|
|
32758
|
+
return number.toExponential(1)
|
|
32385
32759
|
}
|
|
32760
|
+
}
|
|
32761
|
+
}
|
|
32386
32762
|
|
|
32387
|
-
|
|
32388
|
-
nEntries = binaryParser.getInt();
|
|
32389
|
-
while (nEntries-- > 0) {
|
|
32390
|
-
const name = binaryParser.getString();
|
|
32391
|
-
const pos = binaryParser.getLong();
|
|
32392
|
-
const size = binaryParser.getInt();
|
|
32393
|
-
this.groupIndex[name] = {position: pos, size: size};
|
|
32394
|
-
}
|
|
32763
|
+
const DEFAULT_COLOR$2 = 'rgb(150, 150, 150)';
|
|
32395
32764
|
|
|
32396
|
-
|
|
32765
|
+
|
|
32766
|
+
class WigTrack extends TrackBase {
|
|
32767
|
+
|
|
32768
|
+
static defaults = {
|
|
32769
|
+
height: 50,
|
|
32770
|
+
flipAxis: false,
|
|
32771
|
+
logScale: false,
|
|
32772
|
+
windowFunction: 'mean',
|
|
32773
|
+
graphType: 'bar',
|
|
32774
|
+
normalize: undefined,
|
|
32775
|
+
scaleFactor: undefined,
|
|
32776
|
+
overflowColor: `rgb(255, 32, 255)`,
|
|
32777
|
+
baselineColor: 'lightGray',
|
|
32778
|
+
summarize: true
|
|
32397
32779
|
}
|
|
32398
32780
|
|
|
32399
|
-
|
|
32781
|
+
constructor(config, browser) {
|
|
32782
|
+
super(config, browser);
|
|
32783
|
+
}
|
|
32400
32784
|
|
|
32401
|
-
|
|
32785
|
+
init(config) {
|
|
32402
32786
|
|
|
32403
|
-
|
|
32404
|
-
|
|
32787
|
+
super.init(config);
|
|
32788
|
+
|
|
32789
|
+
this.type = "wig";
|
|
32790
|
+
this.featureType = 'numeric';
|
|
32791
|
+
this.resolutionAware = true;
|
|
32792
|
+
this.paintAxis = paintAxis;
|
|
32405
32793
|
|
|
32794
|
+
const format = config.format ? config.format.toLowerCase() : config.format;
|
|
32795
|
+
if (config.featureSource) {
|
|
32796
|
+
this.featureSource = config.featureSource;
|
|
32797
|
+
delete config.featureSource;
|
|
32798
|
+
} else if ("bigwig" === format) {
|
|
32799
|
+
this.featureSource = new BWSource(config, this.browser.genome);
|
|
32800
|
+
} else if ("tdf" === format) {
|
|
32801
|
+
this.featureSource = new TDFSource(config, this.browser.genome);
|
|
32406
32802
|
} else {
|
|
32407
|
-
|
|
32408
|
-
|
|
32409
|
-
const zoomString = (chr.toLowerCase() === "all" || zoom === undefined) ? "0" : zoom.toString();
|
|
32803
|
+
this.featureSource = FeatureSource(config, this.browser.genome);
|
|
32804
|
+
}
|
|
32410
32805
|
|
|
32411
|
-
let dsName;
|
|
32412
|
-
if (windowFunction === "raw") {
|
|
32413
|
-
dsName = "/" + chr + "/raw";
|
|
32414
|
-
} else {
|
|
32415
|
-
dsName = "/" + chr + "/z" + zoomString + wf;
|
|
32416
|
-
}
|
|
32417
|
-
const indexEntry = this.datasetIndex[dsName];
|
|
32418
32806
|
|
|
32419
|
-
|
|
32420
|
-
|
|
32421
|
-
|
|
32807
|
+
// Override autoscale default
|
|
32808
|
+
if (config.max === undefined || config.autoscale === true) {
|
|
32809
|
+
this.autoscale = true;
|
|
32810
|
+
} else {
|
|
32811
|
+
this.dataRange = {
|
|
32812
|
+
min: config.min || 0,
|
|
32813
|
+
max: config.max
|
|
32814
|
+
};
|
|
32815
|
+
}
|
|
32816
|
+
}
|
|
32422
32817
|
|
|
32423
|
-
|
|
32424
|
-
|
|
32425
|
-
|
|
32426
|
-
|
|
32427
|
-
|
|
32428
|
-
}));
|
|
32818
|
+
async postInit() {
|
|
32819
|
+
const header = await this.getHeader();
|
|
32820
|
+
if (this.disposed) return // This track was removed during async load
|
|
32821
|
+
if (header) this.setTrackProperties(header);
|
|
32822
|
+
}
|
|
32429
32823
|
|
|
32430
|
-
|
|
32431
|
-
return undefined
|
|
32432
|
-
}
|
|
32824
|
+
async getFeatures(chr, start, end, bpPerPixel) {
|
|
32433
32825
|
|
|
32434
|
-
|
|
32435
|
-
|
|
32436
|
-
|
|
32437
|
-
|
|
32438
|
-
|
|
32826
|
+
const windowFunction = this.windowFunction;
|
|
32827
|
+
|
|
32828
|
+
const features = await this.featureSource.getFeatures({
|
|
32829
|
+
chr,
|
|
32830
|
+
start,
|
|
32831
|
+
end,
|
|
32832
|
+
bpPerPixel,
|
|
32833
|
+
visibilityWindow: this.visibilityWindow,
|
|
32834
|
+
windowFunction
|
|
32835
|
+
});
|
|
32836
|
+
if (this.normalize && this.featureSource.normalizationFactor) {
|
|
32837
|
+
const scaleFactor = this.featureSource.normalizationFactor;
|
|
32838
|
+
for (let f of features) {
|
|
32839
|
+
f.value *= scaleFactor;
|
|
32439
32840
|
}
|
|
32440
|
-
|
|
32441
|
-
|
|
32442
|
-
|
|
32443
|
-
|
|
32444
|
-
|
|
32445
|
-
tiles.push({position: binaryParser.getLong(), size: binaryParser.getInt()});
|
|
32841
|
+
}
|
|
32842
|
+
if (this.scaleFactor) {
|
|
32843
|
+
const scaleFactor = this.scaleFactor;
|
|
32844
|
+
for (let f of features) {
|
|
32845
|
+
f.value *= scaleFactor;
|
|
32446
32846
|
}
|
|
32847
|
+
}
|
|
32447
32848
|
|
|
32448
|
-
|
|
32449
|
-
|
|
32450
|
-
|
|
32451
|
-
|
|
32452
|
-
|
|
32453
|
-
|
|
32454
|
-
|
|
32849
|
+
// Summarize features to current resolution. This needs to be done here, rather than in the "draw" function,
|
|
32850
|
+
// for group autoscale to work.
|
|
32851
|
+
if (this.summarize && ("mean" === windowFunction || "min" === windowFunction || "max" === windowFunction)) {
|
|
32852
|
+
return summarizeData(features, start, bpPerPixel, windowFunction)
|
|
32853
|
+
} else {
|
|
32854
|
+
return features
|
|
32855
|
+
}
|
|
32856
|
+
}
|
|
32455
32857
|
|
|
32456
|
-
|
|
32457
|
-
|
|
32858
|
+
menuItemList() {
|
|
32859
|
+
const items = [];
|
|
32860
|
+
|
|
32861
|
+
if (this.flipAxis !== undefined) {
|
|
32862
|
+
items.push('<hr>');
|
|
32863
|
+
|
|
32864
|
+
function click() {
|
|
32865
|
+
this.flipAxis = !this.flipAxis;
|
|
32866
|
+
this.trackView.repaintViews();
|
|
32867
|
+
}
|
|
32868
|
+
|
|
32869
|
+
items.push({label: 'Flip y-axis', click});
|
|
32458
32870
|
}
|
|
32871
|
+
|
|
32872
|
+
if(this.featureSource.windowFunctions) {
|
|
32873
|
+
items.push(...this.wigSummarizationItems());
|
|
32874
|
+
}
|
|
32875
|
+
|
|
32876
|
+
items.push(...this.numericDataMenuItems());
|
|
32877
|
+
|
|
32878
|
+
return items
|
|
32459
32879
|
}
|
|
32460
32880
|
|
|
32461
|
-
|
|
32881
|
+
wigSummarizationItems() {
|
|
32462
32882
|
|
|
32463
|
-
const
|
|
32464
|
-
const rootGroup = this.groupCache["/"];
|
|
32465
|
-
if (rootGroup) {
|
|
32466
|
-
return rootGroup
|
|
32467
|
-
} else {
|
|
32883
|
+
const windowFunctions = this.featureSource.windowFunctions;
|
|
32468
32884
|
|
|
32469
|
-
|
|
32470
|
-
|
|
32471
|
-
|
|
32885
|
+
const menuItems = [];
|
|
32886
|
+
menuItems.push('<hr/>');
|
|
32887
|
+
menuItems.push("<div>Windowing function</div>");
|
|
32888
|
+
for (const wf of windowFunctions) {
|
|
32889
|
+
const object = $$1(createCheckbox(wf, this.windowFunction === wf));
|
|
32472
32890
|
|
|
32473
|
-
|
|
32474
|
-
|
|
32475
|
-
this.
|
|
32891
|
+
function clickHandler() {
|
|
32892
|
+
this.windowFunction = wf;
|
|
32893
|
+
this.trackView.updateViews();
|
|
32476
32894
|
}
|
|
32477
32895
|
|
|
32478
|
-
|
|
32479
|
-
|
|
32480
|
-
group.totalCount = Number(totalCountString);
|
|
32481
|
-
}
|
|
32896
|
+
menuItems.push({object, click: clickHandler});
|
|
32897
|
+
}
|
|
32482
32898
|
|
|
32483
|
-
|
|
32484
|
-
|
|
32485
|
-
if (names) {
|
|
32486
|
-
names.split(",").forEach(function (chr) {
|
|
32487
|
-
const canonicalName = genome.getChromosomeName(chr);
|
|
32488
|
-
chrAliasTable[canonicalName] = chr;
|
|
32489
|
-
});
|
|
32490
|
-
}
|
|
32491
|
-
this.chrAliasTable = chrAliasTable;
|
|
32899
|
+
return menuItems
|
|
32900
|
+
}
|
|
32492
32901
|
|
|
32493
|
-
|
|
32494
|
-
|
|
32902
|
+
|
|
32903
|
+
async getHeader() {
|
|
32904
|
+
|
|
32905
|
+
if (typeof this.featureSource.getHeader === "function") {
|
|
32906
|
+
this.header = await this.featureSource.getHeader();
|
|
32495
32907
|
}
|
|
32908
|
+
return this.header
|
|
32496
32909
|
}
|
|
32497
32910
|
|
|
32498
|
-
|
|
32911
|
+
// TODO: refactor to igvUtils.js
|
|
32912
|
+
getScaleFactor(min, max, height, logScale) {
|
|
32913
|
+
const minValue = (logScale === true) ? ((min < 0) ? -Math.log10(Math.abs(min) + 1) : Math.log10(Math.abs(min) + 1)) : min;
|
|
32914
|
+
const maxValue = (logScale === true) ? Math.log10(Math.abs(max) + 1) : max;
|
|
32915
|
+
const scale = height / (maxValue - minValue);
|
|
32916
|
+
return scale
|
|
32917
|
+
}
|
|
32499
32918
|
|
|
32500
|
-
|
|
32501
|
-
|
|
32502
|
-
|
|
32503
|
-
} else {
|
|
32919
|
+
computeYPixelValue(yValue, yScaleFactor) {
|
|
32920
|
+
return (this.flipAxis ? (yValue - this.dataRange.min) : (this.dataRange.max - yValue)) * yScaleFactor
|
|
32921
|
+
}
|
|
32504
32922
|
|
|
32505
|
-
|
|
32506
|
-
|
|
32507
|
-
|
|
32508
|
-
|
|
32509
|
-
|
|
32923
|
+
computeYPixelValueInLogScale(yValue, yScaleFactor) {
|
|
32924
|
+
let maxValue = this.dataRange.max;
|
|
32925
|
+
let minValue = this.dataRange.min;
|
|
32926
|
+
minValue = (minValue < 0) ? -Math.log10(Math.abs(minValue) + 1) : Math.log10(Math.abs(minValue) + 1);
|
|
32927
|
+
maxValue = (maxValue < 0) ? -Math.log10(Math.abs(maxValue) + 1) : Math.log10(Math.abs(maxValue) + 1);
|
|
32928
|
+
|
|
32929
|
+
yValue = (yValue < 0) ? -Math.log10(Math.abs(yValue) +1) : Math.log10(yValue + 1);
|
|
32930
|
+
return ((this.flipAxis ? (yValue - minValue) : (maxValue - yValue)) * yScaleFactor)
|
|
32931
|
+
}
|
|
32510
32932
|
|
|
32511
|
-
|
|
32512
|
-
|
|
32513
|
-
|
|
32514
|
-
|
|
32933
|
+
draw(options) {
|
|
32934
|
+
|
|
32935
|
+
const features = options.features;
|
|
32936
|
+
const ctx = options.context;
|
|
32937
|
+
const bpPerPixel = options.bpPerPixel;
|
|
32938
|
+
const bpStart = options.bpStart;
|
|
32939
|
+
const pixelWidth = options.pixelWidth;
|
|
32940
|
+
const pixelHeight = options.pixelHeight - 1;
|
|
32941
|
+
const bpEnd = bpStart + pixelWidth * bpPerPixel + 1;
|
|
32942
|
+
|
|
32943
|
+
const scaleFactor = this.getScaleFactor(this.dataRange.min, this.dataRange.max, pixelHeight, this.logScale);
|
|
32944
|
+
const yScale = (yValue) => this.logScale
|
|
32945
|
+
? this.computeYPixelValueInLogScale(yValue, scaleFactor)
|
|
32946
|
+
: this.computeYPixelValue(yValue, scaleFactor);
|
|
32947
|
+
|
|
32948
|
+
if (features && features.length > 0) {
|
|
32949
|
+
|
|
32950
|
+
if (this.dataRange.min === undefined) this.dataRange.min = 0;
|
|
32951
|
+
|
|
32952
|
+
// Max can be less than min if config.min is set but max left to autoscale. If that's the case there is
|
|
32953
|
+
// nothing to paint.
|
|
32954
|
+
if (this.dataRange.max > this.dataRange.min) {
|
|
32955
|
+
|
|
32956
|
+
let lastPixelEnd = -1;
|
|
32957
|
+
let lastY;
|
|
32958
|
+
const y0 = yScale(0);
|
|
32959
|
+
|
|
32960
|
+
for (let f of features) {
|
|
32961
|
+
|
|
32962
|
+
if (f.end < bpStart) continue
|
|
32963
|
+
if (f.start > bpEnd) break
|
|
32964
|
+
|
|
32965
|
+
const x = (f.start - bpStart) / bpPerPixel;
|
|
32966
|
+
if (isNaN(x)) continue
|
|
32967
|
+
|
|
32968
|
+
let y = yScale(f.value);
|
|
32969
|
+
|
|
32970
|
+
const rectEnd = (f.end - bpStart) / bpPerPixel;
|
|
32971
|
+
const width = rectEnd - x;
|
|
32972
|
+
|
|
32973
|
+
const color = options.alpha ? IGVColor.addAlpha(this.getColorForFeature(f), options.alpha) : this.getColorForFeature(f);
|
|
32974
|
+
|
|
32975
|
+
if (this.graphType === "line") {
|
|
32976
|
+
if (lastY !== undefined) {
|
|
32977
|
+
IGVGraphics.strokeLine(ctx, lastPixelEnd, lastY, x, y, {
|
|
32978
|
+
"fillStyle": color,
|
|
32979
|
+
"strokeStyle": color
|
|
32980
|
+
});
|
|
32981
|
+
}
|
|
32982
|
+
IGVGraphics.strokeLine(ctx, x, y, x + width, y, {"fillStyle": color, "strokeStyle": color});
|
|
32983
|
+
} else if (this.graphType === "points") {
|
|
32984
|
+
const pointSize = this.config.pointSize || 3;
|
|
32985
|
+
const px = x + width / 2;
|
|
32986
|
+
IGVGraphics.fillCircle(ctx, px, y, pointSize / 2, {"fillStyle": color, "strokeStyle": color});
|
|
32987
|
+
|
|
32988
|
+
if (f.value > this.dataRange.max) {
|
|
32989
|
+
IGVGraphics.fillCircle(ctx, px, pointSize / 2, pointSize / 2, 3, {fillStyle: this.overflowColor});
|
|
32990
|
+
} else if (f.value < this.dataRange.min) {
|
|
32991
|
+
IGVGraphics.fillCircle(ctx, px, pixelHeight - pointSize / 2, pointSize / 2, 3, {fillStyle: this.overflowColor});
|
|
32992
|
+
}
|
|
32993
|
+
|
|
32994
|
+
} else {
|
|
32995
|
+
// Default graph type (bar)
|
|
32996
|
+
const height = Math.min(pixelHeight, y - y0);
|
|
32997
|
+
IGVGraphics.fillRect(ctx, x, y0, width, height, {fillStyle: color});
|
|
32998
|
+
if (f.value > this.dataRange.max) {
|
|
32999
|
+
IGVGraphics.fillRect(ctx, x, 0, width, 3, {fillStyle: this.overflowColor});
|
|
33000
|
+
} else if (f.value < this.dataRange.min) {
|
|
33001
|
+
IGVGraphics.fillRect(ctx, x, pixelHeight - 2, width, 3, {fillStyle: this.overflowColor});
|
|
33002
|
+
}
|
|
33003
|
+
|
|
33004
|
+
}
|
|
33005
|
+
lastPixelEnd = x + width;
|
|
33006
|
+
lastY = y;
|
|
32515
33007
|
}
|
|
32516
|
-
}));
|
|
32517
33008
|
|
|
32518
|
-
|
|
32519
|
-
|
|
33009
|
+
// If the track includes negative values draw a baseline
|
|
33010
|
+
if (this.dataRange.min < 0) {
|
|
33011
|
+
let maxValue = this.dataRange.max;
|
|
33012
|
+
let minValue = this.dataRange.min;
|
|
33013
|
+
minValue = (this.logScale === true) ? ((minValue < 0) ? -Math.log10(Math.abs(minValue) + 1) : Math.log10(Math.abs(minValue) + 1)) : minValue;
|
|
33014
|
+
maxValue = (this.logScale === true) ? ((maxValue < 0) ? -Math.log10(Math.abs(maxValue) + 1) : Math.log10(Math.abs(maxValue) + 1)) : maxValue;
|
|
33015
|
+
const ratio = maxValue / (maxValue - minValue);
|
|
33016
|
+
const basepx = this.flipAxis ? (1 - ratio) * pixelHeight : ratio * pixelHeight;
|
|
33017
|
+
IGVGraphics.strokeLine(ctx, 0, basepx, options.pixelWidth, basepx, {strokeStyle: this.baselineColor});
|
|
33018
|
+
}
|
|
32520
33019
|
}
|
|
33020
|
+
}
|
|
32521
33021
|
|
|
32522
|
-
|
|
32523
|
-
|
|
32524
|
-
let
|
|
32525
|
-
|
|
32526
|
-
|
|
32527
|
-
|
|
32528
|
-
|
|
33022
|
+
// Draw guidelines
|
|
33023
|
+
if (this.config.hasOwnProperty('guideLines')) {
|
|
33024
|
+
for (let line of this.config.guideLines) {
|
|
33025
|
+
if (line.hasOwnProperty('color') && line.hasOwnProperty('y') && line.hasOwnProperty('dotted')) {
|
|
33026
|
+
let y = yScale(line.y);
|
|
33027
|
+
let props = {
|
|
33028
|
+
'strokeStyle': line['color'],
|
|
33029
|
+
'strokeWidth': 2
|
|
33030
|
+
};
|
|
33031
|
+
if (line['dotted']) IGVGraphics.dashedLine(options.context, 0, y, options.pixelWidth, y, 5, props);
|
|
33032
|
+
else IGVGraphics.strokeLine(options.context, 0, y, options.pixelWidth, y, props);
|
|
33033
|
+
}
|
|
32529
33034
|
}
|
|
32530
|
-
this.groupCache[name] = group;
|
|
32531
|
-
return group
|
|
32532
33035
|
}
|
|
32533
33036
|
}
|
|
32534
33037
|
|
|
32535
|
-
|
|
33038
|
+
popupData(clickState, features) {
|
|
32536
33039
|
|
|
32537
|
-
|
|
32538
|
-
return a.position - b.position
|
|
32539
|
-
});
|
|
33040
|
+
if (features === undefined) features = this.clickedFeatures(clickState);
|
|
32540
33041
|
|
|
32541
|
-
|
|
32542
|
-
return idx.size > 0
|
|
32543
|
-
});
|
|
33042
|
+
if (features && features.length > 0) {
|
|
32544
33043
|
|
|
32545
|
-
|
|
32546
|
-
|
|
32547
|
-
}
|
|
33044
|
+
const genomicLocation = clickState.genomicLocation;
|
|
33045
|
+
const popupData = [];
|
|
32548
33046
|
|
|
32549
|
-
|
|
33047
|
+
// Sort features based on distance from click
|
|
33048
|
+
features.sort(function (a, b) {
|
|
33049
|
+
const distA = Math.abs((a.start + a.end) / 2 - genomicLocation);
|
|
33050
|
+
const distB = Math.abs((b.start + b.end) / 2 - genomicLocation);
|
|
33051
|
+
return distA - distB
|
|
33052
|
+
});
|
|
32550
33053
|
|
|
32551
|
-
|
|
33054
|
+
// Display closest 10
|
|
33055
|
+
const displayFeatures = features.length > 10 ? features.slice(0, 10) : features;
|
|
32552
33056
|
|
|
32553
|
-
|
|
33057
|
+
// Resort in ascending order
|
|
33058
|
+
displayFeatures.sort(function (a, b) {
|
|
33059
|
+
return a.start - b.start
|
|
33060
|
+
});
|
|
32554
33061
|
|
|
32555
|
-
|
|
32556
|
-
|
|
32557
|
-
|
|
32558
|
-
|
|
33062
|
+
for (let selectedFeature of displayFeatures) {
|
|
33063
|
+
if (selectedFeature) {
|
|
33064
|
+
if (popupData.length > 0) {
|
|
33065
|
+
popupData.push('<hr/>');
|
|
33066
|
+
}
|
|
33067
|
+
let posString = (selectedFeature.end - selectedFeature.start) === 1 ?
|
|
33068
|
+
numberFormatter$1(Math.floor(selectedFeature.start) + 1)
|
|
33069
|
+
: numberFormatter$1(Math.floor(selectedFeature.start) + 1) + "-" + numberFormatter$1(Math.floor(selectedFeature.end));
|
|
33070
|
+
popupData.push({name: "Position:", value: posString});
|
|
33071
|
+
popupData.push({
|
|
33072
|
+
name: "Value: ",
|
|
33073
|
+
value: numberFormatter$1(selectedFeature.value.toFixed(4))
|
|
33074
|
+
});
|
|
32559
33075
|
}
|
|
32560
|
-
}));
|
|
32561
|
-
|
|
32562
|
-
const tileData = this.compressed ? inflate_1$3(data).buffer : data;
|
|
32563
|
-
|
|
32564
|
-
const binaryParser = new BinaryParser$1(new DataView(tileData));
|
|
32565
|
-
const type = binaryParser.getString();
|
|
32566
|
-
let tile;
|
|
32567
|
-
switch (type) {
|
|
32568
|
-
case "fixedStep":
|
|
32569
|
-
tile = createFixedStep(binaryParser, nTracks);
|
|
32570
|
-
break
|
|
32571
|
-
case "variableStep":
|
|
32572
|
-
tile = createVariableStep(binaryParser, nTracks);
|
|
32573
|
-
break
|
|
32574
|
-
case "bed":
|
|
32575
|
-
case "bedWithName":
|
|
32576
|
-
tile = createBed(binaryParser, nTracks, type);
|
|
32577
|
-
break
|
|
32578
|
-
default:
|
|
32579
|
-
throw "Unknown tile type: " + type
|
|
32580
33076
|
}
|
|
32581
|
-
|
|
33077
|
+
if (displayFeatures.length < features.length) {
|
|
33078
|
+
popupData.push("<hr/>...");
|
|
33079
|
+
}
|
|
32582
33080
|
|
|
33081
|
+
return popupData
|
|
33082
|
+
|
|
33083
|
+
} else {
|
|
33084
|
+
return []
|
|
32583
33085
|
}
|
|
32584
|
-
return tiles
|
|
32585
33086
|
}
|
|
32586
33087
|
|
|
32587
|
-
|
|
33088
|
+
get supportsWholeGenome() {
|
|
33089
|
+
return !this.config.indexURL && this.config.supportsWholeGenome !== false
|
|
33090
|
+
}
|
|
32588
33091
|
|
|
32589
|
-
|
|
32590
|
-
|
|
32591
|
-
|
|
32592
|
-
|
|
32593
|
-
|
|
32594
|
-
}));
|
|
33092
|
+
/**
|
|
33093
|
+
* Return color for feature.
|
|
33094
|
+
* @param feature
|
|
33095
|
+
* @returns {string}
|
|
33096
|
+
*/
|
|
32595
33097
|
|
|
32596
|
-
|
|
32597
|
-
|
|
32598
|
-
|
|
32599
|
-
|
|
33098
|
+
getColorForFeature(f) {
|
|
33099
|
+
let c = (f.value < 0 && this.altColor) ? this.altColor : this.color || DEFAULT_COLOR$2;
|
|
33100
|
+
return (typeof c === "function") ? c(f.value) : c
|
|
33101
|
+
}
|
|
32600
33102
|
|
|
32601
|
-
|
|
32602
|
-
|
|
32603
|
-
|
|
32604
|
-
|
|
32605
|
-
|
|
32606
|
-
|
|
32607
|
-
|
|
32608
|
-
|
|
32609
|
-
|
|
32610
|
-
|
|
33103
|
+
/**
|
|
33104
|
+
* Called when the track is removed. Do any needed cleanup here
|
|
33105
|
+
*/
|
|
33106
|
+
dispose() {
|
|
33107
|
+
this.trackView = undefined;
|
|
33108
|
+
}
|
|
33109
|
+
|
|
33110
|
+
}
|
|
33111
|
+
|
|
33112
|
+
/**
|
|
33113
|
+
* Summarize wig data in bins of size "bpPerPixel" with the given window function.
|
|
33114
|
+
*
|
|
33115
|
+
* @param features wig (numeric) data -- features cannot overlap, and are in ascending order by start position
|
|
33116
|
+
* @param startBP bp start position for computing binned data
|
|
33117
|
+
* @param bpPerPixel bp per pixel (bin)
|
|
33118
|
+
* @param windowFunction mean, min, or max
|
|
33119
|
+
* @returns {*|*[]}
|
|
33120
|
+
*/
|
|
33121
|
+
function summarizeData(features, startBP, bpPerPixel, windowFunction = "mean") {
|
|
33122
|
+
|
|
33123
|
+
if (bpPerPixel <= 1 || !features || features.length === 0) {
|
|
33124
|
+
return features
|
|
33125
|
+
}
|
|
33126
|
+
|
|
33127
|
+
// Assume features are sorted by position. Wig features cannot overlap. Note, UCSC "reductionLevel" == bpPerPixel
|
|
33128
|
+
const chr = features[0].chr;
|
|
33129
|
+
const binSize = bpPerPixel;
|
|
33130
|
+
const summaryFeatures = [];
|
|
33131
|
+
|
|
33132
|
+
const finishBin = (bin) => {
|
|
33133
|
+
const start = startBP + bin.bin * binSize;
|
|
33134
|
+
const end = start + binSize;
|
|
33135
|
+
let value;
|
|
33136
|
+
switch (windowFunction) {
|
|
33137
|
+
case "mean":
|
|
33138
|
+
value = bin.sumData / bin.count;
|
|
33139
|
+
break
|
|
33140
|
+
case "max":
|
|
33141
|
+
value = bin.max;
|
|
33142
|
+
break
|
|
33143
|
+
case "min":
|
|
33144
|
+
value = bin.min;
|
|
33145
|
+
break
|
|
32611
33146
|
default:
|
|
32612
|
-
throw
|
|
33147
|
+
throw Error(`Unknown window function: ${windowFunction}`)
|
|
33148
|
+
}
|
|
33149
|
+
const description = `${windowFunction} of ${bin.count} values`;
|
|
33150
|
+
summaryFeatures.push({chr, start, end, value, description});
|
|
33151
|
+
};
|
|
33152
|
+
|
|
33153
|
+
let currentBinData;
|
|
33154
|
+
for (let f of features) {
|
|
33155
|
+
|
|
33156
|
+
// Loop through bins this feature overlaps, updating the weighted sum for each bin or min/max,
|
|
33157
|
+
// depending on window function
|
|
33158
|
+
let startBin = Math.floor((f.start - startBP) / binSize);
|
|
33159
|
+
const endBin = Math.floor((f.end - startBP) / binSize);
|
|
33160
|
+
|
|
33161
|
+
if (currentBinData && startBin === currentBinData.bin) {
|
|
33162
|
+
currentBinData.add(f);
|
|
33163
|
+
startBin++;
|
|
32613
33164
|
}
|
|
32614
|
-
}
|
|
32615
33165
|
|
|
32616
|
-
|
|
33166
|
+
if (!currentBinData || endBin > currentBinData.bin) {
|
|
32617
33167
|
|
|
32618
|
-
|
|
32619
|
-
|
|
32620
|
-
|
|
32621
|
-
const span = binaryParser.getFloat();
|
|
33168
|
+
if(currentBinData) {
|
|
33169
|
+
finishBin(currentBinData);
|
|
33170
|
+
}
|
|
32622
33171
|
|
|
32623
|
-
|
|
32624
|
-
|
|
32625
|
-
|
|
32626
|
-
|
|
32627
|
-
|
|
32628
|
-
|
|
32629
|
-
|
|
33172
|
+
// Feature stretches across multiple bins.
|
|
33173
|
+
if (endBin > startBin) {
|
|
33174
|
+
const end = startBP + endBin * binSize;
|
|
33175
|
+
summaryFeatures.push({chr, start: f.start, end, value: f.value});
|
|
33176
|
+
}
|
|
33177
|
+
|
|
33178
|
+
currentBinData = new SummaryBinData(endBin, f);
|
|
32630
33179
|
}
|
|
32631
|
-
|
|
33180
|
+
|
|
33181
|
+
}
|
|
33182
|
+
if(currentBinData) {
|
|
33183
|
+
finishBin(currentBinData);
|
|
32632
33184
|
}
|
|
32633
33185
|
|
|
32634
|
-
|
|
32635
|
-
|
|
32636
|
-
|
|
32637
|
-
|
|
32638
|
-
|
|
32639
|
-
|
|
32640
|
-
|
|
33186
|
+
// Consolidate
|
|
33187
|
+
const c = [];
|
|
33188
|
+
let lastFeature = summaryFeatures[0];
|
|
33189
|
+
for (let f of summaryFeatures) {
|
|
33190
|
+
if (lastFeature.value === f.value && f.start <= lastFeature.end) {
|
|
33191
|
+
lastFeature.end = f.end;
|
|
33192
|
+
} else {
|
|
33193
|
+
c.push(lastFeature);
|
|
33194
|
+
lastFeature = f;
|
|
33195
|
+
}
|
|
32641
33196
|
}
|
|
32642
|
-
|
|
33197
|
+
c.push(lastFeature);
|
|
32643
33198
|
|
|
32644
|
-
|
|
33199
|
+
return c
|
|
32645
33200
|
|
|
32646
|
-
|
|
32647
|
-
const span = binaryParser.getFloat();
|
|
32648
|
-
const nPositions = binaryParser.getInt();
|
|
32649
|
-
const start = [];
|
|
33201
|
+
}
|
|
32650
33202
|
|
|
32651
|
-
|
|
32652
|
-
|
|
32653
|
-
|
|
33203
|
+
class SummaryBinData {
|
|
33204
|
+
constructor(bin, feature) {
|
|
33205
|
+
this.bin = bin;
|
|
33206
|
+
this.sumData = feature.value;
|
|
33207
|
+
this.count = 1;
|
|
33208
|
+
this.min = feature.value;
|
|
33209
|
+
this.max = feature.value;
|
|
32654
33210
|
}
|
|
32655
|
-
binaryParser.getInt(); // # of samples, ignored but should === nTracks
|
|
32656
33211
|
|
|
32657
|
-
|
|
32658
|
-
|
|
32659
|
-
|
|
32660
|
-
|
|
32661
|
-
|
|
32662
|
-
while (np-- > 0) {
|
|
32663
|
-
dtrack.push(binaryParser.getFloat());
|
|
32664
|
-
}
|
|
32665
|
-
data.push(dtrack);
|
|
33212
|
+
add(feature) {
|
|
33213
|
+
this.sumData += feature.value;
|
|
33214
|
+
this.max = Math.max(feature.value, this.max);
|
|
33215
|
+
this.min = Math.min(feature.value, this.min);
|
|
33216
|
+
this.count++;
|
|
32666
33217
|
}
|
|
32667
33218
|
|
|
32668
|
-
|
|
32669
|
-
|
|
32670
|
-
tileStart: tileStart,
|
|
32671
|
-
span: span,
|
|
32672
|
-
start: start,
|
|
32673
|
-
data: data,
|
|
32674
|
-
nTracks: nTracks,
|
|
32675
|
-
nPositions: nPositions
|
|
33219
|
+
get mean() {
|
|
33220
|
+
return this.sumData / this.count
|
|
32676
33221
|
}
|
|
32677
33222
|
}
|
|
32678
33223
|
|
|
32679
|
-
|
|
33224
|
+
const DEFAULT_MAX_WG_COUNT = 10000;
|
|
32680
33225
|
|
|
32681
|
-
|
|
33226
|
+
/**
|
|
33227
|
+
* feature source for "bed like" files (tab or whitespace delimited files with 1 feature per line: bed, gff, vcf, etc)
|
|
33228
|
+
*
|
|
33229
|
+
* @param config
|
|
33230
|
+
* @constructor
|
|
33231
|
+
*/
|
|
33232
|
+
class TextFeatureSource extends BaseFeatureSource {
|
|
32682
33233
|
|
|
32683
|
-
|
|
32684
|
-
const start = [];
|
|
32685
|
-
while (n-- > 0) {
|
|
32686
|
-
start.push(binaryParser.getInt());
|
|
32687
|
-
}
|
|
33234
|
+
constructor(config, genome) {
|
|
32688
33235
|
|
|
32689
|
-
|
|
32690
|
-
const end = [];
|
|
32691
|
-
while (n-- > 0) {
|
|
32692
|
-
end.push(binaryParser.getInt());
|
|
32693
|
-
}
|
|
33236
|
+
super(genome);
|
|
32694
33237
|
|
|
32695
|
-
|
|
32696
|
-
|
|
32697
|
-
|
|
32698
|
-
|
|
32699
|
-
|
|
32700
|
-
const dtrack = [];
|
|
32701
|
-
while (np-- > 0) {
|
|
32702
|
-
dtrack.push(binaryParser.getFloat());
|
|
32703
|
-
}
|
|
32704
|
-
data.push(dtrack);
|
|
32705
|
-
}
|
|
33238
|
+
this.config = config || {};
|
|
33239
|
+
this.genome = genome;
|
|
33240
|
+
this.sourceType = (config.sourceType === undefined ? "file" : config.sourceType);
|
|
33241
|
+
this.maxWGCount = config.maxWGCount || DEFAULT_MAX_WG_COUNT;
|
|
33242
|
+
this.windowFunctions = ["mean", "min", "max", "none"];
|
|
32706
33243
|
|
|
32707
|
-
|
|
32708
|
-
|
|
32709
|
-
|
|
32710
|
-
|
|
32711
|
-
|
|
33244
|
+
const queryableFormats = new Set(["bigwig", "bw", "bigbed", "bb", "biginteract", "biggenepred", "bignarrowpeak", "tdf"]);
|
|
33245
|
+
|
|
33246
|
+
this.queryable = config.indexURL || config.queryable === true; // False by default, unless explicitly set
|
|
33247
|
+
if (config.reader) {
|
|
33248
|
+
// Explicit reader implementation
|
|
33249
|
+
this.reader = config.reader;
|
|
33250
|
+
this.queryable = config.queryable !== false;
|
|
33251
|
+
} else if (config.sourceType === "ga4gh") {
|
|
33252
|
+
throw Error("Unsupported source type 'ga4gh'")
|
|
33253
|
+
} else if ((config.type === "eqtl" || config.type === "qtl") && config.sourceType === "gtex-ws") {
|
|
33254
|
+
this.reader = new GtexReader(config);
|
|
33255
|
+
this.queryable = true;
|
|
33256
|
+
} else if ("htsget" === config.sourceType) {
|
|
33257
|
+
this.reader = new HtsgetVariantReader(config, genome);
|
|
33258
|
+
this.queryable = true;
|
|
33259
|
+
} else if (config.sourceType === 'ucscservice') {
|
|
33260
|
+
this.reader = new UCSCServiceReader(config.source);
|
|
33261
|
+
this.queryable = true;
|
|
33262
|
+
} else if (config.sourceType === 'custom') {
|
|
33263
|
+
this.reader = new CustomServiceReader(config.source);
|
|
33264
|
+
this.queryable = false !== config.source.queryable;
|
|
33265
|
+
} else if ('service' === config.sourceType) {
|
|
33266
|
+
this.reader = new FeatureFileReader(config, genome);
|
|
33267
|
+
this.queryable = true;
|
|
33268
|
+
} else {
|
|
33269
|
+
// File of some type (i.e. not a webservice)
|
|
33270
|
+
this.reader = new FeatureFileReader(config, genome);
|
|
33271
|
+
if (config.queryable !== undefined) {
|
|
33272
|
+
this.queryable = config.queryable;
|
|
33273
|
+
} else if (queryableFormats.has(config.format) || this.reader.indexed) {
|
|
33274
|
+
this.queryable = true;
|
|
33275
|
+
} else ;
|
|
32712
33276
|
}
|
|
32713
|
-
}
|
|
32714
33277
|
|
|
32715
|
-
|
|
32716
|
-
|
|
32717
|
-
start: start,
|
|
32718
|
-
end: end,
|
|
32719
|
-
data: data,
|
|
32720
|
-
nTracks: nTracks,
|
|
32721
|
-
nPositions: nPositions
|
|
32722
|
-
}
|
|
32723
|
-
}
|
|
33278
|
+
// Flag indicating if features loaded by this source can be searched for by name or attribute, true by default
|
|
33279
|
+
this.searchable = config.searchable !== false;
|
|
32724
33280
|
|
|
32725
|
-
|
|
33281
|
+
}
|
|
32726
33282
|
|
|
32727
|
-
|
|
32728
|
-
|
|
32729
|
-
|
|
32730
|
-
const t = tiles[i];
|
|
32731
|
-
if (t.position > current.position + current.size) {
|
|
32732
|
-
consolidated.push(current);
|
|
32733
|
-
current = t;
|
|
32734
|
-
} else {
|
|
32735
|
-
current.size = t.position + t.size - current.position;
|
|
33283
|
+
async defaultVisibilityWindow() {
|
|
33284
|
+
if (this.reader && typeof this.reader.defaultVisibilityWindow === 'function') {
|
|
33285
|
+
return this.reader.defaultVisibilityWindow()
|
|
32736
33286
|
}
|
|
32737
33287
|
}
|
|
32738
|
-
consolidated.push(current);
|
|
32739
|
-
return consolidated
|
|
32740
|
-
}
|
|
32741
|
-
|
|
32742
|
-
/*
|
|
32743
|
-
* The MIT License (MIT)
|
|
32744
|
-
*
|
|
32745
|
-
* Copyright (c) 2016 University of California San Diego
|
|
32746
|
-
* Author: Jim Robinson
|
|
32747
|
-
*
|
|
32748
|
-
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
32749
|
-
* of this software and associated documentation files (the "Software"), to deal
|
|
32750
|
-
* in the Software without restriction, including without limitation the rights
|
|
32751
|
-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
32752
|
-
* copies of the Software, and to permit persons to whom the Software is
|
|
32753
|
-
* furnished to do so, subject to the following conditions:
|
|
32754
|
-
*
|
|
32755
|
-
* The above copyright notice and this permission notice shall be included in
|
|
32756
|
-
* all copies or substantial portions of the Software.
|
|
32757
|
-
*
|
|
32758
|
-
*
|
|
32759
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
32760
|
-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
32761
|
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
32762
|
-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
32763
|
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
32764
|
-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
32765
|
-
* THE SOFTWARE.
|
|
32766
|
-
*/
|
|
32767
|
-
|
|
32768
|
-
class TDFSource extends BaseFeatureSource {
|
|
32769
33288
|
|
|
32770
|
-
|
|
32771
|
-
|
|
32772
|
-
|
|
32773
|
-
|
|
32774
|
-
|
|
32775
|
-
|
|
33289
|
+
async trackType() {
|
|
33290
|
+
const header = await this.getHeader();
|
|
33291
|
+
if (header) {
|
|
33292
|
+
return header.type
|
|
33293
|
+
} else {
|
|
33294
|
+
return undefined // Convention for unknown or unspecified
|
|
33295
|
+
}
|
|
32776
33296
|
}
|
|
32777
33297
|
|
|
32778
|
-
async
|
|
33298
|
+
async getHeader() {
|
|
33299
|
+
if (!this.header) {
|
|
32779
33300
|
|
|
32780
|
-
|
|
32781
|
-
|
|
32782
|
-
|
|
32783
|
-
|
|
32784
|
-
|
|
32785
|
-
|
|
32786
|
-
const len = genome.getChromosome(c).bpLength;
|
|
32787
|
-
bpPerPixel = len / 1000;
|
|
32788
|
-
const chrFeatures = await this._getFeatures(c, 0, len, bpPerPixel, windowFunction);
|
|
32789
|
-
if (chrFeatures) {
|
|
32790
|
-
for (let f of chrFeatures) {
|
|
32791
|
-
const wg = Object.assign({}, f);
|
|
32792
|
-
wg.chr = "all";
|
|
32793
|
-
wg.start = genome.getGenomeCoordinate(f.chr, f.start);
|
|
32794
|
-
wg.end = genome.getGenomeCoordinate(f.chr, f.end);
|
|
32795
|
-
wg._f = f;
|
|
32796
|
-
wgFeatures.push(wg);
|
|
32797
|
-
}
|
|
33301
|
+
if (this.reader && typeof this.reader.readHeader === "function") {
|
|
33302
|
+
const header = await this.reader.readHeader();
|
|
33303
|
+
if (header) {
|
|
33304
|
+
this.header = header;
|
|
33305
|
+
if (header.format) {
|
|
33306
|
+
this.config.format = header.format;
|
|
32798
33307
|
}
|
|
33308
|
+
} else {
|
|
33309
|
+
this.header = {};
|
|
32799
33310
|
}
|
|
33311
|
+
} else {
|
|
33312
|
+
this.header = {};
|
|
32800
33313
|
}
|
|
32801
|
-
return wgFeatures
|
|
32802
|
-
|
|
32803
|
-
} else {
|
|
32804
|
-
return this._getFeatures(chr, start, end, bpPerPixel, windowFunction)
|
|
32805
33314
|
}
|
|
33315
|
+
return this.header
|
|
32806
33316
|
}
|
|
32807
|
-
async _getFeatures(chr, start, end, bpPerPixel, windowFunction) {
|
|
32808
|
-
const genomicInterval = new GenomicInterval(chr, start, end);
|
|
32809
|
-
const genome = this.genome;
|
|
32810
33317
|
|
|
33318
|
+
/**
|
|
33319
|
+
* Required function for all data source objects. Fetches features for the
|
|
33320
|
+
* range requested.
|
|
33321
|
+
*
|
|
33322
|
+
* This function is quite complex due to the variety of reader types backing it, some indexed, some queryable,
|
|
33323
|
+
* some not.
|
|
33324
|
+
*
|
|
33325
|
+
* @param chr
|
|
33326
|
+
* @param start
|
|
33327
|
+
* @param end
|
|
33328
|
+
* @param bpPerPixel
|
|
33329
|
+
*/
|
|
33330
|
+
async getFeatures({chr, start, end, bpPerPixel, visibilityWindow, windowFunction}) {
|
|
32811
33331
|
|
|
32812
|
-
|
|
32813
|
-
this.rootGroup = await this.reader.readRootGroup();
|
|
32814
|
-
if (!this.normalizationFactor) {
|
|
32815
|
-
const totalCount = this.rootGroup.totalCount;
|
|
32816
|
-
if (totalCount) {
|
|
32817
|
-
this.normalizationFactor = 1.0e6 / totalCount;
|
|
32818
|
-
}
|
|
32819
|
-
}
|
|
32820
|
-
}
|
|
33332
|
+
const isWholeGenome = ("all" === chr.toLowerCase());
|
|
32821
33333
|
|
|
32822
|
-
|
|
32823
|
-
|
|
32824
|
-
let queryChr = this.reader.chrAliasTable[chr];
|
|
32825
|
-
let maxZoom = this.reader.maxZoom;
|
|
32826
|
-
if (queryChr === undefined) queryChr = chr;
|
|
32827
|
-
if (maxZoom === undefined) maxZoom = -1;
|
|
33334
|
+
start = start || 0;
|
|
33335
|
+
end = end || Number.MAX_SAFE_INTEGER;
|
|
32828
33336
|
|
|
32829
|
-
|
|
32830
|
-
|
|
32831
|
-
|
|
32832
|
-
|
|
33337
|
+
// Various conditions that can require a feature load
|
|
33338
|
+
// * view is "whole genome" but no features are loaded
|
|
33339
|
+
// * cache is disabled
|
|
33340
|
+
// * cache does not contain requested range
|
|
33341
|
+
// const containsRange = this.featureCache.containsRange(new GenomicInterval(queryChr, start, end))
|
|
33342
|
+
if ((isWholeGenome && !this.wgFeatures && this.supportsWholeGenome()) ||
|
|
33343
|
+
this.config.disableCache ||
|
|
33344
|
+
!this.featureCache ||
|
|
33345
|
+
!this.featureCache.containsRange(new GenomicInterval(chr, start, end))) {
|
|
33346
|
+
await this.loadFeatures(chr, start, end, visibilityWindow);
|
|
32833
33347
|
}
|
|
32834
33348
|
|
|
32835
|
-
|
|
32836
|
-
|
|
32837
|
-
|
|
32838
|
-
|
|
32839
|
-
|
|
32840
|
-
|
|
32841
|
-
|
|
32842
|
-
|
|
32843
|
-
|
|
32844
|
-
|
|
32845
|
-
|
|
32846
|
-
|
|
32847
|
-
decodeVaryTile(tile, chr, start, end, bpPerPixel, features);
|
|
32848
|
-
break
|
|
32849
|
-
case "fixedStep":
|
|
32850
|
-
decodeFixedTile(tile, chr, start, end, bpPerPixel, features);
|
|
32851
|
-
break
|
|
32852
|
-
default:
|
|
32853
|
-
throw ("Unknown tile type: " + tile.type)
|
|
33349
|
+
if (isWholeGenome) {
|
|
33350
|
+
if (!this.wgFeatures) {
|
|
33351
|
+
if (this.supportsWholeGenome()) {
|
|
33352
|
+
if("wig" === this.config.type) {
|
|
33353
|
+
const allWgFeatures = await computeWGFeatures(this.featureCache.getAllFeatures(), this.genome, 1000000);
|
|
33354
|
+
this.wgFeatures = summarizeData(allWgFeatures, 0, bpPerPixel, windowFunction);
|
|
33355
|
+
} else {
|
|
33356
|
+
this.wgFeatures = await computeWGFeatures(this.featureCache.getAllFeatures(), this.genome, this.maxWGCount);
|
|
33357
|
+
}
|
|
33358
|
+
} else {
|
|
33359
|
+
this.wgFeatures = [];
|
|
33360
|
+
}
|
|
32854
33361
|
}
|
|
33362
|
+
return this.wgFeatures
|
|
33363
|
+
} else {
|
|
33364
|
+
return this.featureCache.queryFeatures(chr, start, end)
|
|
32855
33365
|
}
|
|
32856
|
-
features.sort(function (a, b) {
|
|
32857
|
-
return a.start - b.start
|
|
32858
|
-
});
|
|
32859
|
-
|
|
32860
|
-
return features
|
|
32861
33366
|
}
|
|
32862
33367
|
|
|
32863
|
-
|
|
32864
|
-
return
|
|
33368
|
+
async findFeatures(fn) {
|
|
33369
|
+
return this.featureCache ? this.featureCache.findFeatures(fn) : []
|
|
32865
33370
|
}
|
|
32866
33371
|
|
|
32867
|
-
|
|
32868
|
-
return this.
|
|
33372
|
+
supportsWholeGenome() {
|
|
33373
|
+
return !this.queryable // queryable (indexed, web services) sources don't support whole genome view
|
|
32869
33374
|
}
|
|
32870
|
-
}
|
|
32871
33375
|
|
|
32872
|
-
|
|
32873
|
-
|
|
32874
|
-
|
|
32875
|
-
|
|
32876
|
-
|
|
32877
|
-
|
|
32878
|
-
|
|
32879
|
-
const s = starts[i];
|
|
32880
|
-
const e = ends[i];
|
|
32881
|
-
if (e < bpStart) continue
|
|
32882
|
-
if (s > bpEnd) break
|
|
32883
|
-
features.push({
|
|
32884
|
-
chr: chr,
|
|
32885
|
-
start: s,
|
|
32886
|
-
end: e,
|
|
32887
|
-
value: data[i]
|
|
32888
|
-
});
|
|
33376
|
+
// TODO -- experimental, will only work for non-indexed sources
|
|
33377
|
+
getAllFeatures() {
|
|
33378
|
+
if (this.queryable || !this.featureCache) { // queryable sources don't support all features
|
|
33379
|
+
return []
|
|
33380
|
+
} else {
|
|
33381
|
+
return this.featureCache.getAllFeatures()
|
|
33382
|
+
}
|
|
32889
33383
|
}
|
|
32890
|
-
}
|
|
32891
33384
|
|
|
32892
|
-
function decodeVaryTile(tile, chr, bpStart, bpEnd, bpPerPixel, features) {
|
|
32893
33385
|
|
|
32894
|
-
|
|
32895
|
-
const starts = tile.start;
|
|
32896
|
-
const span = tile.span;
|
|
32897
|
-
const data = tile.data[0]; // Single track for now
|
|
32898
|
-
for (let i = 0; i < nPositions; i++) {
|
|
32899
|
-
const s = starts[i];
|
|
32900
|
-
const e = s + span;
|
|
32901
|
-
if (e < bpStart) continue
|
|
32902
|
-
if (s > bpEnd) break
|
|
32903
|
-
features.push({
|
|
32904
|
-
chr: chr,
|
|
32905
|
-
start: s,
|
|
32906
|
-
end: e,
|
|
32907
|
-
value: data[i]
|
|
32908
|
-
});
|
|
32909
|
-
}
|
|
32910
|
-
}
|
|
33386
|
+
async loadFeatures(chr, start, end, visibilityWindow) {
|
|
32911
33387
|
|
|
32912
|
-
|
|
33388
|
+
await this.getHeader();
|
|
32913
33389
|
|
|
32914
|
-
|
|
32915
|
-
|
|
32916
|
-
|
|
32917
|
-
const data = tile.data[0]; // Single track for now
|
|
33390
|
+
const reader = this.reader;
|
|
33391
|
+
let intervalStart = start;
|
|
33392
|
+
let intervalEnd = end;
|
|
32918
33393
|
|
|
32919
|
-
|
|
32920
|
-
|
|
32921
|
-
if (
|
|
32922
|
-
|
|
32923
|
-
|
|
32924
|
-
|
|
32925
|
-
|
|
32926
|
-
|
|
32927
|
-
|
|
32928
|
-
|
|
32929
|
-
|
|
33394
|
+
// chr aliasing
|
|
33395
|
+
let queryChr = chr;
|
|
33396
|
+
if (!this.chrAliasManager && this.reader && this.reader.sequenceNames) {
|
|
33397
|
+
this.chrAliasManager = new ChromAliasManager(this.reader.sequenceNames, this.genome);
|
|
33398
|
+
}
|
|
33399
|
+
if (this.chrAliasManager) {
|
|
33400
|
+
queryChr = await this.chrAliasManager.getAliasName(chr);
|
|
33401
|
+
}
|
|
33402
|
+
|
|
33403
|
+
// Use visibility window to potentially expand query interval.
|
|
33404
|
+
// This can save re-queries as we zoom out. Visibility window <= 0 is a special case
|
|
33405
|
+
// indicating whole chromosome should be read at once.
|
|
33406
|
+
if ((!visibilityWindow || visibilityWindow <= 0) && this.config.expandQuery !== false) {
|
|
33407
|
+
// Whole chromosome
|
|
33408
|
+
const chromosome = this.genome ? this.genome.getChromosome(queryChr) : undefined;
|
|
33409
|
+
intervalStart = 0;
|
|
33410
|
+
intervalEnd = Math.max(chromosome ? chromosome.bpLength : Number.MAX_SAFE_INTEGER, end);
|
|
33411
|
+
} else if (visibilityWindow > (end - start) && this.config.expandQuery !== false) {
|
|
33412
|
+
let expansionWindow = Math.min(4.1 * (end - start), visibilityWindow);
|
|
33413
|
+
if(this.config.minQuerySize && expansionWindow < this.config.minQuerySize) {
|
|
33414
|
+
expansionWindow = this.config.minQuerySize;
|
|
32930
33415
|
}
|
|
33416
|
+
intervalStart = Math.max(0, (start + end - expansionWindow) / 2);
|
|
33417
|
+
intervalEnd = intervalStart + expansionWindow;
|
|
32931
33418
|
}
|
|
32932
|
-
s = e;
|
|
32933
|
-
}
|
|
32934
|
-
}
|
|
32935
33419
|
|
|
33420
|
+
let features = await reader.readFeatures(queryChr, intervalStart, intervalEnd);
|
|
33421
|
+
if (this.queryable === undefined) {
|
|
33422
|
+
this.queryable = reader.indexed;
|
|
33423
|
+
}
|
|
32936
33424
|
|
|
32937
|
-
|
|
33425
|
+
const genomicInterval = this.queryable ?
|
|
33426
|
+
new GenomicInterval(chr, intervalStart, intervalEnd) :
|
|
33427
|
+
undefined;
|
|
32938
33428
|
|
|
32939
|
-
|
|
33429
|
+
if (features) {
|
|
32940
33430
|
|
|
32941
|
-
|
|
32942
|
-
|
|
32943
|
-
|
|
33431
|
+
// Assign overlapping features to rows
|
|
33432
|
+
if (this.config.format !== "wig" && this.config.type !== "junctions") {
|
|
33433
|
+
const maxRows = this.config.maxRows || Number.MAX_SAFE_INTEGER;
|
|
33434
|
+
packFeatures(features, maxRows);
|
|
33435
|
+
}
|
|
32944
33436
|
|
|
32945
|
-
|
|
33437
|
+
// Note - replacing previous cache with new one. genomicInterval is optional (might be undefined => includes all features)
|
|
33438
|
+
this.featureCache = new FeatureCache$1(features, this.genome, genomicInterval);
|
|
32946
33439
|
|
|
32947
|
-
|
|
33440
|
+
// If track is marked "searchable"< cache features by name -- use this with caution, memory intensive
|
|
33441
|
+
if (this.searchable) {
|
|
33442
|
+
this.addFeaturesToDB(features, this.config);
|
|
33443
|
+
}
|
|
33444
|
+
} else {
|
|
33445
|
+
this.featureCache = new FeatureCache$1([], genomicInterval); // Empty cache
|
|
33446
|
+
}
|
|
33447
|
+
}
|
|
33448
|
+
|
|
33449
|
+
addFeaturesToDB(featureList, config) {
|
|
33450
|
+
if (!this.featureMap) {
|
|
33451
|
+
this.featureMap = new Map();
|
|
33452
|
+
}
|
|
33453
|
+
const searchableFields = config.searchableFields || ["name", "transcript_id", "gene_id", "gene_name", "id"];
|
|
33454
|
+
for (let feature of featureList) {
|
|
33455
|
+
for (let field of searchableFields) {
|
|
33456
|
+
let key;
|
|
33457
|
+
if (typeof feature.getAttributeValue === 'function') {
|
|
33458
|
+
key = feature.getAttributeValue(field);
|
|
33459
|
+
}
|
|
33460
|
+
if (key) {
|
|
33461
|
+
key = key.replaceAll(' ', '+').toUpperCase();
|
|
33462
|
+
// If feature is already present keep largest one
|
|
33463
|
+
if (this.featureMap.has(key)) {
|
|
33464
|
+
const f2 = this.featureMap.get(key);
|
|
33465
|
+
if (feature.end - feature.start < f2.end - f2.start) {
|
|
33466
|
+
continue
|
|
33467
|
+
}
|
|
33468
|
+
}
|
|
33469
|
+
this.featureMap.set(key, feature);
|
|
33470
|
+
}
|
|
33471
|
+
}
|
|
33472
|
+
}
|
|
33473
|
+
}
|
|
33474
|
+
|
|
33475
|
+
search(term) {
|
|
33476
|
+
if (this.featureMap) {
|
|
33477
|
+
return this.featureMap.get(term.toUpperCase())
|
|
33478
|
+
}
|
|
33479
|
+
|
|
33480
|
+
}
|
|
32948
33481
|
}
|
|
32949
33482
|
|
|
32950
33483
|
/*
|
|
@@ -34236,7 +34769,7 @@
|
|
|
34236
34769
|
}
|
|
34237
34770
|
}
|
|
34238
34771
|
|
|
34239
|
-
const DEFAULT_COLOR$
|
|
34772
|
+
const DEFAULT_COLOR$1 = 'rgb(0, 0, 150)';
|
|
34240
34773
|
|
|
34241
34774
|
|
|
34242
34775
|
class FeatureTrack extends TrackBase {
|
|
@@ -34422,7 +34955,7 @@
|
|
|
34422
34955
|
}
|
|
34423
34956
|
|
|
34424
34957
|
|
|
34425
|
-
if (!this.
|
|
34958
|
+
if (!this.isMergedTrack) {
|
|
34426
34959
|
IGVGraphics.fillRect(context, 0, options.pixelTop, pixelWidth, pixelHeight, {'fillStyle': "rgb(255, 255, 255)"});
|
|
34427
34960
|
}
|
|
34428
34961
|
|
|
@@ -34733,7 +35266,7 @@
|
|
|
34733
35266
|
|
|
34734
35267
|
// If no explicit setting use the default
|
|
34735
35268
|
if (!color) {
|
|
34736
|
-
color = DEFAULT_COLOR$
|
|
35269
|
+
color = DEFAULT_COLOR$1; // Track default
|
|
34737
35270
|
}
|
|
34738
35271
|
|
|
34739
35272
|
if (feature.alpha && feature.alpha !== 1) {
|
|
@@ -37860,7 +38393,6 @@
|
|
|
37860
38393
|
}
|
|
37861
38394
|
|
|
37862
38395
|
const DEFAULT_GENOMES_URL = "https://igv.org/genomes/genomes.json";
|
|
37863
|
-
const BACKUP_GENOMES_URL = "https://s3.amazonaws.com/igv.org.genomes/genomes.json";
|
|
37864
38396
|
|
|
37865
38397
|
const GenomeUtils = {
|
|
37866
38398
|
|
|
@@ -37872,21 +38404,9 @@
|
|
|
37872
38404
|
|
|
37873
38405
|
// Get default genomes
|
|
37874
38406
|
if (config.loadDefaultGenomes !== false) {
|
|
37875
|
-
|
|
37876
|
-
|
|
37877
|
-
|
|
37878
|
-
processJson(jsonArray);
|
|
37879
|
-
} catch (e) {
|
|
37880
|
-
console.error(e);
|
|
37881
|
-
try {
|
|
37882
|
-
const url = BACKUP_GENOMES_URL;
|
|
37883
|
-
const jsonArray = await igvxhr.loadJson(url, {});
|
|
37884
|
-
processJson(jsonArray);
|
|
37885
|
-
} catch (e) {
|
|
37886
|
-
console.error(e);
|
|
37887
|
-
console.warn("Errors loading default genome definitions.");
|
|
37888
|
-
}
|
|
37889
|
-
}
|
|
38407
|
+
const url = DEFAULT_GENOMES_URL;
|
|
38408
|
+
const jsonArray = await igvxhr.loadJson(url, {timeout: 5000});
|
|
38409
|
+
processJson(jsonArray);
|
|
37890
38410
|
}
|
|
37891
38411
|
|
|
37892
38412
|
// Add user-defined genomes
|
|
@@ -37951,7 +38471,7 @@
|
|
|
37951
38471
|
}
|
|
37952
38472
|
}
|
|
37953
38473
|
|
|
37954
|
-
if(!reference) {
|
|
38474
|
+
if (!reference) {
|
|
37955
38475
|
alert.present(new Error(`Unknown genome id: ${genomeID}`), undefined);
|
|
37956
38476
|
}
|
|
37957
38477
|
}
|
|
@@ -41349,301 +41869,242 @@
|
|
|
41349
41869
|
* THE SOFTWARE.
|
|
41350
41870
|
*/
|
|
41351
41871
|
|
|
41352
|
-
class NavbarButton {
|
|
41353
|
-
|
|
41354
|
-
constructor(browser, parent, title, buttonLabel, imageSVG, imageHoverSVG, initialButtonState) {
|
|
41355
|
-
|
|
41356
|
-
this.browser = browser;
|
|
41357
|
-
|
|
41358
|
-
this.button = div({class: 'igv-navbar-text-button'});
|
|
41359
|
-
parent.appendChild(this.button);
|
|
41360
|
-
|
|
41361
|
-
if (Array.isArray(title)) {
|
|
41362
|
-
this.textContent = title[ 0 ];
|
|
41363
|
-
this.title = title[ 1 ];
|
|
41364
|
-
} else {
|
|
41365
|
-
this.textContent = this.title = title;
|
|
41366
|
-
}
|
|
41367
|
-
|
|
41368
|
-
this.buttonLabel = buttonLabel;
|
|
41369
|
-
|
|
41370
|
-
this.imageDictionary =
|
|
41371
|
-
{
|
|
41372
|
-
image: `url("data:image/svg+xml,${ encodeURIComponent(imageSVG) }")`,
|
|
41373
|
-
imageHover: `url("data:image/svg+xml,${ encodeURIComponent(imageHoverSVG) }")`,
|
|
41374
|
-
};
|
|
41375
|
-
|
|
41376
|
-
this.responsiveKey = 'text';
|
|
41377
|
-
|
|
41378
|
-
this.configureButton(this.textContent, this.title);
|
|
41379
|
-
|
|
41380
|
-
this.setState(initialButtonState);
|
|
41381
|
-
|
|
41382
|
-
browser.on('navbar-resize', navbarButtonCSSClass => {
|
|
41383
|
-
this.navbarResizeHandler(navbarButtonCSSClass);
|
|
41384
|
-
});
|
|
41385
|
-
|
|
41386
|
-
}
|
|
41387
|
-
|
|
41388
|
-
navbarResizeHandler(navbarButtonCSSClass) {
|
|
41389
|
-
const key = 'igv-navbar-icon-button' === navbarButtonCSSClass ? 'image' : 'text';
|
|
41390
|
-
if (key !== this.responsiveKey) {
|
|
41391
|
-
this.responsiveKey = key;
|
|
41392
|
-
this.configureButton(this.textContent, this.title);
|
|
41393
|
-
this.setState(undefined);
|
|
41394
|
-
}
|
|
41395
|
-
}
|
|
41396
|
-
|
|
41397
|
-
configureButton(textContent, title) {
|
|
41398
|
-
|
|
41399
|
-
this.groupElement = undefined;
|
|
41400
|
-
this.button.title = title;
|
|
41401
|
-
this.button.innerHTML = '';
|
|
41402
|
-
this.button.style.backgroundImage = 'none';
|
|
41403
|
-
this.button.classList.remove('igv-navbar-icon-button');
|
|
41404
|
-
this.button.classList.remove('igv-navbar-text-button');
|
|
41405
|
-
|
|
41406
|
-
'text' === this.responsiveKey ? this.configureTextButton(textContent) : this.configureIconButton();
|
|
41407
|
-
|
|
41408
|
-
}
|
|
41409
|
-
|
|
41410
|
-
configureTextButton(textContent) {
|
|
41411
|
-
|
|
41412
|
-
this.button.classList.add('igv-navbar-text-button');
|
|
41413
|
-
|
|
41414
|
-
const tempDiv = document.createElement('div');
|
|
41415
|
-
tempDiv.innerHTML = this.buttonLabel;
|
|
41416
|
-
const svgRoot = tempDiv.firstChild;
|
|
41417
|
-
this.button.appendChild(svgRoot);
|
|
41418
|
-
|
|
41419
|
-
this.groupElement = svgRoot.querySelector('#igv-navbar-button-group');
|
|
41420
|
-
|
|
41421
|
-
const tspanElement = svgRoot.querySelector('#igv-navbar-button-label');
|
|
41422
|
-
tspanElement.textContent = textContent;
|
|
41423
|
-
}
|
|
41424
|
-
|
|
41425
|
-
configureIconButton() {
|
|
41426
|
-
this.button.classList.add('igv-navbar-icon-button');
|
|
41427
|
-
}
|
|
41428
|
-
|
|
41429
|
-
setState(doHover) {
|
|
41430
|
-
|
|
41431
|
-
if (undefined !== doHover) {
|
|
41432
|
-
this.doHover = doHover;
|
|
41433
|
-
}
|
|
41434
|
-
|
|
41435
|
-
'text' === this.responsiveKey ? this.setTextButtonState(this.doHover) : this.setIconButtonState(this.doHover);
|
|
41436
|
-
|
|
41437
|
-
}
|
|
41438
|
-
|
|
41439
|
-
setTextButtonState(doHover) {
|
|
41440
|
-
this.groupElement.classList.remove(...this.groupElement.classList);
|
|
41441
|
-
const className = true === doHover ? 'igv-navbar-text-button-svg-hover' : 'igv-navbar-text-button-svg-inactive';
|
|
41442
|
-
this.groupElement.classList.add(className);
|
|
41443
|
-
}
|
|
41444
|
-
|
|
41445
|
-
setIconButtonState(doHover) {
|
|
41446
|
-
this.button.style.backgroundImage = true === doHover ? this.imageDictionary.imageHover : this.imageDictionary.image;
|
|
41447
|
-
}
|
|
41448
|
-
|
|
41449
|
-
show() {
|
|
41450
|
-
// this.button.style.display = 'block'
|
|
41451
|
-
this.button.style.display = 'flex';
|
|
41452
|
-
}
|
|
41453
|
-
|
|
41454
|
-
hide() {
|
|
41455
|
-
this.button.style.display = 'none';
|
|
41456
|
-
}
|
|
41457
|
-
|
|
41458
|
-
setVisibility(isVisible) {
|
|
41459
|
-
if (true === isVisible) {
|
|
41460
|
-
this.show();
|
|
41461
|
-
} else {
|
|
41462
|
-
this.hide();
|
|
41463
|
-
}
|
|
41464
|
-
}
|
|
41465
|
-
|
|
41466
|
-
static currentNavbarButtonClass(browser) {
|
|
41467
|
-
const el = browser.$navigation.get(0).querySelector('.igv-navbar-text-button');
|
|
41468
|
-
return el ? 'igv-navbar-text-button' : 'igv-navbar-icon-button'
|
|
41469
|
-
}
|
|
41470
|
-
}
|
|
41471
|
-
|
|
41472
|
-
const overlayTrackImage =
|
|
41473
|
-
`<svg width="625px" height="625px" viewBox="0 0 625 625" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
41474
|
-
<title>Overlay Tracks</title>
|
|
41475
|
-
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
41476
|
-
<g id="Overlay-Tracks">
|
|
41477
|
-
<rect id="backdrop" stroke="#737373" stroke-width="12" fill="#FFFFFF" x="6" y="6" width="613" height="613" rx="135"></rect>
|
|
41478
|
-
<g id="layer-group" transform="translate(3, 127)">
|
|
41479
|
-
<rect id="a" stroke="#737373" stroke-width="24" fill="#A1A1A1" x="12" y="12" width="332" height="139"></rect>
|
|
41480
|
-
<rect id="a---hold-out" fill="#A1A1A1" x="9" y="25" width="324" height="115"></rect>
|
|
41481
|
-
<rect id="b" stroke="#737373" stroke-width="24" fill="#C9C9C9" x="81" y="103" width="474" height="139"></rect>
|
|
41482
|
-
<rect id="c" stroke="#737373" stroke-width="24" fill="#ECECEC" x="238" y="214" width="372" height="139"></rect>
|
|
41483
|
-
<rect id="c---hold-out" fill="#ECECEC" x="250" y="226" width="372" height="115"></rect>
|
|
41484
|
-
</g>
|
|
41485
|
-
<rect id="over-border" stroke="#737373" stroke-width="12" x="6" y="6" width="613" height="613" rx="135"></rect>
|
|
41486
|
-
</g>
|
|
41487
|
-
</g>
|
|
41488
|
-
</svg>`;
|
|
41489
|
-
|
|
41490
|
-
const overlayTrackImageHover =
|
|
41491
|
-
`<svg width="625px" height="625px" viewBox="0 0 625 625" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
41492
|
-
<title>Overlay Tracks Hover</title>
|
|
41493
|
-
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
41494
|
-
<g id="Overlay-Tracks-Hover">
|
|
41495
|
-
<rect id="backdrop-copy" stroke="#737373" stroke-width="12" fill="#737373" x="6" y="6" width="613" height="613" rx="135"></rect>
|
|
41496
|
-
<g id="layer-group" transform="translate(3, 127)">
|
|
41497
|
-
<rect id="a" stroke="#FFFFFF" stroke-width="24" fill="#A1A1A1" x="12" y="12" width="332" height="139"></rect>
|
|
41498
|
-
<rect id="a---hold-out" fill="#A1A1A1" x="9" y="25" width="324" height="115"></rect>
|
|
41499
|
-
<rect id="b" stroke="#FFFFFF" stroke-width="24" fill="#C9C9C9" x="81" y="103" width="474" height="139"></rect>
|
|
41500
|
-
<rect id="c" stroke="#FFFFFF" stroke-width="24" fill="#ECECEC" x="238" y="214" width="372" height="139"></rect>
|
|
41501
|
-
<rect id="c---hold-out" fill="#ECECEC" x="250" y="226" width="372" height="115"></rect>
|
|
41502
|
-
</g>
|
|
41503
|
-
<rect id="over-border-copy" stroke="#737373" stroke-width="12" x="6" y="6" width="613" height="613" rx="135"></rect>
|
|
41504
|
-
</g>
|
|
41505
|
-
</g>
|
|
41506
|
-
</svg>`;
|
|
41507
|
-
|
|
41508
|
-
const buttonLabel =
|
|
41509
|
-
`<svg width="80px" height="18px" viewBox="0 0 80 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
41510
|
-
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
41511
|
-
<g id="igv-navbar-button-group">
|
|
41512
|
-
<rect id="Rectangle" x="0.5" y="0.5" width="79" height="17" rx="6"></rect>
|
|
41513
|
-
<text id="igv-text-button-label" x="50%" y="50%" dy=".1em" font-family="Helvetica" font-size="12" font-weight="normal" letter-spacing="-0.372">
|
|
41514
|
-
<tspan id="igv-navbar-button-label"></tspan>
|
|
41515
|
-
</text>
|
|
41516
|
-
</g>
|
|
41517
|
-
</g>
|
|
41518
|
-
</svg>`;
|
|
41519
|
-
|
|
41520
|
-
const sampleNameButtonLabel =
|
|
41521
|
-
`<svg width="90px" height="20px" viewBox="0 0 90 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
41522
|
-
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
41523
|
-
<g id="igv-navbar-button-group">
|
|
41524
|
-
<rect id="Rectangle" x="0.5" y="0.5" width="89" height="18" rx="6"></rect>
|
|
41525
|
-
<text id="igv-text-button-label" x="50%" y="50%" dy=".1em" font-family="Helvetica" font-size="12" font-weight="normal" letter-spacing="-0.372">
|
|
41526
|
-
<tspan id="igv-navbar-button-label"></tspan>
|
|
41527
|
-
</text>
|
|
41528
|
-
</g>
|
|
41529
|
-
</g>
|
|
41530
|
-
</svg>`;
|
|
41531
|
-
|
|
41532
|
-
|
|
41533
|
-
|
|
41534
|
-
|
|
41535
|
-
|
|
41536
|
-
|
|
41537
|
-
|
|
41538
|
-
|
|
41539
|
-
|
|
41540
|
-
|
|
41541
|
-
|
|
41542
|
-
|
|
41543
|
-
|
|
41544
|
-
|
|
41545
|
-
|
|
41546
|
-
|
|
41547
|
-
|
|
41548
|
-
|
|
41549
|
-
|
|
41550
|
-
|
|
41551
|
-
|
|
41552
|
-
|
|
41553
|
-
|
|
41554
|
-
|
|
41555
|
-
|
|
41556
|
-
|
|
41557
|
-
};
|
|
41558
|
-
|
|
41559
|
-
// tick
|
|
41560
|
-
IGVGraphics.strokeLine(ctx, xTickStart, shim * height, xTickEnd, shim * height, properties);
|
|
41561
|
-
IGVGraphics.fillText(ctx, prettyPrint(flipAxis ? this.dataRange.min : this.dataRange.max), xTickStart + 4, shim * height + 12, properties);
|
|
41562
|
-
|
|
41563
|
-
const y = (1.0 - shim) * height;
|
|
41564
|
-
|
|
41565
|
-
// tick
|
|
41566
|
-
IGVGraphics.strokeLine(ctx, xTickStart, y, xTickEnd, y, properties);
|
|
41567
|
-
IGVGraphics.fillText(ctx, prettyPrint(flipAxis ? this.dataRange.max : this.dataRange.min), xTickStart + 4, y - 4, properties);
|
|
41568
|
-
|
|
41569
|
-
// vertical axis
|
|
41570
|
-
IGVGraphics.strokeLine(ctx, xTickEnd, shim * height, xTickEnd, y, properties);
|
|
41571
|
-
|
|
41572
|
-
function prettyPrint(number) {
|
|
41573
|
-
|
|
41574
|
-
if (number === 0) {
|
|
41575
|
-
return "0"
|
|
41576
|
-
} else if (Math.abs(number) >= 10) {
|
|
41577
|
-
return number.toFixed()
|
|
41578
|
-
} else if (Math.abs(number) >= 1) {
|
|
41579
|
-
return number.toFixed(1)
|
|
41580
|
-
} else if (Math.abs(number) >= 0.1) {
|
|
41581
|
-
return number.toFixed(2)
|
|
41582
|
-
} else {
|
|
41583
|
-
return number.toExponential(1)
|
|
41584
|
-
}
|
|
41585
|
-
}
|
|
41586
|
-
}
|
|
41587
|
-
|
|
41588
|
-
/*
|
|
41589
|
-
* The MIT License (MIT)
|
|
41590
|
-
*
|
|
41591
|
-
* Copyright (c) 2016-2017 The Regents of the University of California
|
|
41592
|
-
* Author: Jim Robinson
|
|
41593
|
-
*
|
|
41594
|
-
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
41595
|
-
* of this software and associated documentation files (the "Software"), to deal
|
|
41596
|
-
* in the Software without restriction, including without limitation the rights
|
|
41597
|
-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
41598
|
-
* copies of the Software, and to permit persons to whom the Software is
|
|
41599
|
-
* furnished to do so, subject to the following conditions:
|
|
41600
|
-
*
|
|
41601
|
-
* The above copyright notice and this permission notice shall be included in
|
|
41602
|
-
* all copies or substantial portions of the Software.
|
|
41603
|
-
*
|
|
41604
|
-
*
|
|
41605
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
41606
|
-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
41607
|
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
41608
|
-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
41609
|
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
41610
|
-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
41611
|
-
* THE SOFTWARE.
|
|
41612
|
-
*/
|
|
41613
|
-
|
|
41872
|
+
class NavbarButton {
|
|
41873
|
+
|
|
41874
|
+
constructor(browser, parent, title, buttonLabel, imageSVG, imageHoverSVG, initialButtonState) {
|
|
41875
|
+
|
|
41876
|
+
this.browser = browser;
|
|
41877
|
+
|
|
41878
|
+
this.button = div({class: 'igv-navbar-text-button'});
|
|
41879
|
+
parent.appendChild(this.button);
|
|
41880
|
+
|
|
41881
|
+
if (Array.isArray(title)) {
|
|
41882
|
+
this.textContent = title[ 0 ];
|
|
41883
|
+
this.title = title[ 1 ];
|
|
41884
|
+
} else {
|
|
41885
|
+
this.textContent = this.title = title;
|
|
41886
|
+
}
|
|
41887
|
+
|
|
41888
|
+
this.buttonLabel = buttonLabel;
|
|
41889
|
+
|
|
41890
|
+
this.imageDictionary =
|
|
41891
|
+
{
|
|
41892
|
+
image: `url("data:image/svg+xml,${ encodeURIComponent(imageSVG) }")`,
|
|
41893
|
+
imageHover: `url("data:image/svg+xml,${ encodeURIComponent(imageHoverSVG) }")`,
|
|
41894
|
+
};
|
|
41895
|
+
|
|
41896
|
+
this.responsiveKey = 'text';
|
|
41897
|
+
|
|
41898
|
+
this.configureButton(this.textContent, this.title);
|
|
41899
|
+
|
|
41900
|
+
this.setState(initialButtonState);
|
|
41901
|
+
|
|
41902
|
+
browser.on('navbar-resize', navbarButtonCSSClass => {
|
|
41903
|
+
this.navbarResizeHandler(navbarButtonCSSClass);
|
|
41904
|
+
});
|
|
41905
|
+
|
|
41906
|
+
}
|
|
41907
|
+
|
|
41908
|
+
navbarResizeHandler(navbarButtonCSSClass) {
|
|
41909
|
+
const key = 'igv-navbar-icon-button' === navbarButtonCSSClass ? 'image' : 'text';
|
|
41910
|
+
if (key !== this.responsiveKey) {
|
|
41911
|
+
this.responsiveKey = key;
|
|
41912
|
+
this.configureButton(this.textContent, this.title);
|
|
41913
|
+
this.setState(undefined);
|
|
41914
|
+
}
|
|
41915
|
+
}
|
|
41916
|
+
|
|
41917
|
+
configureButton(textContent, title) {
|
|
41918
|
+
|
|
41919
|
+
this.groupElement = undefined;
|
|
41920
|
+
this.button.title = title;
|
|
41921
|
+
this.button.innerHTML = '';
|
|
41922
|
+
this.button.style.backgroundImage = 'none';
|
|
41923
|
+
this.button.classList.remove('igv-navbar-icon-button');
|
|
41924
|
+
this.button.classList.remove('igv-navbar-text-button');
|
|
41925
|
+
|
|
41926
|
+
'text' === this.responsiveKey ? this.configureTextButton(textContent) : this.configureIconButton();
|
|
41927
|
+
|
|
41928
|
+
}
|
|
41929
|
+
|
|
41930
|
+
configureTextButton(textContent) {
|
|
41931
|
+
|
|
41932
|
+
this.button.classList.add('igv-navbar-text-button');
|
|
41933
|
+
|
|
41934
|
+
const tempDiv = document.createElement('div');
|
|
41935
|
+
tempDiv.innerHTML = this.buttonLabel;
|
|
41936
|
+
const svgRoot = tempDiv.firstChild;
|
|
41937
|
+
this.button.appendChild(svgRoot);
|
|
41938
|
+
|
|
41939
|
+
this.groupElement = svgRoot.querySelector('#igv-navbar-button-group');
|
|
41940
|
+
|
|
41941
|
+
const tspanElement = svgRoot.querySelector('#igv-navbar-button-label');
|
|
41942
|
+
tspanElement.textContent = textContent;
|
|
41943
|
+
}
|
|
41944
|
+
|
|
41945
|
+
configureIconButton() {
|
|
41946
|
+
this.button.classList.add('igv-navbar-icon-button');
|
|
41947
|
+
}
|
|
41948
|
+
|
|
41949
|
+
setState(doHover) {
|
|
41950
|
+
|
|
41951
|
+
if (undefined !== doHover) {
|
|
41952
|
+
this.doHover = doHover;
|
|
41953
|
+
}
|
|
41954
|
+
|
|
41955
|
+
'text' === this.responsiveKey ? this.setTextButtonState(this.doHover) : this.setIconButtonState(this.doHover);
|
|
41956
|
+
|
|
41957
|
+
}
|
|
41958
|
+
|
|
41959
|
+
setTextButtonState(doHover) {
|
|
41960
|
+
this.groupElement.classList.remove(...this.groupElement.classList);
|
|
41961
|
+
const className = true === doHover ? 'igv-navbar-text-button-svg-hover' : 'igv-navbar-text-button-svg-inactive';
|
|
41962
|
+
this.groupElement.classList.add(className);
|
|
41963
|
+
}
|
|
41964
|
+
|
|
41965
|
+
setIconButtonState(doHover) {
|
|
41966
|
+
this.button.style.backgroundImage = true === doHover ? this.imageDictionary.imageHover : this.imageDictionary.image;
|
|
41967
|
+
}
|
|
41968
|
+
|
|
41969
|
+
show() {
|
|
41970
|
+
// this.button.style.display = 'block'
|
|
41971
|
+
this.button.style.display = 'flex';
|
|
41972
|
+
}
|
|
41973
|
+
|
|
41974
|
+
hide() {
|
|
41975
|
+
this.button.style.display = 'none';
|
|
41976
|
+
}
|
|
41977
|
+
|
|
41978
|
+
setVisibility(isVisible) {
|
|
41979
|
+
if (true === isVisible) {
|
|
41980
|
+
this.show();
|
|
41981
|
+
} else {
|
|
41982
|
+
this.hide();
|
|
41983
|
+
}
|
|
41984
|
+
}
|
|
41985
|
+
|
|
41986
|
+
static currentNavbarButtonClass(browser) {
|
|
41987
|
+
const el = browser.$navigation.get(0).querySelector('.igv-navbar-text-button');
|
|
41988
|
+
return el ? 'igv-navbar-text-button' : 'igv-navbar-icon-button'
|
|
41989
|
+
}
|
|
41990
|
+
}
|
|
41991
|
+
|
|
41992
|
+
const overlayTrackImage =
|
|
41993
|
+
`<svg width="625px" height="625px" viewBox="0 0 625 625" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
41994
|
+
<title>Overlay Tracks</title>
|
|
41995
|
+
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
41996
|
+
<g id="Overlay-Tracks">
|
|
41997
|
+
<rect id="backdrop" stroke="#737373" stroke-width="12" fill="#FFFFFF" x="6" y="6" width="613" height="613" rx="135"></rect>
|
|
41998
|
+
<g id="layer-group" transform="translate(3, 127)">
|
|
41999
|
+
<rect id="a" stroke="#737373" stroke-width="24" fill="#A1A1A1" x="12" y="12" width="332" height="139"></rect>
|
|
42000
|
+
<rect id="a---hold-out" fill="#A1A1A1" x="9" y="25" width="324" height="115"></rect>
|
|
42001
|
+
<rect id="b" stroke="#737373" stroke-width="24" fill="#C9C9C9" x="81" y="103" width="474" height="139"></rect>
|
|
42002
|
+
<rect id="c" stroke="#737373" stroke-width="24" fill="#ECECEC" x="238" y="214" width="372" height="139"></rect>
|
|
42003
|
+
<rect id="c---hold-out" fill="#ECECEC" x="250" y="226" width="372" height="115"></rect>
|
|
42004
|
+
</g>
|
|
42005
|
+
<rect id="over-border" stroke="#737373" stroke-width="12" x="6" y="6" width="613" height="613" rx="135"></rect>
|
|
42006
|
+
</g>
|
|
42007
|
+
</g>
|
|
42008
|
+
</svg>`;
|
|
42009
|
+
|
|
42010
|
+
const overlayTrackImageHover =
|
|
42011
|
+
`<svg width="625px" height="625px" viewBox="0 0 625 625" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
42012
|
+
<title>Overlay Tracks Hover</title>
|
|
42013
|
+
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
42014
|
+
<g id="Overlay-Tracks-Hover">
|
|
42015
|
+
<rect id="backdrop-copy" stroke="#737373" stroke-width="12" fill="#737373" x="6" y="6" width="613" height="613" rx="135"></rect>
|
|
42016
|
+
<g id="layer-group" transform="translate(3, 127)">
|
|
42017
|
+
<rect id="a" stroke="#FFFFFF" stroke-width="24" fill="#A1A1A1" x="12" y="12" width="332" height="139"></rect>
|
|
42018
|
+
<rect id="a---hold-out" fill="#A1A1A1" x="9" y="25" width="324" height="115"></rect>
|
|
42019
|
+
<rect id="b" stroke="#FFFFFF" stroke-width="24" fill="#C9C9C9" x="81" y="103" width="474" height="139"></rect>
|
|
42020
|
+
<rect id="c" stroke="#FFFFFF" stroke-width="24" fill="#ECECEC" x="238" y="214" width="372" height="139"></rect>
|
|
42021
|
+
<rect id="c---hold-out" fill="#ECECEC" x="250" y="226" width="372" height="115"></rect>
|
|
42022
|
+
</g>
|
|
42023
|
+
<rect id="over-border-copy" stroke="#737373" stroke-width="12" x="6" y="6" width="613" height="613" rx="135"></rect>
|
|
42024
|
+
</g>
|
|
42025
|
+
</g>
|
|
42026
|
+
</svg>`;
|
|
42027
|
+
|
|
42028
|
+
const buttonLabel =
|
|
42029
|
+
`<svg width="80px" height="18px" viewBox="0 0 80 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
42030
|
+
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
42031
|
+
<g id="igv-navbar-button-group">
|
|
42032
|
+
<rect id="Rectangle" x="0.5" y="0.5" width="79" height="17" rx="6"></rect>
|
|
42033
|
+
<text id="igv-text-button-label" x="50%" y="50%" dy=".1em" font-family="Helvetica" font-size="12" font-weight="normal" letter-spacing="-0.372">
|
|
42034
|
+
<tspan id="igv-navbar-button-label"></tspan>
|
|
42035
|
+
</text>
|
|
42036
|
+
</g>
|
|
42037
|
+
</g>
|
|
42038
|
+
</svg>`;
|
|
42039
|
+
|
|
42040
|
+
const sampleNameButtonLabel =
|
|
42041
|
+
`<svg width="90px" height="20px" viewBox="0 0 90 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
42042
|
+
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
42043
|
+
<g id="igv-navbar-button-group">
|
|
42044
|
+
<rect id="Rectangle" x="0.5" y="0.5" width="89" height="18" rx="6"></rect>
|
|
42045
|
+
<text id="igv-text-button-label" x="50%" y="50%" dy=".1em" font-family="Helvetica" font-size="12" font-weight="normal" letter-spacing="-0.372">
|
|
42046
|
+
<tspan id="igv-navbar-button-label"></tspan>
|
|
42047
|
+
</text>
|
|
42048
|
+
</g>
|
|
42049
|
+
</g>
|
|
42050
|
+
</svg>`;
|
|
42051
|
+
|
|
42052
|
+
/*
|
|
42053
|
+
* The MIT License (MIT)
|
|
42054
|
+
*
|
|
42055
|
+
* Copyright (c) 2016-2017 The Regents of the University of California
|
|
42056
|
+
* Author: Jim Robinson
|
|
42057
|
+
*
|
|
42058
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
42059
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
42060
|
+
* in the Software without restriction, including without limitation the rights
|
|
42061
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
42062
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
42063
|
+
* furnished to do so, subject to the following conditions:
|
|
42064
|
+
*
|
|
42065
|
+
* The above copyright notice and this permission notice shall be included in
|
|
42066
|
+
* all copies or substantial portions of the Software.
|
|
42067
|
+
*
|
|
42068
|
+
*
|
|
42069
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
42070
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
42071
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
42072
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
42073
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
42074
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
42075
|
+
* THE SOFTWARE.
|
|
42076
|
+
*/
|
|
41614
42077
|
|
|
41615
42078
|
/**
|
|
41616
|
-
* Represents 2 or more
|
|
42079
|
+
* Represents 2 or more tracks overlaid on a common viewport.
|
|
41617
42080
|
*/
|
|
41618
42081
|
class MergedTrack extends TrackBase {
|
|
41619
42082
|
|
|
41620
42083
|
static defaults = {
|
|
42084
|
+
autoscale: undefined,
|
|
41621
42085
|
alpha: 0.5,
|
|
41622
42086
|
height: 50
|
|
41623
42087
|
}
|
|
41624
42088
|
|
|
41625
|
-
constructor(config, browser) {
|
|
42089
|
+
constructor(config, browser, tracks) {
|
|
41626
42090
|
super(config, browser);
|
|
41627
42091
|
this.type = "merged";
|
|
41628
|
-
this.featureType = "numeric";
|
|
41629
42092
|
this.paintAxis = paintAxis;
|
|
41630
42093
|
this.graphType = config.graphType;
|
|
41631
|
-
|
|
41632
|
-
|
|
41633
|
-
|
|
41634
|
-
|
|
41635
|
-
throw Error("Error: no tracks defined for merged track" + config)
|
|
42094
|
+
if (tracks) {
|
|
42095
|
+
this.tracks = tracks; // Dynamic creation, actual track objects (not configurations)
|
|
42096
|
+
} else {
|
|
42097
|
+
this.tracks = [];
|
|
41636
42098
|
}
|
|
41637
|
-
super.init(config);
|
|
41638
42099
|
}
|
|
41639
42100
|
|
|
42101
|
+
|
|
41640
42102
|
async postInit() {
|
|
41641
42103
|
|
|
41642
|
-
this.tracks = [];
|
|
41643
42104
|
if (this.config.tracks) {
|
|
41644
|
-
//
|
|
42105
|
+
// Track configurations, this indicates a configured merged track as opposed to dynamic merge through the UI
|
|
42106
|
+
// Actual track objects need to be created.
|
|
41645
42107
|
for (let tconf of this.config.tracks) {
|
|
41646
|
-
tconf.isMergedTrack = true;
|
|
41647
42108
|
const t = await this.browser.createTrack(tconf);
|
|
41648
42109
|
if (t) {
|
|
41649
42110
|
this.tracks.push(t);
|
|
@@ -41654,24 +42115,27 @@
|
|
|
41654
42115
|
await t.postInit();
|
|
41655
42116
|
}
|
|
41656
42117
|
}
|
|
41657
|
-
//
|
|
41658
|
-
|
|
41659
|
-
|
|
41660
|
-
|
|
41661
|
-
this.dataRange = {
|
|
41662
|
-
min: this.config.min || 0,
|
|
41663
|
-
max: this.config.max
|
|
41664
|
-
};
|
|
41665
|
-
} else {
|
|
41666
|
-
this.autoscale = !this.tracks.every(t => t.config.autoscale || t.config.max !== undefined);
|
|
42118
|
+
// Default to autoscale unless scale if range or autoscale is not otherwise defined
|
|
42119
|
+
const allTracksSpecified = this.config.tracks.every(config => config.autoscale !== undefined || config.max !== undefined);
|
|
42120
|
+
if (!allTracksSpecified) {
|
|
42121
|
+
this.config.autoscale = this.config.max === undefined;
|
|
41667
42122
|
}
|
|
41668
|
-
} else {
|
|
41669
|
-
// Dynamic merged track
|
|
41670
|
-
this.tracks = this.config._tracks;
|
|
41671
|
-
this.autoscale = false;
|
|
41672
|
-
delete this.config._tracks;
|
|
41673
42123
|
}
|
|
41674
42124
|
|
|
42125
|
+
// Mark constitutive tracks as merged.
|
|
42126
|
+
for (let t of this.tracks) t.isMergedTrack = true;
|
|
42127
|
+
|
|
42128
|
+
// Explicit settings -- these will override any individual track settings
|
|
42129
|
+
if(this.config.autoscale) {
|
|
42130
|
+
this.autoscale = this.config.autoscale;
|
|
42131
|
+
} else if (this.config.max !== undefined) {
|
|
42132
|
+
this.setDataRange ({
|
|
42133
|
+
min: this.config.min || 0,
|
|
42134
|
+
max: this.config.max
|
|
42135
|
+
});
|
|
42136
|
+
}
|
|
42137
|
+
|
|
42138
|
+
|
|
41675
42139
|
if (this.config.flipAxis !== undefined) {
|
|
41676
42140
|
for (let t of this.tracks) t.flipAxis = this.config.flipAxis;
|
|
41677
42141
|
}
|
|
@@ -41681,25 +42145,28 @@
|
|
|
41681
42145
|
}
|
|
41682
42146
|
|
|
41683
42147
|
this.resolutionAware = this.tracks.some(t => t.resolutionAware);
|
|
41684
|
-
|
|
41685
42148
|
}
|
|
41686
42149
|
|
|
41687
42150
|
set flipAxis(b) {
|
|
41688
42151
|
this.config.flipAxis = b;
|
|
41689
|
-
for (let t of this.tracks)
|
|
42152
|
+
for (let t of numericTracks(this.tracks)) {
|
|
42153
|
+
t.flipAxis = b;
|
|
42154
|
+
}
|
|
41690
42155
|
}
|
|
41691
42156
|
|
|
41692
42157
|
get flipAxis() {
|
|
41693
|
-
return this.tracks.every(t => t.flipAxis)
|
|
42158
|
+
return numericTracks(this.tracks).every(t => t.flipAxis)
|
|
41694
42159
|
}
|
|
41695
42160
|
|
|
41696
42161
|
set logScale(b) {
|
|
41697
42162
|
this.config.logScale = b;
|
|
41698
|
-
for (let t of this.tracks)
|
|
42163
|
+
for (let t of numericTracks(this.tracks)) {
|
|
42164
|
+
t.logScale = b;
|
|
42165
|
+
}
|
|
41699
42166
|
}
|
|
41700
42167
|
|
|
41701
42168
|
get logScale() {
|
|
41702
|
-
return this.tracks.every(t => t.logScale)
|
|
42169
|
+
return numericTracks(this.tracks).every(t => t.logScale)
|
|
41703
42170
|
}
|
|
41704
42171
|
|
|
41705
42172
|
get height() {
|
|
@@ -41717,49 +42184,76 @@
|
|
|
41717
42184
|
}
|
|
41718
42185
|
}
|
|
41719
42186
|
|
|
41720
|
-
|
|
41721
|
-
|
|
41722
|
-
if
|
|
41723
|
-
|
|
42187
|
+
set autoscale(b) {
|
|
42188
|
+
this._autoscale = b;
|
|
42189
|
+
if(b === false && this.tracks) {
|
|
42190
|
+
for(let t of this.tracks) t.autoscale = false;
|
|
41724
42191
|
}
|
|
42192
|
+
}
|
|
41725
42193
|
|
|
41726
|
-
|
|
41727
|
-
|
|
41728
|
-
|
|
41729
|
-
}
|
|
42194
|
+
get autoscale() {
|
|
42195
|
+
return this._autoscale
|
|
42196
|
+
}
|
|
41730
42197
|
|
|
41731
|
-
|
|
41732
|
-
|
|
41733
|
-
|
|
41734
|
-
|
|
42198
|
+
/**
|
|
42199
|
+
* Set the data range of all constitutive numeric tracks. This method is called from the menu item, i.e. an explicit
|
|
42200
|
+
* setting, so it should disable autoscale as well.
|
|
42201
|
+
*
|
|
42202
|
+
* @param min
|
|
42203
|
+
* @param max
|
|
42204
|
+
*/
|
|
41735
42205
|
|
|
41736
|
-
|
|
41737
|
-
|
|
41738
|
-
|
|
42206
|
+
setDataRange({min, max}) {
|
|
42207
|
+
this.autoscale = false;
|
|
42208
|
+
for (const track of numericTracks(this.tracks)) {
|
|
42209
|
+
track.dataRange = {min, max};
|
|
42210
|
+
track.autoscale = false;
|
|
42211
|
+
track.autoscaleGroup = false;
|
|
41739
42212
|
}
|
|
42213
|
+
}
|
|
41740
42214
|
|
|
41741
|
-
|
|
42215
|
+
set dataRange({min, max}) {
|
|
42216
|
+
for (const track of numericTracks(this.tracks)) {
|
|
42217
|
+
track.dataRange = {min, max};
|
|
42218
|
+
}
|
|
41742
42219
|
}
|
|
41743
42220
|
|
|
41744
|
-
|
|
41745
|
-
|
|
41746
|
-
|
|
42221
|
+
/**
|
|
42222
|
+
* Return a DataRang {min, max} if all constitutive numeric tracks have identical range. A numeric track is defined
|
|
42223
|
+
* as a track with a data range. Otherwise return undefined.
|
|
42224
|
+
*
|
|
42225
|
+
* @returns {{min: any, max: any}|undefined}
|
|
42226
|
+
*/
|
|
42227
|
+
get dataRange() {
|
|
42228
|
+
if(this.tracks) {
|
|
42229
|
+
const num = numericTracks(this.tracks);
|
|
42230
|
+
if (num.length > 0) {
|
|
42231
|
+
const firstRange = num[0].dataRange;
|
|
42232
|
+
if (num.every(t => t.dataRange && t.dataRange.min === firstRange.min && t.dataRange.max === firstRange.max)) {
|
|
42233
|
+
return firstRange
|
|
42234
|
+
}
|
|
42235
|
+
}
|
|
41747
42236
|
}
|
|
42237
|
+
return undefined
|
|
41748
42238
|
}
|
|
41749
42239
|
|
|
42240
|
+
|
|
41750
42241
|
menuItemList() {
|
|
41751
42242
|
const items = [];
|
|
41752
|
-
if (this.
|
|
41753
|
-
items.push({
|
|
41754
|
-
label: "Flip y-axis",
|
|
41755
|
-
click: function flipYAxisHandler() {
|
|
41756
|
-
this.flipAxis = !this.flipAxis;
|
|
41757
|
-
this.trackView.repaintViews();
|
|
41758
|
-
}
|
|
41759
|
-
});
|
|
41760
|
-
}
|
|
42243
|
+
if (numericTracks(this.tracks).length > 0) {
|
|
41761
42244
|
|
|
41762
|
-
|
|
42245
|
+
if (this.flipAxis !== undefined) {
|
|
42246
|
+
items.push({
|
|
42247
|
+
label: "Flip y-axis",
|
|
42248
|
+
click: function flipYAxisHandler() {
|
|
42249
|
+
this.flipAxis = !this.flipAxis;
|
|
42250
|
+
this.trackView.repaintViews();
|
|
42251
|
+
}
|
|
42252
|
+
});
|
|
42253
|
+
}
|
|
42254
|
+
|
|
42255
|
+
items.push(...this.numericDataMenuItems());
|
|
42256
|
+
}
|
|
41763
42257
|
|
|
41764
42258
|
items.push('<hr/>');
|
|
41765
42259
|
items.push(this.overlayTrackAlphaAdjustmentMenuItem());
|
|
@@ -41776,7 +42270,14 @@
|
|
|
41776
42270
|
|
|
41777
42271
|
const promises = this.tracks.map((t) => t.getFeatures(chr, bpStart, bpEnd, bpPerPixel));
|
|
41778
42272
|
const featureArrays = await Promise.all(promises);
|
|
41779
|
-
|
|
42273
|
+
|
|
42274
|
+
if (featureArrays.every((arr) => arr.length === 0)){
|
|
42275
|
+
return new MergedFeatureCollection([], [])
|
|
42276
|
+
}
|
|
42277
|
+
else {
|
|
42278
|
+
const trackNames = this.tracks.map((t) => t.name);
|
|
42279
|
+
return new MergedFeatureCollection(featureArrays, trackNames)
|
|
42280
|
+
}
|
|
41780
42281
|
}
|
|
41781
42282
|
|
|
41782
42283
|
draw(options) {
|
|
@@ -41788,11 +42289,6 @@
|
|
|
41788
42289
|
trackOptions.features = mergedFeatures.featureArrays[i];
|
|
41789
42290
|
trackOptions.alpha = this.alpha;
|
|
41790
42291
|
|
|
41791
|
-
if (this.dataRange) {
|
|
41792
|
-
// Single data scale for all tracks
|
|
41793
|
-
this.tracks[i].dataRange = this.dataRange;
|
|
41794
|
-
}
|
|
41795
|
-
|
|
41796
42292
|
if (this.graphType) {
|
|
41797
42293
|
this.tracks[i].graphType = this.graphType;
|
|
41798
42294
|
}
|
|
@@ -41886,6 +42382,7 @@
|
|
|
41886
42382
|
let scaleChange;
|
|
41887
42383
|
|
|
41888
42384
|
if (this.autoscale) {
|
|
42385
|
+
// Overrides any specific track scale settings
|
|
41889
42386
|
scaleChange = true;
|
|
41890
42387
|
let allFeatures = [];
|
|
41891
42388
|
for (let visibleViewport of visibleViewports) {
|
|
@@ -41900,7 +42397,12 @@
|
|
|
41900
42397
|
allFeatures.push({value: mergedFeatureCollection.getMin(start, end)});
|
|
41901
42398
|
}
|
|
41902
42399
|
}
|
|
41903
|
-
|
|
42400
|
+
const dataRange = doAutoscale(allFeatures);
|
|
42401
|
+
for (const track of numericTracks(this.tracks)) {
|
|
42402
|
+
// Do not use this.dataRange, as that has side effects
|
|
42403
|
+
track.dataRange = dataRange;
|
|
42404
|
+
}
|
|
42405
|
+
|
|
41904
42406
|
}
|
|
41905
42407
|
} else {
|
|
41906
42408
|
// Individual track scaling
|
|
@@ -41980,6 +42482,7 @@
|
|
|
41980
42482
|
if (groupAutoscale) {
|
|
41981
42483
|
track.autoscaleGroup = name;
|
|
41982
42484
|
}
|
|
42485
|
+
track.isMergedTrack = false;
|
|
41983
42486
|
browser.addTrack(track.config, track);
|
|
41984
42487
|
}
|
|
41985
42488
|
browser.updateViews();
|
|
@@ -41993,25 +42496,30 @@
|
|
|
41993
42496
|
|
|
41994
42497
|
class MergedFeatureCollection {
|
|
41995
42498
|
|
|
41996
|
-
constructor(featureArrays) {
|
|
42499
|
+
constructor(featureArrays,trackNames) {
|
|
41997
42500
|
this.featureArrays = featureArrays;
|
|
42501
|
+
//trackNames is needed for the popup data to populate track names
|
|
42502
|
+
//preserving the order of the actual tracks
|
|
42503
|
+
this.trackNames = trackNames;
|
|
41998
42504
|
}
|
|
41999
42505
|
|
|
42000
42506
|
getMax(start, end) {
|
|
42001
42507
|
let max = -Number.MAX_VALUE;
|
|
42002
42508
|
|
|
42003
42509
|
for (let a of this.featureArrays) {
|
|
42004
|
-
|
|
42005
|
-
|
|
42006
|
-
|
|
42007
|
-
|
|
42008
|
-
|
|
42009
|
-
|
|
42010
|
-
|
|
42011
|
-
|
|
42012
|
-
|
|
42510
|
+
if (Array.isArray(a)) {
|
|
42511
|
+
for (let f of a) {
|
|
42512
|
+
if (typeof f.value === 'undefined' || Number.isNaN(f.value)) {
|
|
42513
|
+
continue
|
|
42514
|
+
}
|
|
42515
|
+
if (f.end < start) {
|
|
42516
|
+
continue
|
|
42517
|
+
}
|
|
42518
|
+
if (f.start > end) {
|
|
42519
|
+
break
|
|
42520
|
+
}
|
|
42521
|
+
max = Math.max(max, f.value);
|
|
42013
42522
|
}
|
|
42014
|
-
max = Math.max(max, f.value);
|
|
42015
42523
|
}
|
|
42016
42524
|
}
|
|
42017
42525
|
|
|
@@ -42022,15 +42530,17 @@
|
|
|
42022
42530
|
getMin(start, end) {
|
|
42023
42531
|
let min = 0;
|
|
42024
42532
|
for (let a of this.featureArrays) {
|
|
42025
|
-
|
|
42026
|
-
|
|
42027
|
-
if (f.
|
|
42028
|
-
|
|
42029
|
-
|
|
42030
|
-
|
|
42031
|
-
|
|
42533
|
+
if (Array.isArray(a)) {
|
|
42534
|
+
for (let f of a) {
|
|
42535
|
+
if (typeof f.value !== 'undefined' && !Number.isNaN(f.value)) {
|
|
42536
|
+
if (f.end < start) {
|
|
42537
|
+
continue
|
|
42538
|
+
}
|
|
42539
|
+
if (f.start > end) {
|
|
42540
|
+
break
|
|
42541
|
+
}
|
|
42542
|
+
min = Math.min(min, f.value);
|
|
42032
42543
|
}
|
|
42033
|
-
min = Math.min(min, f.value);
|
|
42034
42544
|
}
|
|
42035
42545
|
}
|
|
42036
42546
|
}
|
|
@@ -42038,6 +42548,16 @@
|
|
|
42038
42548
|
}
|
|
42039
42549
|
}
|
|
42040
42550
|
|
|
42551
|
+
/**
|
|
42552
|
+
* Heuristic for finding numeric tracks.
|
|
42553
|
+
*
|
|
42554
|
+
* @param tracks
|
|
42555
|
+
* @returns {*}
|
|
42556
|
+
*/
|
|
42557
|
+
const numericTracks = (tracks) => {
|
|
42558
|
+
return tracks ? tracks.filter(track => undefined !== track.dataRange || undefined !== track.autoscale || undefined !== track.autoscaleGroup) : []
|
|
42559
|
+
};
|
|
42560
|
+
|
|
42041
42561
|
class OverlayTrackButton extends NavbarButton {
|
|
42042
42562
|
constructor(browser, parent) {
|
|
42043
42563
|
|
|
@@ -42083,13 +42603,13 @@
|
|
|
42083
42603
|
{
|
|
42084
42604
|
name: 'Overlay',
|
|
42085
42605
|
type: 'merged',
|
|
42606
|
+
autoscale: false,
|
|
42086
42607
|
alpha: 0.5, //fudge * (1.0/tracks.length),
|
|
42087
42608
|
height: Math.max(...tracks.map(({ height }) => height)),
|
|
42088
42609
|
order: Math.min(...tracks.map(({ order }) => order)),
|
|
42089
|
-
_tracks: flattenedTracks
|
|
42090
42610
|
};
|
|
42091
42611
|
|
|
42092
|
-
const mergedTrack = new MergedTrack(config, this.browser);
|
|
42612
|
+
const mergedTrack = new MergedTrack(config, this.browser, flattenedTracks);
|
|
42093
42613
|
|
|
42094
42614
|
for (const track of tracks) {
|
|
42095
42615
|
this.browser.removeTrack(track);
|
|
@@ -42309,27 +42829,6 @@
|
|
|
42309
42829
|
}
|
|
42310
42830
|
}
|
|
42311
42831
|
|
|
42312
|
-
get dataRange() {
|
|
42313
|
-
return this.track.dataRange ? this.track.dataRange : undefined
|
|
42314
|
-
}
|
|
42315
|
-
|
|
42316
|
-
set dataRange({ min, max }) {
|
|
42317
|
-
|
|
42318
|
-
this.track.dataRange = { min, max };
|
|
42319
|
-
|
|
42320
|
-
this.track.autoscale = false;
|
|
42321
|
-
this.track.autoscaleGroup = undefined;
|
|
42322
|
-
|
|
42323
|
-
const list = this.browser.trackViews.filter(({track}) => track.autoscaleGroup);
|
|
42324
|
-
if (1 === list.length) {
|
|
42325
|
-
list[0].track.autoscale = false;
|
|
42326
|
-
list[0].track.autoscaleGroup = undefined;
|
|
42327
|
-
list[0].repaintViews();
|
|
42328
|
-
}
|
|
42329
|
-
|
|
42330
|
-
this.repaintViews();
|
|
42331
|
-
|
|
42332
|
-
}
|
|
42333
42832
|
|
|
42334
42833
|
presentColorPicker(key) {
|
|
42335
42834
|
|
|
@@ -43145,462 +43644,6 @@
|
|
|
43145
43644
|
|
|
43146
43645
|
}
|
|
43147
43646
|
|
|
43148
|
-
const DEFAULT_COLOR$1 = 'rgb(150, 150, 150)';
|
|
43149
|
-
|
|
43150
|
-
|
|
43151
|
-
class WigTrack extends TrackBase {
|
|
43152
|
-
|
|
43153
|
-
static defaults = {
|
|
43154
|
-
height: 50,
|
|
43155
|
-
flipAxis: false,
|
|
43156
|
-
logScale: false,
|
|
43157
|
-
windowFunction: 'mean',
|
|
43158
|
-
graphType: 'bar',
|
|
43159
|
-
normalize: undefined,
|
|
43160
|
-
scaleFactor: undefined,
|
|
43161
|
-
overflowColor: `rgb(255, 32, 255)`,
|
|
43162
|
-
baselineColor: 'lightGray',
|
|
43163
|
-
summarize: true
|
|
43164
|
-
}
|
|
43165
|
-
|
|
43166
|
-
constructor(config, browser) {
|
|
43167
|
-
super(config, browser);
|
|
43168
|
-
}
|
|
43169
|
-
|
|
43170
|
-
init(config) {
|
|
43171
|
-
|
|
43172
|
-
super.init(config);
|
|
43173
|
-
|
|
43174
|
-
this.type = "wig";
|
|
43175
|
-
this.featureType = 'numeric';
|
|
43176
|
-
this.resolutionAware = true;
|
|
43177
|
-
this.paintAxis = paintAxis;
|
|
43178
|
-
|
|
43179
|
-
const format = config.format ? config.format.toLowerCase() : config.format;
|
|
43180
|
-
if (config.featureSource) {
|
|
43181
|
-
this.featureSource = config.featureSource;
|
|
43182
|
-
delete config.featureSource;
|
|
43183
|
-
} else if ("bigwig" === format) {
|
|
43184
|
-
this.featureSource = new BWSource(config, this.browser.genome);
|
|
43185
|
-
} else if ("tdf" === format) {
|
|
43186
|
-
this.featureSource = new TDFSource(config, this.browser.genome);
|
|
43187
|
-
} else {
|
|
43188
|
-
this.featureSource = FeatureSource(config, this.browser.genome);
|
|
43189
|
-
}
|
|
43190
|
-
|
|
43191
|
-
|
|
43192
|
-
// Override autoscale default
|
|
43193
|
-
if (config.max === undefined || config.autoscale === true) {
|
|
43194
|
-
this.autoscale = true;
|
|
43195
|
-
} else {
|
|
43196
|
-
this.dataRange = {
|
|
43197
|
-
min: config.min || 0,
|
|
43198
|
-
max: config.max
|
|
43199
|
-
};
|
|
43200
|
-
}
|
|
43201
|
-
}
|
|
43202
|
-
|
|
43203
|
-
async postInit() {
|
|
43204
|
-
const header = await this.getHeader();
|
|
43205
|
-
if (this.disposed) return // This track was removed during async load
|
|
43206
|
-
if (header) this.setTrackProperties(header);
|
|
43207
|
-
}
|
|
43208
|
-
|
|
43209
|
-
async getFeatures(chr, start, end, bpPerPixel) {
|
|
43210
|
-
|
|
43211
|
-
const windowFunction = this.windowFunction;
|
|
43212
|
-
|
|
43213
|
-
const features = await this.featureSource.getFeatures({
|
|
43214
|
-
chr,
|
|
43215
|
-
start,
|
|
43216
|
-
end,
|
|
43217
|
-
bpPerPixel,
|
|
43218
|
-
visibilityWindow: this.visibilityWindow,
|
|
43219
|
-
windowFunction
|
|
43220
|
-
});
|
|
43221
|
-
if (this.normalize && this.featureSource.normalizationFactor) {
|
|
43222
|
-
const scaleFactor = this.featureSource.normalizationFactor;
|
|
43223
|
-
for (let f of features) {
|
|
43224
|
-
f.value *= scaleFactor;
|
|
43225
|
-
}
|
|
43226
|
-
}
|
|
43227
|
-
if (this.scaleFactor) {
|
|
43228
|
-
const scaleFactor = this.scaleFactor;
|
|
43229
|
-
for (let f of features) {
|
|
43230
|
-
f.value *= scaleFactor;
|
|
43231
|
-
}
|
|
43232
|
-
}
|
|
43233
|
-
|
|
43234
|
-
// Summarize features to current resolution. This needs to be done here, rather than in the "draw" function,
|
|
43235
|
-
// for group autoscale to work.
|
|
43236
|
-
if (this.summarize && ("mean" === windowFunction || "min" === windowFunction || "max" === windowFunction)) {
|
|
43237
|
-
return summarizeData(features, start, bpPerPixel, windowFunction)
|
|
43238
|
-
} else {
|
|
43239
|
-
return features
|
|
43240
|
-
}
|
|
43241
|
-
}
|
|
43242
|
-
|
|
43243
|
-
menuItemList() {
|
|
43244
|
-
const items = [];
|
|
43245
|
-
|
|
43246
|
-
if (this.flipAxis !== undefined) {
|
|
43247
|
-
items.push('<hr>');
|
|
43248
|
-
|
|
43249
|
-
function click() {
|
|
43250
|
-
this.flipAxis = !this.flipAxis;
|
|
43251
|
-
this.trackView.repaintViews();
|
|
43252
|
-
}
|
|
43253
|
-
|
|
43254
|
-
items.push({label: 'Flip y-axis', click});
|
|
43255
|
-
}
|
|
43256
|
-
|
|
43257
|
-
if(this.featureSource.windowFunctions) {
|
|
43258
|
-
items.push(...this.wigSummarizationItems());
|
|
43259
|
-
}
|
|
43260
|
-
|
|
43261
|
-
items.push(...this.numericDataMenuItems());
|
|
43262
|
-
|
|
43263
|
-
return items
|
|
43264
|
-
}
|
|
43265
|
-
|
|
43266
|
-
wigSummarizationItems() {
|
|
43267
|
-
|
|
43268
|
-
const windowFunctions = this.featureSource.windowFunctions;
|
|
43269
|
-
|
|
43270
|
-
const menuItems = [];
|
|
43271
|
-
menuItems.push('<hr/>');
|
|
43272
|
-
menuItems.push("<div>Windowing function</div>");
|
|
43273
|
-
for (const wf of windowFunctions) {
|
|
43274
|
-
const object = $$1(createCheckbox(wf, this.windowFunction === wf));
|
|
43275
|
-
|
|
43276
|
-
function clickHandler() {
|
|
43277
|
-
this.windowFunction = wf;
|
|
43278
|
-
this.trackView.updateViews();
|
|
43279
|
-
}
|
|
43280
|
-
|
|
43281
|
-
menuItems.push({object, click: clickHandler});
|
|
43282
|
-
}
|
|
43283
|
-
|
|
43284
|
-
return menuItems
|
|
43285
|
-
}
|
|
43286
|
-
|
|
43287
|
-
|
|
43288
|
-
async getHeader() {
|
|
43289
|
-
|
|
43290
|
-
if (typeof this.featureSource.getHeader === "function") {
|
|
43291
|
-
this.header = await this.featureSource.getHeader();
|
|
43292
|
-
}
|
|
43293
|
-
return this.header
|
|
43294
|
-
}
|
|
43295
|
-
|
|
43296
|
-
// TODO: refactor to igvUtils.js
|
|
43297
|
-
getScaleFactor(min, max, height, logScale) {
|
|
43298
|
-
const scale = logScale ? height / (Math.log10(max + 1) - (min <= 0 ? 0 : Math.log10(min + 1))) : height / (max - min);
|
|
43299
|
-
return scale
|
|
43300
|
-
}
|
|
43301
|
-
|
|
43302
|
-
computeYPixelValue(yValue, yScaleFactor) {
|
|
43303
|
-
return (this.flipAxis ? (yValue - this.dataRange.min) : (this.dataRange.max - yValue)) * yScaleFactor
|
|
43304
|
-
}
|
|
43305
|
-
|
|
43306
|
-
computeYPixelValueInLogScale(yValue, yScaleFactor) {
|
|
43307
|
-
let maxValue = this.dataRange.max;
|
|
43308
|
-
let minValue = this.dataRange.min;
|
|
43309
|
-
if (maxValue <= 0) return 0 // TODO:
|
|
43310
|
-
if (minValue <= -1) minValue = 0;
|
|
43311
|
-
minValue = (minValue <= 0) ? 0 : Math.log10(minValue + 1);
|
|
43312
|
-
maxValue = Math.log10(maxValue + 1);
|
|
43313
|
-
yValue = Math.log10(yValue + 1);
|
|
43314
|
-
return ((this.flipAxis ? (yValue - minValue) : (maxValue - yValue)) * yScaleFactor)
|
|
43315
|
-
}
|
|
43316
|
-
|
|
43317
|
-
draw(options) {
|
|
43318
|
-
|
|
43319
|
-
const features = options.features;
|
|
43320
|
-
const ctx = options.context;
|
|
43321
|
-
const bpPerPixel = options.bpPerPixel;
|
|
43322
|
-
const bpStart = options.bpStart;
|
|
43323
|
-
const pixelWidth = options.pixelWidth;
|
|
43324
|
-
const pixelHeight = options.pixelHeight;
|
|
43325
|
-
const bpEnd = bpStart + pixelWidth * bpPerPixel + 1;
|
|
43326
|
-
this.color || DEFAULT_COLOR$1;
|
|
43327
|
-
const scaleFactor = this.getScaleFactor(this.dataRange.min, this.dataRange.max, options.pixelHeight, this.logScale);
|
|
43328
|
-
const yScale = (yValue) => this.logScale
|
|
43329
|
-
? this.computeYPixelValueInLogScale(yValue, scaleFactor)
|
|
43330
|
-
: this.computeYPixelValue(yValue, scaleFactor);
|
|
43331
|
-
|
|
43332
|
-
if (features && features.length > 0) {
|
|
43333
|
-
|
|
43334
|
-
if (this.dataRange.min === undefined) this.dataRange.min = 0;
|
|
43335
|
-
|
|
43336
|
-
// Max can be less than min if config.min is set but max left to autoscale. If that's the case there is
|
|
43337
|
-
// nothing to paint.
|
|
43338
|
-
if (this.dataRange.max > this.dataRange.min) {
|
|
43339
|
-
|
|
43340
|
-
let lastPixelEnd = -1;
|
|
43341
|
-
let lastY;
|
|
43342
|
-
const y0 = yScale(0);
|
|
43343
|
-
|
|
43344
|
-
for (let f of features) {
|
|
43345
|
-
|
|
43346
|
-
if (f.end < bpStart) continue
|
|
43347
|
-
if (f.start > bpEnd) break
|
|
43348
|
-
|
|
43349
|
-
const x = (f.start - bpStart) / bpPerPixel;
|
|
43350
|
-
if (isNaN(x)) continue
|
|
43351
|
-
|
|
43352
|
-
let y = yScale(f.value);
|
|
43353
|
-
|
|
43354
|
-
const rectEnd = (f.end - bpStart) / bpPerPixel;
|
|
43355
|
-
const width = rectEnd - x;
|
|
43356
|
-
|
|
43357
|
-
const color = options.alpha ? IGVColor.addAlpha(this.getColorForFeature(f), options.alpha) : this.getColorForFeature(f);
|
|
43358
|
-
|
|
43359
|
-
if (this.graphType === "line") {
|
|
43360
|
-
if (lastY !== undefined) {
|
|
43361
|
-
IGVGraphics.strokeLine(ctx, lastPixelEnd, lastY, x, y, {
|
|
43362
|
-
"fillStyle": color,
|
|
43363
|
-
"strokeStyle": color
|
|
43364
|
-
});
|
|
43365
|
-
}
|
|
43366
|
-
IGVGraphics.strokeLine(ctx, x, y, x + width, y, {"fillStyle": color, "strokeStyle": color});
|
|
43367
|
-
} else if (this.graphType === "points") {
|
|
43368
|
-
const pointSize = this.config.pointSize || 3;
|
|
43369
|
-
const px = x + width / 2;
|
|
43370
|
-
IGVGraphics.fillCircle(ctx, px, y, pointSize / 2, {"fillStyle": color, "strokeStyle": color});
|
|
43371
|
-
|
|
43372
|
-
if (f.value > this.dataRange.max) {
|
|
43373
|
-
IGVGraphics.fillCircle(ctx, px, pointSize / 2, pointSize / 2, 3, {fillStyle: this.overflowColor});
|
|
43374
|
-
} else if (f.value < this.dataRange.min) {
|
|
43375
|
-
IGVGraphics.fillCircle(ctx, px, pixelHeight - pointSize / 2, pointSize / 2, 3, {fillStyle: this.overflowColor});
|
|
43376
|
-
}
|
|
43377
|
-
|
|
43378
|
-
} else {
|
|
43379
|
-
// Default graph type (bar)
|
|
43380
|
-
const height = Math.min(pixelHeight, y - y0);
|
|
43381
|
-
IGVGraphics.fillRect(ctx, x, y0, width, height, {fillStyle: color});
|
|
43382
|
-
if (f.value > this.dataRange.max) {
|
|
43383
|
-
IGVGraphics.fillRect(ctx, x, 0, width, 3, {fillStyle: this.overflowColor});
|
|
43384
|
-
} else if (f.value < this.dataRange.min) {
|
|
43385
|
-
IGVGraphics.fillRect(ctx, x, pixelHeight - 3, width, 3, {fillStyle: this.overflowColor});
|
|
43386
|
-
}
|
|
43387
|
-
|
|
43388
|
-
}
|
|
43389
|
-
lastPixelEnd = x + width;
|
|
43390
|
-
lastY = y;
|
|
43391
|
-
}
|
|
43392
|
-
|
|
43393
|
-
// If the track includes negative values draw a baseline
|
|
43394
|
-
if (this.dataRange.min < 0) {
|
|
43395
|
-
const ratio = this.dataRange.max / (this.dataRange.max - this.dataRange.min);
|
|
43396
|
-
const basepx = this.flipAxis ? (1 - ratio) * options.pixelHeight : ratio * options.pixelHeight;
|
|
43397
|
-
IGVGraphics.strokeLine(ctx, 0, basepx, options.pixelWidth, basepx, {strokeStyle: this.baselineColor});
|
|
43398
|
-
}
|
|
43399
|
-
}
|
|
43400
|
-
}
|
|
43401
|
-
|
|
43402
|
-
// Draw guidelines
|
|
43403
|
-
if (this.config.hasOwnProperty('guideLines')) {
|
|
43404
|
-
for (let line of this.config.guideLines) {
|
|
43405
|
-
if (line.hasOwnProperty('color') && line.hasOwnProperty('y') && line.hasOwnProperty('dotted')) {
|
|
43406
|
-
let y = yScale(line.y);
|
|
43407
|
-
let props = {
|
|
43408
|
-
'strokeStyle': line['color'],
|
|
43409
|
-
'strokeWidth': 2
|
|
43410
|
-
};
|
|
43411
|
-
if (line['dotted']) IGVGraphics.dashedLine(options.context, 0, y, options.pixelWidth, y, 5, props);
|
|
43412
|
-
else IGVGraphics.strokeLine(options.context, 0, y, options.pixelWidth, y, props);
|
|
43413
|
-
}
|
|
43414
|
-
}
|
|
43415
|
-
}
|
|
43416
|
-
}
|
|
43417
|
-
|
|
43418
|
-
popupData(clickState, features) {
|
|
43419
|
-
|
|
43420
|
-
if (features === undefined) features = this.clickedFeatures(clickState);
|
|
43421
|
-
|
|
43422
|
-
if (features && features.length > 0) {
|
|
43423
|
-
|
|
43424
|
-
const genomicLocation = clickState.genomicLocation;
|
|
43425
|
-
const popupData = [];
|
|
43426
|
-
|
|
43427
|
-
// Sort features based on distance from click
|
|
43428
|
-
features.sort(function (a, b) {
|
|
43429
|
-
const distA = Math.abs((a.start + a.end) / 2 - genomicLocation);
|
|
43430
|
-
const distB = Math.abs((b.start + b.end) / 2 - genomicLocation);
|
|
43431
|
-
return distA - distB
|
|
43432
|
-
});
|
|
43433
|
-
|
|
43434
|
-
// Display closest 10
|
|
43435
|
-
const displayFeatures = features.length > 10 ? features.slice(0, 10) : features;
|
|
43436
|
-
|
|
43437
|
-
// Resort in ascending order
|
|
43438
|
-
displayFeatures.sort(function (a, b) {
|
|
43439
|
-
return a.start - b.start
|
|
43440
|
-
});
|
|
43441
|
-
|
|
43442
|
-
for (let selectedFeature of displayFeatures) {
|
|
43443
|
-
if (selectedFeature) {
|
|
43444
|
-
if (popupData.length > 0) {
|
|
43445
|
-
popupData.push('<hr/>');
|
|
43446
|
-
}
|
|
43447
|
-
let posString = (selectedFeature.end - selectedFeature.start) === 1 ?
|
|
43448
|
-
numberFormatter$1(Math.floor(selectedFeature.start) + 1)
|
|
43449
|
-
: numberFormatter$1(Math.floor(selectedFeature.start) + 1) + "-" + numberFormatter$1(Math.floor(selectedFeature.end));
|
|
43450
|
-
popupData.push({name: "Position:", value: posString});
|
|
43451
|
-
popupData.push({
|
|
43452
|
-
name: "Value: ",
|
|
43453
|
-
value: numberFormatter$1(selectedFeature.value.toFixed(4))
|
|
43454
|
-
});
|
|
43455
|
-
}
|
|
43456
|
-
}
|
|
43457
|
-
if (displayFeatures.length < features.length) {
|
|
43458
|
-
popupData.push("<hr/>...");
|
|
43459
|
-
}
|
|
43460
|
-
|
|
43461
|
-
return popupData
|
|
43462
|
-
|
|
43463
|
-
} else {
|
|
43464
|
-
return []
|
|
43465
|
-
}
|
|
43466
|
-
}
|
|
43467
|
-
|
|
43468
|
-
get supportsWholeGenome() {
|
|
43469
|
-
return !this.config.indexURL && this.config.supportsWholeGenome !== false
|
|
43470
|
-
}
|
|
43471
|
-
|
|
43472
|
-
/**
|
|
43473
|
-
* Return color for feature.
|
|
43474
|
-
* @param feature
|
|
43475
|
-
* @returns {string}
|
|
43476
|
-
*/
|
|
43477
|
-
|
|
43478
|
-
getColorForFeature(f) {
|
|
43479
|
-
let c = (f.value < 0 && this.altColor) ? this.altColor : this.color || DEFAULT_COLOR$1;
|
|
43480
|
-
return (typeof c === "function") ? c(f.value) : c
|
|
43481
|
-
}
|
|
43482
|
-
|
|
43483
|
-
/**
|
|
43484
|
-
* Called when the track is removed. Do any needed cleanup here
|
|
43485
|
-
*/
|
|
43486
|
-
dispose() {
|
|
43487
|
-
this.trackView = undefined;
|
|
43488
|
-
}
|
|
43489
|
-
|
|
43490
|
-
}
|
|
43491
|
-
|
|
43492
|
-
/**
|
|
43493
|
-
* Summarize wig data in bins of size "bpPerPixel" with the given window function.
|
|
43494
|
-
*
|
|
43495
|
-
* @param features wig (numeric) data -- features cannot overlap, and are in ascending order by start position
|
|
43496
|
-
* @param startBP bp start position for computing binned data
|
|
43497
|
-
* @param bpPerPixel bp per pixel (bin)
|
|
43498
|
-
* @param windowFunction mean, min, or max
|
|
43499
|
-
* @returns {*|*[]}
|
|
43500
|
-
*/
|
|
43501
|
-
function summarizeData(features, startBP, bpPerPixel, windowFunction = "mean") {
|
|
43502
|
-
|
|
43503
|
-
if (bpPerPixel <= 1 || !features || features.length === 0) {
|
|
43504
|
-
return features
|
|
43505
|
-
}
|
|
43506
|
-
|
|
43507
|
-
// Assume features are sorted by position. Wig features cannot overlap. Note, UCSC "reductionLevel" == bpPerPixel
|
|
43508
|
-
const chr = features[0].chr;
|
|
43509
|
-
const binSize = bpPerPixel;
|
|
43510
|
-
const summaryFeatures = [];
|
|
43511
|
-
|
|
43512
|
-
const finishBin = (bin) => {
|
|
43513
|
-
const start = startBP + bin.bin * binSize;
|
|
43514
|
-
const end = start + binSize;
|
|
43515
|
-
let value;
|
|
43516
|
-
switch (windowFunction) {
|
|
43517
|
-
case "mean":
|
|
43518
|
-
value = bin.sumData / bin.count;
|
|
43519
|
-
break
|
|
43520
|
-
case "max":
|
|
43521
|
-
value = bin.max;
|
|
43522
|
-
break
|
|
43523
|
-
case "min":
|
|
43524
|
-
value = bin.min;
|
|
43525
|
-
break
|
|
43526
|
-
default:
|
|
43527
|
-
throw Error(`Unknown window function: ${windowFunction}`)
|
|
43528
|
-
}
|
|
43529
|
-
const description = `${windowFunction} of ${bin.count} values`;
|
|
43530
|
-
summaryFeatures.push({chr, start, end, value, description});
|
|
43531
|
-
};
|
|
43532
|
-
|
|
43533
|
-
let currentBinData;
|
|
43534
|
-
for (let f of features) {
|
|
43535
|
-
|
|
43536
|
-
// Loop through bins this feature overlaps, updating the weighted sum for each bin or min/max,
|
|
43537
|
-
// depending on window function
|
|
43538
|
-
let startBin = Math.floor((f.start - startBP) / binSize);
|
|
43539
|
-
const endBin = Math.floor((f.end - startBP) / binSize);
|
|
43540
|
-
|
|
43541
|
-
if (currentBinData && startBin === currentBinData.bin) {
|
|
43542
|
-
currentBinData.add(f);
|
|
43543
|
-
startBin++;
|
|
43544
|
-
}
|
|
43545
|
-
|
|
43546
|
-
if (!currentBinData || endBin > currentBinData.bin) {
|
|
43547
|
-
|
|
43548
|
-
if(currentBinData) {
|
|
43549
|
-
finishBin(currentBinData);
|
|
43550
|
-
}
|
|
43551
|
-
|
|
43552
|
-
// Feature stretches across multiple bins.
|
|
43553
|
-
if (endBin > startBin) {
|
|
43554
|
-
const end = startBP + endBin * binSize;
|
|
43555
|
-
summaryFeatures.push({chr, start: f.start, end, value: f.value});
|
|
43556
|
-
}
|
|
43557
|
-
|
|
43558
|
-
currentBinData = new SummaryBinData(endBin, f);
|
|
43559
|
-
}
|
|
43560
|
-
|
|
43561
|
-
}
|
|
43562
|
-
if(currentBinData) {
|
|
43563
|
-
finishBin(currentBinData);
|
|
43564
|
-
}
|
|
43565
|
-
|
|
43566
|
-
// Consolidate
|
|
43567
|
-
const c = [];
|
|
43568
|
-
let lastFeature = summaryFeatures[0];
|
|
43569
|
-
for (let f of summaryFeatures) {
|
|
43570
|
-
if (lastFeature.value === f.value && f.start <= lastFeature.end) {
|
|
43571
|
-
lastFeature.end = f.end;
|
|
43572
|
-
} else {
|
|
43573
|
-
c.push(lastFeature);
|
|
43574
|
-
lastFeature = f;
|
|
43575
|
-
}
|
|
43576
|
-
}
|
|
43577
|
-
c.push(lastFeature);
|
|
43578
|
-
|
|
43579
|
-
return c
|
|
43580
|
-
|
|
43581
|
-
}
|
|
43582
|
-
|
|
43583
|
-
class SummaryBinData {
|
|
43584
|
-
constructor(bin, feature) {
|
|
43585
|
-
this.bin = bin;
|
|
43586
|
-
this.sumData = feature.value;
|
|
43587
|
-
this.count = 1;
|
|
43588
|
-
this.min = feature.value;
|
|
43589
|
-
this.max = feature.value;
|
|
43590
|
-
}
|
|
43591
|
-
|
|
43592
|
-
add(feature) {
|
|
43593
|
-
this.sumData += feature.value;
|
|
43594
|
-
this.max = Math.max(feature.value, this.max);
|
|
43595
|
-
this.min = Math.min(feature.value, this.min);
|
|
43596
|
-
this.count++;
|
|
43597
|
-
}
|
|
43598
|
-
|
|
43599
|
-
get mean() {
|
|
43600
|
-
return this.sumData / this.count
|
|
43601
|
-
}
|
|
43602
|
-
}
|
|
43603
|
-
|
|
43604
43647
|
/**
|
|
43605
43648
|
*
|
|
43606
43649
|
* @param cs - object containing
|
|
@@ -48515,7 +48558,7 @@
|
|
|
48515
48558
|
allAlignments() {
|
|
48516
48559
|
if (this.alignments) {
|
|
48517
48560
|
return this.alignments
|
|
48518
|
-
} else {
|
|
48561
|
+
} else if (this.packedGroups) {
|
|
48519
48562
|
const all = Array.from(this.packedGroups.values()).flatMap(group => group.rows.flatMap(row => row.alignments));
|
|
48520
48563
|
if (this.#unpacked && this.#unpacked.length > 0) {
|
|
48521
48564
|
for (let a of this.#unpacked) {
|
|
@@ -48523,6 +48566,8 @@
|
|
|
48523
48566
|
}
|
|
48524
48567
|
}
|
|
48525
48568
|
return all
|
|
48569
|
+
} else {
|
|
48570
|
+
return []
|
|
48526
48571
|
}
|
|
48527
48572
|
}
|
|
48528
48573
|
|
|
@@ -48531,9 +48576,10 @@
|
|
|
48531
48576
|
}
|
|
48532
48577
|
|
|
48533
48578
|
sortRows(options) {
|
|
48534
|
-
|
|
48535
|
-
|
|
48536
|
-
|
|
48579
|
+
if(this.packedGroups) {
|
|
48580
|
+
for (let group of this.packedGroups.values()) {
|
|
48581
|
+
group.sortRows(options, this);
|
|
48582
|
+
}
|
|
48537
48583
|
}
|
|
48538
48584
|
}
|
|
48539
48585
|
}
|
|
@@ -49778,46 +49824,41 @@
|
|
|
49778
49824
|
},
|
|
49779
49825
|
|
|
49780
49826
|
/**
|
|
49827
|
+
* @param ba - UInt8Array bytes to decode
|
|
49781
49828
|
*
|
|
49782
|
-
* @
|
|
49783
|
-
* @param genome optional igv genome object
|
|
49784
|
-
* @returns {{ magicNumer: number, size: number, chrNames: Array, chrToIndex: ({}|*), chrAliasTable: ({}|*) }}
|
|
49829
|
+
* @returns {{size: *, chrNames: *[], magicNumber: *, chrToIndex: {}}}
|
|
49785
49830
|
*/
|
|
49786
|
-
decodeBamHeader: function (ba
|
|
49831
|
+
decodeBamHeader: function (ba) {
|
|
49787
49832
|
|
|
49788
|
-
var magic, samHeaderLen, samHeader, chrToIndex, chrNames;
|
|
49789
49833
|
|
|
49790
|
-
magic = readInt(ba, 0);
|
|
49834
|
+
const magic = readInt(ba, 0);
|
|
49791
49835
|
if (magic !== BAM1_MAGIC_NUMBER) {
|
|
49792
49836
|
throw new Error('BAM header errror: bad magic number. This could be caused by either a corrupt or missing file.')
|
|
49793
49837
|
}
|
|
49794
49838
|
|
|
49795
|
-
samHeaderLen = readInt(ba, 4);
|
|
49796
|
-
samHeader = '';
|
|
49797
|
-
|
|
49839
|
+
const samHeaderLen = readInt(ba, 4);
|
|
49840
|
+
let samHeader = '';
|
|
49798
49841
|
for (var i = 0; i < samHeaderLen; ++i) {
|
|
49799
49842
|
samHeader += String.fromCharCode(ba[i + 8]);
|
|
49800
49843
|
}
|
|
49801
49844
|
|
|
49802
|
-
|
|
49803
|
-
|
|
49845
|
+
const nRef = readInt(ba, samHeaderLen + 8);
|
|
49846
|
+
let p = samHeaderLen + 12;
|
|
49804
49847
|
|
|
49805
|
-
chrToIndex = {};
|
|
49806
|
-
chrNames = [];
|
|
49848
|
+
const chrToIndex = {};
|
|
49849
|
+
const chrNames = [];
|
|
49807
49850
|
|
|
49808
49851
|
for (i = 0; i < nRef; ++i) {
|
|
49809
|
-
|
|
49810
|
-
|
|
49811
|
-
for (var j = 0; j <
|
|
49852
|
+
const len = readInt(ba, p);
|
|
49853
|
+
let name = '';
|
|
49854
|
+
for (var j = 0; j < len - 1; ++j) {
|
|
49812
49855
|
name += String.fromCharCode(ba[p + 4 + j]);
|
|
49813
49856
|
}
|
|
49814
|
-
readInt(ba, p + lName + 4);
|
|
49815
|
-
//dlog(name + ': ' + lRef);
|
|
49816
49857
|
|
|
49817
49858
|
chrToIndex[name] = i;
|
|
49818
49859
|
chrNames[i] = name;
|
|
49819
49860
|
|
|
49820
|
-
p = p + 8 +
|
|
49861
|
+
p = p + 8 + len;
|
|
49821
49862
|
}
|
|
49822
49863
|
|
|
49823
49864
|
return {
|
|
@@ -50807,8 +50848,8 @@
|
|
|
50807
50848
|
const ba = unbgzf(compressedData.buffer);
|
|
50808
50849
|
this.header = BamUtils.decodeBamHeader(ba, this.genome);
|
|
50809
50850
|
this.chrAliasTable = new Map();
|
|
50810
|
-
for (let
|
|
50811
|
-
this.chrAliasTable.set(
|
|
50851
|
+
for (let name of this.header.chrNames) {
|
|
50852
|
+
this.chrAliasTable.set(name, this.genome.getChromosomeName(name));
|
|
50812
50853
|
}
|
|
50813
50854
|
}
|
|
50814
50855
|
|
|
@@ -50818,6 +50859,7 @@
|
|
|
50818
50859
|
|
|
50819
50860
|
// BAM decoding
|
|
50820
50861
|
const ba = unbgzf(compressedData.buffer);
|
|
50862
|
+
this.header = BamUtils.decodeBamHeader(ba, this.genome);
|
|
50821
50863
|
|
|
50822
50864
|
const chrIdx = this.header.chrToIndex[chr];
|
|
50823
50865
|
const alignmentContainer = new AlignmentContainer(chr, start, end, this.config);
|
|
@@ -57327,23 +57369,24 @@
|
|
|
57327
57369
|
const y = clickState.y;
|
|
57328
57370
|
const offsetY = y - this.top;
|
|
57329
57371
|
const genomicLocation = clickState.genomicLocation;
|
|
57330
|
-
|
|
57331
|
-
|
|
57332
|
-
|
|
57333
|
-
|
|
57334
|
-
|
|
57335
|
-
|
|
57336
|
-
|
|
57337
|
-
|
|
57338
|
-
|
|
57339
|
-
|
|
57340
|
-
|
|
57341
|
-
|
|
57342
|
-
|
|
57343
|
-
|
|
57344
|
-
|
|
57345
|
-
|
|
57346
|
-
|
|
57372
|
+
|
|
57373
|
+
if(features.packedGroups) {
|
|
57374
|
+
let minGroupY = Number.MAX_VALUE;
|
|
57375
|
+
for (let group of features.packedGroups.values()) {
|
|
57376
|
+
minGroupY = Math.min(minGroupY, group.pixelTop);
|
|
57377
|
+
if (offsetY > group.pixelTop && offsetY <= group.pixelBottom) {
|
|
57378
|
+
|
|
57379
|
+
const alignmentRowHeight = this.displayMode === "SQUISHED" ?
|
|
57380
|
+
this.squishedRowHeight :
|
|
57381
|
+
this.alignmentRowHeight;
|
|
57382
|
+
|
|
57383
|
+
let packedAlignmentsIndex = Math.floor((offsetY - group.pixelTop) / alignmentRowHeight);
|
|
57384
|
+
|
|
57385
|
+
if (packedAlignmentsIndex >= 0 && packedAlignmentsIndex < group.length) {
|
|
57386
|
+
const alignmentRow = group.rows[packedAlignmentsIndex];
|
|
57387
|
+
const clicked = alignmentRow.alignments.filter(alignment => alignment.containsLocation(genomicLocation, this.showSoftClips));
|
|
57388
|
+
if (clicked.length > 0) return clicked[0]
|
|
57389
|
+
}
|
|
57347
57390
|
}
|
|
57348
57391
|
}
|
|
57349
57392
|
}
|
|
@@ -57662,6 +57705,7 @@
|
|
|
57662
57705
|
|
|
57663
57706
|
|
|
57664
57707
|
constructor(config, parent) {
|
|
57708
|
+
this.featureType = 'numeric';
|
|
57665
57709
|
this.parent = parent;
|
|
57666
57710
|
this.featureSource = parent.featureSource;
|
|
57667
57711
|
|
|
@@ -66140,12 +66184,12 @@
|
|
|
66140
66184
|
if (this.signals.includes(signal_name)) {
|
|
66141
66185
|
let tconf = {};
|
|
66142
66186
|
tconf.type = "wig";
|
|
66143
|
-
tconf.isMergedTrack = true;
|
|
66144
66187
|
tconf.features = wig;
|
|
66145
66188
|
tconf.name = signal_name;
|
|
66146
66189
|
tconf.color = this.signal_colors.filter(x => x.singal_name === signal_name).map(x => x.color);
|
|
66147
66190
|
const t = await this.browser.createTrack(tconf);
|
|
66148
66191
|
if (t) {
|
|
66192
|
+
t.isMergedTrack = true;
|
|
66149
66193
|
t.autoscale = false; // Scaling done from merged track
|
|
66150
66194
|
this.tracks.push(t);
|
|
66151
66195
|
} else {
|
|
@@ -66324,12 +66368,12 @@
|
|
|
66324
66368
|
if (this.signals.includes(signal_name)) {
|
|
66325
66369
|
let tconf = {};
|
|
66326
66370
|
tconf.type = "wig";
|
|
66327
|
-
tconf.isMergedTrack = true;
|
|
66328
66371
|
tconf.features = wig;
|
|
66329
66372
|
tconf.name = signal_name;
|
|
66330
66373
|
tconf.color = this.signal_colors.filter(x => x.singal_name === signal_name).map(x => x.color);
|
|
66331
66374
|
const t = await this.browser.createTrack(tconf);
|
|
66332
66375
|
if (t) {
|
|
66376
|
+
t.isMergedTrack = true;
|
|
66333
66377
|
t.autoscale = false; // Scaling done from merged track
|
|
66334
66378
|
this.tracks.push(t);
|
|
66335
66379
|
} else {
|
|
@@ -66722,23 +66766,26 @@
|
|
|
66722
66766
|
this.header = await this.getHeader();
|
|
66723
66767
|
|
|
66724
66768
|
// Set colorBy, if not explicitly set default to allele frequency, if available, otherwise default to none (undefined)
|
|
66725
|
-
|
|
66726
|
-
|
|
66727
|
-
|
|
66728
|
-
|
|
66729
|
-
this.
|
|
66730
|
-
|
|
66769
|
+
if(this.header.INFO) {
|
|
66770
|
+
const infoFields = new Set(Object.keys(this.header.INFO));
|
|
66771
|
+
if (this.config.colorBy) {
|
|
66772
|
+
this.colorBy = this.config.colorBy;
|
|
66773
|
+
} else if (!this.config.color && infoFields.has('AF')) {
|
|
66774
|
+
this.colorBy = 'AF';
|
|
66775
|
+
}
|
|
66731
66776
|
|
|
66732
|
-
|
|
66733
|
-
|
|
66734
|
-
|
|
66735
|
-
|
|
66736
|
-
|
|
66737
|
-
|
|
66738
|
-
|
|
66739
|
-
|
|
66740
|
-
|
|
66777
|
+
// Configure menu items based on info available
|
|
66778
|
+
if (infoFields.has('AF')) {
|
|
66779
|
+
this._colorByItems.set('AF', 'Allele frequency');
|
|
66780
|
+
}
|
|
66781
|
+
if (infoFields.has('VT')) {
|
|
66782
|
+
this._colorByItems.set('VT', 'Variant Type');
|
|
66783
|
+
}
|
|
66784
|
+
if (infoFields.has('SVTYPE')) {
|
|
66785
|
+
this._colorByItems.set('SVTYPE', 'SV Type');
|
|
66786
|
+
}
|
|
66741
66787
|
}
|
|
66788
|
+
|
|
66742
66789
|
if (this.config.colorBy && !this._colorByItems.has(this.config.colorBy)) {
|
|
66743
66790
|
this._colorByItems.set(this.config.colorBy, this.config.colorBy);
|
|
66744
66791
|
}
|
|
@@ -69583,11 +69630,15 @@
|
|
|
69583
69630
|
JUNCTION_MOTIF_PALETTE.getColor(motif);
|
|
69584
69631
|
});
|
|
69585
69632
|
|
|
69586
|
-
// rendering context with values that only need to be computed once per render, rather than for each splice junction
|
|
69587
|
-
const junctionRenderingContext = {};
|
|
69588
69633
|
|
|
69589
69634
|
class SpliceJunctionTrack extends TrackBase {
|
|
69590
69635
|
|
|
69636
|
+
static defaults = {
|
|
69637
|
+
margin: 10,
|
|
69638
|
+
colorByNumReadsThreshold: 5,
|
|
69639
|
+
height: 100
|
|
69640
|
+
}
|
|
69641
|
+
|
|
69591
69642
|
constructor(config, browser) {
|
|
69592
69643
|
super(config, browser);
|
|
69593
69644
|
}
|
|
@@ -69608,16 +69659,6 @@
|
|
|
69608
69659
|
FeatureSource(config, this.browser.genome);
|
|
69609
69660
|
}
|
|
69610
69661
|
|
|
69611
|
-
this.margin = config.margin === undefined ? 10 : config.margin;
|
|
69612
|
-
|
|
69613
|
-
if (!this.height) {
|
|
69614
|
-
this.height = 100;
|
|
69615
|
-
}
|
|
69616
|
-
|
|
69617
|
-
//set defaults
|
|
69618
|
-
if (config.colorByNumReadsThreshold === undefined) {
|
|
69619
|
-
config.colorByNumReadsThreshold = 5;
|
|
69620
|
-
}
|
|
69621
69662
|
}
|
|
69622
69663
|
|
|
69623
69664
|
async postInit() {
|
|
@@ -69672,13 +69713,16 @@
|
|
|
69672
69713
|
const bpEnd = bpStart + pixelWidth * bpPerPixel + 1;
|
|
69673
69714
|
|
|
69674
69715
|
|
|
69675
|
-
if (!this.
|
|
69716
|
+
if (!this.isMergedTrack) {
|
|
69676
69717
|
IGVGraphics.fillRect(ctx, 0, options.pixelTop, pixelWidth, pixelHeight, {'fillStyle': "rgb(255, 255, 255)"});
|
|
69677
69718
|
}
|
|
69678
69719
|
|
|
69679
69720
|
if (featureList) {
|
|
69680
69721
|
|
|
69681
69722
|
|
|
69723
|
+
// rendering context with values that only need to be computed once per render, rather than for each splice junction
|
|
69724
|
+
const junctionRenderingContext = {};
|
|
69725
|
+
|
|
69682
69726
|
junctionRenderingContext.referenceFrame = options.viewport.referenceFrame;
|
|
69683
69727
|
junctionRenderingContext.referenceFrameStart = junctionRenderingContext.referenceFrame.start;
|
|
69684
69728
|
junctionRenderingContext.referenceFrameEnd = junctionRenderingContext.referenceFrameStart +
|
|
@@ -69691,7 +69735,7 @@
|
|
|
69691
69735
|
for (let feature of featureList) {
|
|
69692
69736
|
if (feature.end < bpStart) continue
|
|
69693
69737
|
if (feature.start > bpEnd) break
|
|
69694
|
-
this.renderJunction(feature, bpStart, bpPerPixel, pixelHeight, ctx);
|
|
69738
|
+
this.renderJunction(feature, bpStart, bpPerPixel, pixelHeight, ctx, junctionRenderingContext);
|
|
69695
69739
|
}
|
|
69696
69740
|
|
|
69697
69741
|
} else {
|
|
@@ -69708,7 +69752,7 @@
|
|
|
69708
69752
|
* @param pixelHeight pixel height of the current canvas
|
|
69709
69753
|
* @param ctx the canvas 2d context
|
|
69710
69754
|
*/
|
|
69711
|
-
renderJunction(feature, bpStart, xScale, pixelHeight, ctx) {
|
|
69755
|
+
renderJunction(feature, bpStart, xScale, pixelHeight, ctx, junctionRenderingContext) {
|
|
69712
69756
|
// cache whether this junction is rendered or filtered out. Use later to exclude non-rendered junctions from click detection.
|
|
69713
69757
|
feature.isVisible = false;
|
|
69714
69758
|
|
|
@@ -69792,7 +69836,7 @@
|
|
|
69792
69836
|
}
|
|
69793
69837
|
|
|
69794
69838
|
const py = this.margin;
|
|
69795
|
-
const rowHeight =
|
|
69839
|
+
const rowHeight = pixelHeight;
|
|
69796
69840
|
|
|
69797
69841
|
const cy = py + 0.5 * rowHeight;
|
|
69798
69842
|
let topY = py;
|
|
@@ -70477,7 +70521,7 @@
|
|
|
70477
70521
|
})
|
|
70478
70522
|
}
|
|
70479
70523
|
|
|
70480
|
-
const _version = "3.0.
|
|
70524
|
+
const _version = "3.0.3";
|
|
70481
70525
|
function version() {
|
|
70482
70526
|
return _version
|
|
70483
70527
|
}
|
|
@@ -71948,16 +71992,17 @@
|
|
|
71948
71992
|
|
|
71949
71993
|
}
|
|
71950
71994
|
|
|
71951
|
-
async present(feature,
|
|
71952
|
-
const menuItems = this.menuItems(feature,
|
|
71995
|
+
async present(feature, roiSet, event, roiManager, columnContainer, regionElement) {
|
|
71996
|
+
const menuItems = this.menuItems(feature, roiSet, event, roiManager, columnContainer, regionElement);
|
|
71953
71997
|
this.browser.menuPopup.presentTrackContextMenu(event, menuItems);
|
|
71954
71998
|
}
|
|
71955
71999
|
|
|
71956
|
-
menuItems(feature,
|
|
72000
|
+
menuItems(feature, roiSet, event, roiManager, columnContainer, regionElement) {
|
|
72001
|
+
const items = feature.name ? [`<b>${feature.name}</b><br/>`] : [];
|
|
72002
|
+
if ('name' in roiSet) items.push(`<b>ROI Set: ${roiSet.name}</b>`);
|
|
72003
|
+
if (items.length > 0) items.push(`<hr/>`);
|
|
71957
72004
|
|
|
71958
|
-
|
|
71959
|
-
|
|
71960
|
-
if (isUserDefined) {
|
|
72005
|
+
if (roiSet.isUserDefined) {
|
|
71961
72006
|
items.push(
|
|
71962
72007
|
{
|
|
71963
72008
|
label: 'Set description ...',
|
|
@@ -72021,7 +72066,7 @@
|
|
|
72021
72066
|
}
|
|
72022
72067
|
|
|
72023
72068
|
|
|
72024
|
-
if (isUserDefined) {
|
|
72069
|
+
if (roiSet.isUserDefined) {
|
|
72025
72070
|
items.push(
|
|
72026
72071
|
'<hr/>',
|
|
72027
72072
|
{
|
|
@@ -72306,13 +72351,17 @@
|
|
|
72306
72351
|
const [rectA, rectB] = tracks
|
|
72307
72352
|
.map(track => track.trackView.viewports[0].$viewport.get(0))
|
|
72308
72353
|
.map(element => getElementVerticalDimension(element));
|
|
72309
|
-
|
|
72354
|
+
|
|
72355
|
+
//Covers cases in which ruler and/or ideogram are hidden
|
|
72356
|
+
const heightA = rectA ? rectA.height : 0;
|
|
72357
|
+
const heightB = rectB ? rectB.height : 0;
|
|
72358
|
+
|
|
72310
72359
|
const elements = browser.columnContainer.querySelectorAll('.igv-roi-region');
|
|
72311
72360
|
|
|
72312
72361
|
const fudge = -0.5;
|
|
72313
72362
|
if (elements) {
|
|
72314
72363
|
for (const element of elements) {
|
|
72315
|
-
element.style.marginTop = `${
|
|
72364
|
+
element.style.marginTop = `${heightA + heightB + fudge}px`;
|
|
72316
72365
|
}
|
|
72317
72366
|
|
|
72318
72367
|
}
|
|
@@ -72480,7 +72529,6 @@
|
|
|
72480
72529
|
if (features) {
|
|
72481
72530
|
|
|
72482
72531
|
for (let feature of features) {
|
|
72483
|
-
|
|
72484
72532
|
const regionKey = createRegionKey(chr, feature.start, feature.end);
|
|
72485
72533
|
|
|
72486
72534
|
const {
|
|
@@ -72525,8 +72573,7 @@
|
|
|
72525
72573
|
event.stopPropagation();
|
|
72526
72574
|
|
|
72527
72575
|
translateMouseCoordinates(event, columnContainer);
|
|
72528
|
-
|
|
72529
|
-
this.roiMenu.present(feature, isUserDefined, event, this, columnContainer, regionElement);
|
|
72576
|
+
this.roiMenu.present(feature, roiSet, event, this, columnContainer, regionElement);
|
|
72530
72577
|
});
|
|
72531
72578
|
|
|
72532
72579
|
|
|
@@ -72928,7 +72975,7 @@
|
|
|
72928
72975
|
}
|
|
72929
72976
|
|
|
72930
72977
|
/**
|
|
72931
|
-
* Return the canonical chromosome name for the alias. If none found return the alias.
|
|
72978
|
+
* Return the cached canonical chromosome name for the alias. If none found return the alias.
|
|
72932
72979
|
*
|
|
72933
72980
|
* Note this will only work if a "search" for ths chromosome has been performed previously.
|
|
72934
72981
|
*
|
|
@@ -73695,7 +73742,7 @@
|
|
|
73695
73742
|
}
|
|
73696
73743
|
}
|
|
73697
73744
|
|
|
73698
|
-
var igvCss = '.igv-ui-dropdown {\n cursor: default;\n position: absolute;\n top: 0;\n left: 0;\n z-index: 2048;\n border-color: #7F7F7F;\n border-style: solid;\n border-width: 1px;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n font-weight: 400;\n background-color: white;\n}\n.igv-ui-dropdown > div {\n overflow-y: auto;\n overflow-x: hidden;\n background-color: white;\n}\n.igv-ui-dropdown > div > div {\n padding: 4px;\n width: 100%;\n overflow-x: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: 1px;\n background-color: white;\n}\n.igv-ui-dropdown > div > div:last-child {\n border-bottom-color: transparent;\n border-bottom-width: 0;\n}\n.igv-ui-dropdown > div > div:hover {\n cursor: pointer;\n background-color: rgba(0, 0, 0, 0.04);\n}\n\n.igv-ui-popover {\n cursor: default;\n position: absolute;\n z-index: 2048;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: 1px;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n background-color: white;\n}\n.igv-ui-popover > div:first-child {\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-width: 0;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n}\n.igv-ui-popover > div:first-child > div:first-child {\n margin-left: 4px;\n}\n.igv-ui-popover > div:first-child > div:last-child {\n margin-right: 4px;\n height: 12px;\n width: 12px;\n color: #7F7F7F;\n}\n.igv-ui-popover > div:first-child > div:last-child:hover {\n cursor: pointer;\n color: #444;\n}\n.igv-ui-popover > div:last-child {\n user-select: text;\n overflow-y: auto;\n overflow-x: hidden;\n max-height: 400px;\n max-width: 800px;\n background-color: white;\n border-bottom-width: 0;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n}\n.igv-ui-popover > div:last-child > div {\n margin-left: 4px;\n margin-right: 4px;\n min-width: 220px;\n overflow-x: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.igv-ui-popover > div:last-child > div > span {\n font-weight: bolder;\n}\n.igv-ui-popover > div:last-child hr {\n width: 100%;\n}\n\n.igv-ui-alert-dialog-container {\n box-sizing: content-box;\n position: absolute;\n z-index: 2048;\n top: 50%;\n left: 50%;\n width: 400px;\n height: 200px;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n outline: none;\n font-family: \"Open Sans\", sans-serif;\n font-size: 15px;\n font-weight: 400;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n}\n.igv-ui-alert-dialog-container > div:first-child {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n}\n.igv-ui-alert-dialog-container > div:first-child div:first-child {\n padding-left: 8px;\n}\n.igv-ui-alert-dialog-container .igv-ui-alert-dialog-body {\n -webkit-user-select: text;\n -moz-user-select: text;\n -ms-user-select: text;\n user-select: text;\n color: #373737;\n width: 100%;\n height: calc(100% - 24px - 64px);\n overflow-y: scroll;\n}\n.igv-ui-alert-dialog-container .igv-ui-alert-dialog-body .igv-ui-alert-dialog-body-copy {\n margin: 16px;\n width: auto;\n height: auto;\n overflow-wrap: break-word;\n word-break: break-word;\n background-color: white;\n border: unset;\n}\n.igv-ui-alert-dialog-container > div:last-child {\n width: 100%;\n margin-bottom: 10px;\n background-color: white;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: center;\n align-items: center;\n}\n.igv-ui-alert-dialog-container > div:last-child div {\n margin: unset;\n width: 40px;\n height: 30px;\n line-height: 30px;\n text-align: center;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n font-weight: 400;\n border-color: #2B81AF;\n border-style: solid;\n border-width: thin;\n border-radius: 4px;\n background-color: #2B81AF;\n}\n.igv-ui-alert-dialog-container > div:last-child div:hover {\n cursor: pointer;\n border-color: #25597f;\n background-color: #25597f;\n}\n\n.igv-ui-color-swatch {\n position: relative;\n box-sizing: content-box;\n display: flex;\n flex-flow: row;\n flex-wrap: wrap;\n justify-content: center;\n align-items: center;\n width: 32px;\n height: 32px;\n border-style: solid;\n border-width: 2px;\n border-color: white;\n border-radius: 4px;\n}\n\n.igv-ui-color-swatch:hover {\n border-color: dimgray;\n}\n\n.igv-ui-colorpicker-menu-close-button {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 32px;\n margin-top: 4px;\n margin-bottom: 4px;\n padding-right: 8px;\n}\n.igv-ui-colorpicker-menu-close-button i.fa {\n display: block;\n margin-left: 4px;\n margin-right: 4px;\n color: #5f5f5f;\n}\n.igv-ui-colorpicker-menu-close-button i.fa:hover,\n.igv-ui-colorpicker-menu-close-button i.fa:focus,\n.igv-ui-colorpicker-menu-close-button i.fa:active {\n cursor: pointer;\n color: #0f0f0f;\n}\n\n.igv-ui-generic-dialog-container {\n box-sizing: content-box;\n position: fixed;\n top: 0;\n left: 0;\n width: 300px;\n height: fit-content;\n padding-bottom: 16px;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n z-index: 2048;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-header {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-header div {\n margin-right: 4px;\n margin-bottom: 2px;\n height: 12px;\n width: 12px;\n color: #7F7F7F;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-header div:hover {\n cursor: pointer;\n color: #444;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-one-liner {\n color: #373737;\n width: 95%;\n height: 24px;\n line-height: 24px;\n text-align: left;\n margin-top: 8px;\n padding-left: 8px;\n overflow-wrap: break-word;\n background-color: white;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input {\n margin-top: 8px;\n width: 95%;\n height: 24px;\n color: #373737;\n line-height: 24px;\n padding-left: 8px;\n background-color: white;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input > div {\n width: fit-content;\n height: 100%;\n font-size: 16px;\n text-align: right;\n padding-right: 8px;\n background-color: white;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input input {\n display: block;\n height: 100%;\n width: 100%;\n padding-left: 4px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n color: #373737;\n text-align: left;\n outline: none;\n border-style: solid;\n border-width: thin;\n border-color: #7F7F7F;\n background-color: white;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input input {\n width: 50%;\n font-size: 16px;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-input {\n margin-top: 8px;\n width: calc(100% - 16px);\n height: 24px;\n color: #373737;\n line-height: 24px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-input input {\n display: block;\n height: 100%;\n width: 100%;\n padding-left: 4px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n color: #373737;\n text-align: left;\n outline: none;\n border-style: solid;\n border-width: thin;\n border-color: #7F7F7F;\n background-color: white;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-input input {\n font-size: 16px;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-input input[type=range] {\n width: 70%;\n -webkit-appearance: none;\n background: linear-gradient(90deg, white, black);\n outline: none;\n margin: 0;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-input output {\n display: block;\n height: 100%;\n width: 20%;\n font-size: 16px;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel {\n width: 100%;\n height: 28px;\n padding-top: 16px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel > div {\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n width: 75px;\n height: 28px;\n line-height: 28px;\n text-align: center;\n border-color: transparent;\n border-style: solid;\n border-width: thin;\n border-radius: 2px;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel > div:first-child {\n margin-left: 32px;\n margin-right: 0;\n background-color: #5ea4e0;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel > div:last-child {\n margin-left: 0;\n margin-right: 32px;\n background-color: #c4c4c4;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel > div:first-child:hover {\n cursor: pointer;\n background-color: #3b5c7f;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel > div:last-child:hover {\n cursor: pointer;\n background-color: #7f7f7f;\n}\n\n.igv-ui-generic-container {\n box-sizing: content-box;\n position: absolute;\n z-index: 2048;\n background-color: white;\n cursor: pointer;\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-ui-generic-container > div:first-child {\n cursor: move;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n height: 24px;\n width: 100%;\n background-color: #dddddd;\n}\n.igv-ui-generic-container > div:first-child > div {\n display: block;\n color: #5f5f5f;\n cursor: pointer;\n width: 14px;\n height: 14px;\n margin-right: 8px;\n margin-bottom: 4px;\n}\n\n.igv-ui-dialog {\n z-index: 2048;\n position: fixed;\n width: fit-content;\n height: fit-content;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n background-color: white;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n}\n.igv-ui-dialog .igv-ui-dialog-header {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n}\n.igv-ui-dialog .igv-ui-dialog-header div {\n margin-right: 4px;\n margin-bottom: 2px;\n height: 12px;\n width: 12px;\n color: #7F7F7F;\n}\n.igv-ui-dialog .igv-ui-dialog-header div:hover {\n cursor: pointer;\n color: #444;\n}\n.igv-ui-dialog .igv-ui-dialog-one-liner {\n width: 95%;\n height: 24px;\n line-height: 24px;\n text-align: left;\n margin: 8px;\n overflow-wrap: break-word;\n background-color: white;\n font-weight: bold;\n}\n.igv-ui-dialog .igv-ui-dialog-ok-cancel {\n width: 100%;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n}\n.igv-ui-dialog .igv-ui-dialog-ok-cancel div {\n margin: 16px;\n margin-top: 32px;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n width: 75px;\n height: 28px;\n line-height: 28px;\n text-align: center;\n border-color: transparent;\n border-style: solid;\n border-width: thin;\n border-radius: 2px;\n}\n.igv-ui-dialog .igv-ui-dialog-ok-cancel div:first-child {\n background-color: #5ea4e0;\n}\n.igv-ui-dialog .igv-ui-dialog-ok-cancel div:last-child {\n background-color: #c4c4c4;\n}\n.igv-ui-dialog .igv-ui-dialog-ok-cancel div:first-child:hover {\n cursor: pointer;\n background-color: #3b5c7f;\n}\n.igv-ui-dialog .igv-ui-dialog-ok-cancel div:last-child:hover {\n cursor: pointer;\n background-color: #7f7f7f;\n}\n.igv-ui-dialog .igv-ui-dialog-ok {\n width: 100%;\n height: 36px;\n margin-top: 32px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n}\n.igv-ui-dialog .igv-ui-dialog-ok div {\n width: 98px;\n height: 36px;\n line-height: 36px;\n text-align: center;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n border-color: white;\n border-style: solid;\n border-width: thin;\n border-radius: 4px;\n background-color: #2B81AF;\n}\n.igv-ui-dialog .igv-ui-dialog-ok div:hover {\n cursor: pointer;\n background-color: #25597f;\n}\n\n.igv-ui-panel, .igv-ui-panel-row, .igv-ui-panel-column {\n z-index: 2048;\n background-color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n display: flex;\n justify-content: flex-start;\n align-items: flex-start;\n}\n\n.igv-ui-panel-column {\n display: flex;\n flex-direction: column;\n}\n\n.igv-ui-panel-row {\n display: flex;\n flex-direction: row;\n}\n\n.igv-ui-textbox {\n background-color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n display: flex;\n justify-content: flex-start;\n align-items: flex-start;\n}\n\n.igv-ui-table {\n background-color: white;\n}\n\n.igv-ui-table thead {\n position: sticky;\n top: 0;\n}\n\n.igv-ui-table th {\n text-align: left;\n}\n\n.igv-ui-table td {\n padding-right: 20px;\n}\n\n.igv-ui-table tr:hover {\n background-color: lightblue;\n}\n\n.igv-ui-center-fixed {\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n}\n\n.igv-navbar {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n box-sizing: border-box;\n width: 100%;\n color: #444;\n font-size: 12px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n line-height: 32px;\n padding-left: 8px;\n padding-right: 8px;\n margin-top: 2px;\n margin-bottom: 6px;\n height: 32px;\n border-style: solid;\n border-radius: 3px;\n border-width: thin;\n border-color: #bfbfbf;\n background-color: #f3f3f3;\n}\n.igv-navbar .igv-navbar-left-container {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n height: 32px;\n line-height: 32px;\n}\n.igv-navbar .igv-navbar-left-container .igv-logo {\n width: 34px;\n height: 32px;\n margin-right: 8px;\n}\n.igv-navbar .igv-navbar-left-container .igv-current-genome {\n height: 32px;\n margin-left: 4px;\n margin-right: 4px;\n user-select: none;\n line-height: 32px;\n vertical-align: middle;\n text-align: center;\n}\n.igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n height: 100%;\n}\n.igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-chromosome-select-widget-container {\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n height: 100%;\n width: 125px;\n margin-right: 4px;\n}\n.igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-chromosome-select-widget-container select {\n display: block;\n cursor: pointer;\n width: 100px;\n height: 75%;\n outline: none;\n font-size: 12px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n}\n.igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-locus-size-group {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n margin-left: 8px;\n height: 22px;\n}\n.igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-locus-size-group .igv-search-container {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n width: 240px;\n height: 22px;\n line-height: 22px;\n}\n.igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-locus-size-group .igv-search-container input.igv-search-input {\n cursor: text;\n width: 85%;\n height: 22px;\n line-height: 22px;\n font-size: 12px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n text-align: left;\n padding-left: 8px;\n margin-right: 8px;\n outline: none;\n border-style: solid;\n border-radius: 3px;\n border-width: thin;\n border-color: #bfbfbf;\n background-color: white;\n}\n.igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-locus-size-group .igv-search-container .igv-search-icon-container {\n cursor: pointer;\n height: 16px;\n width: 16px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: center;\n align-items: center;\n}\n.igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-locus-size-group .igv-windowsize-panel-container {\n margin-left: 4px;\n user-select: none;\n}\n.igv-navbar .igv-navbar-right-container {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n}\n.igv-navbar .igv-navbar-right-container .igv-navbar-toggle-button-container {\n position: relative;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n}\n.igv-navbar .igv-navbar-right-container .igv-navbar-toggle-button-container-hidden {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n height: 100%;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget {\n color: #737373;\n font-size: 18px;\n margin-left: 8px;\n user-select: none;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget div {\n cursor: pointer;\n margin-left: unset;\n margin-right: unset;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget div:first-child {\n height: 20px;\n width: 20px;\n margin-left: unset;\n margin-right: 4px;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget div:last-child {\n height: 20px;\n width: 20px;\n margin-left: 4px;\n margin-right: unset;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget div:nth-child(even) {\n display: block;\n height: fit-content;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget input {\n display: block;\n width: 125px;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget svg {\n display: block;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 {\n color: #737373;\n font-size: 18px;\n height: 32px;\n line-height: 32px;\n margin-left: 8px;\n user-select: none;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 div {\n cursor: pointer;\n margin-left: unset;\n margin-right: unset;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 div:first-child {\n height: 20px;\n width: 20px;\n margin-left: unset;\n margin-right: 4px;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 div:last-child {\n height: 20px;\n width: 20px;\n margin-left: 4px;\n margin-right: unset;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 div:nth-child(even) {\n width: 0;\n height: 0;\n display: none;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 input {\n width: 0;\n height: 0;\n display: none;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 svg {\n display: block;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-hidden {\n display: none;\n}\n\n.igv-navbar-button {\n display: block;\n box-sizing: unset;\n padding-left: 6px;\n padding-right: 6px;\n height: 18px;\n text-transform: capitalize;\n user-select: none;\n line-height: 18px;\n text-align: center;\n vertical-align: middle;\n font-family: \"Open Sans\", sans-serif;\n font-size: 11px;\n font-weight: 200;\n color: #737373;\n background-color: #f3f3f3;\n border-color: #737373;\n border-style: solid;\n border-width: thin;\n border-radius: 6px;\n}\n\n.igv-navbar-button:hover {\n cursor: pointer;\n}\n\n.igv-navbar-button-clicked {\n color: white;\n background-color: #737373;\n}\n\n.igv-navbar-icon-button {\n cursor: pointer;\n position: relative;\n width: 24px;\n height: 24px;\n margin-left: 4px;\n margin-right: 4px;\n border: none;\n background-size: contain;\n background-repeat: no-repeat;\n background-position: center;\n}\n.igv-navbar-icon-button > div:first-child {\n z-index: 512;\n position: absolute;\n top: 36px;\n left: -18px;\n width: 24px;\n height: 24px;\n border: none;\n background-size: contain;\n background-repeat: no-repeat;\n background-position: center;\n}\n.igv-navbar-icon-button > div:last-child {\n z-index: 512;\n position: absolute;\n top: 36px;\n left: 18px;\n width: 24px;\n height: 24px;\n border: none;\n background-size: contain;\n background-repeat: no-repeat;\n background-position: center;\n}\n\n.igv-navbar-text-button {\n cursor: pointer;\n position: relative;\n margin-left: 2px;\n margin-right: 2px;\n border: none;\n display: flex;\n justify-content: center;\n align-items: center;\n}\n.igv-navbar-text-button > div:nth-child(2) {\n z-index: 512;\n position: absolute;\n top: 36px;\n left: 0;\n width: 38px;\n height: 18px;\n border: none;\n background-size: contain;\n background-repeat: no-repeat;\n background-position: center;\n}\n.igv-navbar-text-button > div:nth-child(3) {\n z-index: 512;\n position: absolute;\n top: 36px;\n left: 42px;\n width: 38px;\n height: 18px;\n border: none;\n background-size: contain;\n background-repeat: no-repeat;\n background-position: center;\n}\n\n#igv-text-button-label {\n text-anchor: middle;\n dominant-baseline: middle;\n}\n\n.igv-navbar-text-button-svg-inactive rect {\n stroke: #737373;\n fill: white;\n}\n.igv-navbar-text-button-svg-inactive text {\n fill: #737373;\n}\n.igv-navbar-text-button-svg-inactive tspan {\n dominant-baseline: middle;\n}\n\n.igv-navbar-text-button-svg-hover rect {\n stroke: #737373;\n fill: #737373;\n}\n.igv-navbar-text-button-svg-hover text {\n fill: white;\n}\n.igv-navbar-text-button-svg-hover tspan {\n dominant-baseline: middle;\n}\n\n#igv-save-svg-group rect {\n stroke: #737373;\n fill: white;\n}\n#igv-save-svg-group text {\n fill: #737373;\n}\n\n#igv-save-svg-group:hover rect {\n stroke: #737373;\n fill: #737373;\n}\n#igv-save-svg-group:hover text {\n fill: white;\n}\n\n#igv-save-png-group rect {\n stroke: #737373;\n fill: white;\n}\n#igv-save-png-group text {\n fill: #737373;\n}\n\n#igv-save-png-group:hover rect {\n stroke: #737373;\n fill: #737373;\n}\n#igv-save-png-group:hover text {\n fill: white;\n}\n\n.igv-zoom-in-notice-container {\n z-index: 256;\n position: absolute;\n top: 8px;\n left: 50%;\n transform: translate(-50%, 0%);\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n justify-content: center;\n align-items: center;\n background-color: white;\n}\n.igv-zoom-in-notice-container > div {\n padding-left: 4px;\n padding-right: 4px;\n padding-top: 2px;\n padding-bottom: 2px;\n width: 100%;\n height: 100%;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n color: #3f3f3f;\n}\n\n.igv-zoom-in-notice {\n position: absolute;\n top: 10px;\n left: 50%;\n}\n.igv-zoom-in-notice div {\n position: relative;\n left: -50%;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n color: #3f3f3f;\n background-color: rgba(255, 255, 255, 0.51);\n z-index: 64;\n}\n\n.igv-container-spinner {\n position: absolute;\n top: 90%;\n left: 50%;\n transform: translate(-50%, -50%);\n z-index: 1024;\n width: 24px;\n height: 24px;\n pointer-events: none;\n color: #737373;\n}\n\n.igv-multi-locus-close-button {\n position: absolute;\n top: 2px;\n right: 0;\n padding-left: 2px;\n padding-right: 2px;\n width: 12px;\n height: 12px;\n color: #666666;\n background-color: white;\n z-index: 1000;\n}\n.igv-multi-locus-close-button > svg {\n vertical-align: top;\n}\n\n.igv-multi-locus-close-button:hover {\n cursor: pointer;\n color: #434343;\n}\n\n.igv-multi-locus-ruler-label {\n z-index: 64;\n position: absolute;\n top: 2px;\n left: 0;\n width: 100%;\n height: 12px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: center;\n align-items: center;\n}\n.igv-multi-locus-ruler-label > div {\n font-family: \"Open Sans\", sans-serif;\n font-size: 12px;\n color: rgb(16, 16, 16);\n background-color: white;\n}\n.igv-multi-locus-ruler-label > div {\n cursor: pointer;\n}\n\n.igv-multi-locus-ruler-label-square-dot {\n z-index: 64;\n position: absolute;\n left: 50%;\n top: 5%;\n transform: translate(-50%, 0%);\n background-color: white;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-multi-locus-ruler-label-square-dot > div:first-child {\n width: 14px;\n height: 14px;\n}\n.igv-multi-locus-ruler-label-square-dot > div:last-child {\n margin-left: 16px;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n color: rgb(16, 16, 16);\n}\n\n.igv-ruler-sweeper {\n display: none;\n pointer-events: none;\n position: absolute;\n top: 26px;\n bottom: 0;\n left: 0;\n width: 0;\n z-index: 99999;\n background-color: rgba(68, 134, 247, 0.25);\n}\n\n.igv-ruler-tooltip {\n pointer-events: none;\n z-index: 128;\n position: absolute;\n top: 0;\n left: 0;\n width: 1px;\n height: 32px;\n background-color: transparent;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-ruler-tooltip > div {\n pointer-events: none;\n width: 128px;\n height: auto;\n padding: 1px;\n color: #373737;\n font-size: 10px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n background-color: white;\n border-style: solid;\n border-width: thin;\n border-color: #373737;\n}\n\n.igv-track-label {\n position: absolute;\n left: 8px;\n top: 8px;\n width: auto;\n height: auto;\n max-width: 50%;\n padding-left: 4px;\n padding-right: 4px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n font-weight: 400;\n text-align: center;\n user-select: none;\n -moz-user-select: none;\n -webkit-user-select: none;\n border-color: #444;\n border-radius: 2px;\n border-style: solid;\n border-width: thin;\n background-color: white;\n z-index: 128;\n cursor: pointer;\n}\n\n.igv-track-label:hover,\n.igv-track-label:focus,\n.igv-track-label:active {\n background-color: #e8e8e8;\n}\n\n.igv-track-label-popup-shim {\n padding-left: 8px;\n padding-right: 8px;\n padding-top: 4px;\n}\n\n.igv-center-line {\n display: none;\n pointer-events: none;\n position: absolute;\n top: 0;\n bottom: 0;\n left: 50%;\n transform: translateX(-50%);\n z-index: 8;\n user-select: none;\n -moz-user-select: none;\n -webkit-user-select: none;\n border-left-style: dashed;\n border-left-width: thin;\n border-right-style: dashed;\n border-right-width: thin;\n}\n\n.igv-center-line-wide {\n background-color: rgba(0, 0, 0, 0);\n border-left-color: rgba(127, 127, 127, 0.51);\n border-right-color: rgba(127, 127, 127, 0.51);\n}\n\n.igv-center-line-thin {\n background-color: rgba(0, 0, 0, 0);\n border-left-color: rgba(127, 127, 127, 0.51);\n border-right-color: rgba(0, 0, 0, 0);\n}\n\n.igv-cursor-guide-horizontal {\n display: none;\n pointer-events: none;\n user-select: none;\n -moz-user-select: none;\n -webkit-user-select: none;\n position: absolute;\n left: 0;\n right: 0;\n top: 50%;\n height: 1px;\n z-index: 32;\n margin-left: 50px;\n margin-right: 54px;\n border-top-style: dotted;\n border-top-width: thin;\n border-top-color: rgba(127, 127, 127, 0.76);\n}\n\n.igv-cursor-guide-vertical {\n pointer-events: none;\n user-select: none;\n -moz-user-select: none;\n -webkit-user-select: none;\n position: absolute;\n top: 0;\n bottom: 0;\n left: 50%;\n width: 1px;\n z-index: 32;\n border-left-style: dotted;\n border-left-width: thin;\n border-left-color: rgba(127, 127, 127, 0.76);\n display: none;\n}\n\n.igv-user-feedback {\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 512px;\n height: 360px;\n z-index: 2048;\n background-color: white;\n border-color: #a2a2a2;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n color: #444;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-user-feedback div:first-child {\n position: relative;\n height: 24px;\n width: 100%;\n background-color: white;\n border-bottom-color: #a2a2a2;\n border-bottom-style: solid;\n border-bottom-width: thin;\n}\n.igv-user-feedback div:first-child div {\n position: absolute;\n top: 2px;\n width: 16px;\n height: 16px;\n background-color: transparent;\n}\n.igv-user-feedback div:first-child div:first-child {\n left: 8px;\n}\n.igv-user-feedback div:first-child div:last-child {\n cursor: pointer;\n right: 8px;\n}\n.igv-user-feedback div:last-child {\n width: 100%;\n height: calc(100% - 24px);\n border-width: 0;\n}\n.igv-user-feedback div:last-child div {\n width: auto;\n height: auto;\n margin: 8px;\n}\n\n.igv-generic-dialog-container {\n position: absolute;\n top: 0;\n left: 0;\n width: 300px;\n height: 200px;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n z-index: 2048;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-generic-dialog-container .igv-generic-dialog-header {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n}\n.igv-generic-dialog-container .igv-generic-dialog-header div {\n margin-right: 4px;\n margin-bottom: 2px;\n height: 12px;\n width: 12px;\n color: #7F7F7F;\n}\n.igv-generic-dialog-container .igv-generic-dialog-header div:hover {\n cursor: pointer;\n color: #444;\n}\n.igv-generic-dialog-container .igv-generic-dialog-one-liner {\n color: #373737;\n width: 95%;\n height: 24px;\n line-height: 24px;\n text-align: left;\n margin-top: 8px;\n padding-left: 8px;\n overflow-wrap: break-word;\n background-color: white;\n}\n.igv-generic-dialog-container .igv-generic-dialog-label-input {\n margin-top: 8px;\n width: 95%;\n height: 24px;\n color: #373737;\n line-height: 24px;\n padding-left: 8px;\n background-color: white;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-generic-dialog-container .igv-generic-dialog-label-input div {\n width: 30%;\n height: 100%;\n font-size: 16px;\n text-align: right;\n padding-right: 8px;\n background-color: white;\n}\n.igv-generic-dialog-container .igv-generic-dialog-label-input input {\n display: block;\n height: 100%;\n width: 100%;\n padding-left: 4px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n color: #373737;\n text-align: left;\n outline: none;\n border-style: solid;\n border-width: thin;\n border-color: #7F7F7F;\n background-color: white;\n}\n.igv-generic-dialog-container .igv-generic-dialog-label-input input {\n width: 50%;\n font-size: 16px;\n}\n.igv-generic-dialog-container .igv-generic-dialog-input {\n margin-top: 8px;\n width: calc(100% - 16px);\n height: 24px;\n color: #373737;\n line-height: 24px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n}\n.igv-generic-dialog-container .igv-generic-dialog-input input {\n display: block;\n height: 100%;\n width: 100%;\n padding-left: 4px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n color: #373737;\n text-align: left;\n outline: none;\n border-style: solid;\n border-width: thin;\n border-color: #7F7F7F;\n background-color: white;\n}\n.igv-generic-dialog-container .igv-generic-dialog-input input {\n font-size: 16px;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok-cancel {\n width: 100%;\n height: 28px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok-cancel div {\n margin-top: 32px;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n width: 75px;\n height: 28px;\n line-height: 28px;\n text-align: center;\n border-color: transparent;\n border-style: solid;\n border-width: thin;\n border-radius: 2px;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok-cancel div:first-child {\n margin-left: 32px;\n margin-right: 0;\n background-color: #5ea4e0;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok-cancel div:last-child {\n margin-left: 0;\n margin-right: 32px;\n background-color: #c4c4c4;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok-cancel div:first-child:hover {\n cursor: pointer;\n background-color: #3b5c7f;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok-cancel div:last-child:hover {\n cursor: pointer;\n background-color: #7f7f7f;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok {\n width: 100%;\n height: 36px;\n margin-top: 32px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok div {\n width: 98px;\n height: 36px;\n line-height: 36px;\n text-align: center;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n border-color: white;\n border-style: solid;\n border-width: thin;\n border-radius: 4px;\n background-color: #2B81AF;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok div:hover {\n cursor: pointer;\n background-color: #25597f;\n}\n\n.igv-generic-container {\n position: absolute;\n top: 0;\n left: 0;\n z-index: 2048;\n background-color: white;\n cursor: pointer;\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-generic-container div:first-child {\n cursor: move;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n height: 24px;\n width: 100%;\n background-color: #dddddd;\n}\n.igv-generic-container div:first-child i {\n display: block;\n color: #5f5f5f;\n cursor: pointer;\n width: 14px;\n height: 14px;\n margin-right: 8px;\n margin-bottom: 4px;\n}\n\n.igv-menu-popup {\n position: absolute;\n top: 0;\n left: 0;\n width: max-content;\n z-index: 512;\n cursor: pointer;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n font-weight: 400;\n color: #4b4b4b;\n background: white;\n border-radius: 4px;\n border-color: #7F7F7F;\n border-style: solid;\n border-width: thin;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-end;\n text-align: left;\n}\n.igv-menu-popup > div:not(:first-child) {\n width: 100%;\n}\n.igv-menu-popup > div:not(:first-child) > div {\n background: white;\n}\n.igv-menu-popup > div:not(:first-child) > div.context-menu {\n padding-left: 4px;\n padding-right: 4px;\n}\n.igv-menu-popup > div:not(:first-child) > div:last-child {\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n border-bottom-color: transparent;\n border-bottom-style: solid;\n border-bottom-width: thin;\n}\n.igv-menu-popup > div:not(:first-child) > div:hover {\n background: #efefef;\n}\n\n.igv-menu-popup-shim {\n padding-left: 8px;\n padding-right: 8px;\n padding-bottom: 1px;\n padding-top: 1px;\n}\n\n.igv-menu-popup-header {\n position: relative;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-color: transparent;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n}\n.igv-menu-popup-header div {\n margin-right: 4px;\n height: 12px;\n width: 12px;\n color: #7F7F7F;\n}\n.igv-menu-popup-header div:hover {\n cursor: pointer;\n color: #444;\n}\n\n.igv-menu-popup-check-container {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n width: 100%;\n height: 20px;\n margin-right: 4px;\n background-color: transparent;\n}\n.igv-menu-popup-check-container div {\n padding-top: 2px;\n padding-left: 8px;\n}\n.igv-menu-popup-check-container div:first-child {\n position: relative;\n width: 12px;\n height: 12px;\n}\n.igv-menu-popup-check-container div:first-child svg {\n position: absolute;\n width: 12px;\n height: 12px;\n}\n\n.igv-user-feedback {\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 512px;\n height: 360px;\n z-index: 2048;\n background-color: white;\n border-color: #a2a2a2;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n color: #444;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-user-feedback div:first-child {\n position: relative;\n height: 24px;\n width: 100%;\n background-color: white;\n border-bottom-color: #a2a2a2;\n border-bottom-style: solid;\n border-bottom-width: thin;\n}\n.igv-user-feedback div:first-child div {\n position: absolute;\n top: 2px;\n width: 16px;\n height: 16px;\n background-color: transparent;\n}\n.igv-user-feedback div:first-child div:first-child {\n left: 8px;\n}\n.igv-user-feedback div:first-child div:last-child {\n cursor: pointer;\n right: 8px;\n}\n.igv-user-feedback div:last-child {\n width: 100%;\n height: calc(100% - 24px);\n border-width: 0;\n}\n.igv-user-feedback div:last-child div {\n width: auto;\n height: auto;\n margin: 8px;\n}\n\n.igv-loading-spinner-container {\n z-index: 1024;\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 32px;\n height: 32px;\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n justify-content: center;\n align-items: center;\n}\n.igv-loading-spinner-container > div {\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n border-radius: 50%;\n border: 4px solid rgba(128, 128, 128, 0.5);\n border-top-color: rgb(255, 255, 255);\n animation: spin 1s ease-in-out infinite;\n -webkit-animation: spin 1s ease-in-out infinite;\n}\n\n@keyframes spin {\n to {\n -webkit-transform: rotate(360deg);\n transform: rotate(360deg);\n }\n}\n@-webkit-keyframes spin {\n to {\n -webkit-transform: rotate(360deg);\n transform: rotate(360deg);\n }\n}\n.igv-roi-menu {\n position: absolute;\n z-index: 512;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n font-weight: 400;\n color: #4b4b4b;\n background-color: white;\n width: 192px;\n border-radius: 4px;\n border-color: #7F7F7F;\n border-style: solid;\n border-width: thin;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: stretch;\n}\n.igv-roi-menu > div:first-child {\n height: 24px;\n border-top-color: transparent;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n}\n.igv-roi-menu > div:first-child > div {\n margin-right: 4px;\n height: 12px;\n width: 12px;\n color: #7F7F7F;\n}\n.igv-roi-menu > div:first-child > div:hover {\n cursor: pointer;\n color: #444;\n}\n.igv-roi-menu > div:last-child {\n background-color: white;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n border-bottom-color: transparent;\n border-bottom-style: solid;\n border-bottom-width: 0;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: stretch;\n text-align: start;\n vertical-align: middle;\n}\n.igv-roi-menu > div:last-child > div {\n height: 24px;\n padding-left: 4px;\n border-bottom-style: solid;\n border-bottom-width: thin;\n border-bottom-color: #7f7f7f;\n}\n.igv-roi-menu > div:last-child > div:not(:first-child):hover {\n cursor: pointer;\n background-color: rgba(127, 127, 127, 0.1);\n}\n.igv-roi-menu > div:last-child div:first-child {\n font-style: italic;\n text-align: center;\n padding-right: 4px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.igv-roi-menu > div:last-child > div:last-child {\n border-bottom-width: 0;\n border-bottom-color: transparent;\n}\n\n.igv-roi-placeholder {\n font-style: normal;\n color: rgba(75, 75, 75, 0.6);\n}\n\n.igv-roi-table {\n position: absolute;\n z-index: 1024;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: stretch;\n resize: both;\n overflow: hidden;\n width: min-content;\n max-width: 1600px;\n border-color: #7f7f7f;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: 12px;\n font-weight: 400;\n background-color: white;\n cursor: default;\n}\n.igv-roi-table > div {\n height: 24px;\n font-size: 14px;\n text-align: start;\n vertical-align: middle;\n line-height: 24px;\n}\n.igv-roi-table > div:first-child {\n border-color: transparent;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-top-width: 0;\n border-bottom-color: #7f7f7f;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n cursor: move;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n}\n.igv-roi-table > div:first-child > div:first-child {\n text-align: center;\n white-space: nowrap;\n text-overflow: ellipsis;\n overflow: hidden;\n margin-left: 4px;\n margin-right: 4px;\n width: calc(100% - 4px - 12px);\n}\n.igv-roi-table > div:first-child > div:last-child {\n margin-right: 4px;\n margin-bottom: 2px;\n height: 12px;\n width: 12px;\n color: #7f7f7f;\n}\n.igv-roi-table > div:first-child > div:last-child > svg {\n display: block;\n}\n.igv-roi-table > div:first-child > div:last-child:hover {\n cursor: pointer;\n color: #444;\n}\n.igv-roi-table > .igv-roi-table-description {\n padding: 4px;\n margin-left: 4px;\n word-break: break-all;\n overflow-y: auto;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n background-color: transparent;\n}\n.igv-roi-table > .igv-roi-table-goto-explainer {\n margin-top: 5px;\n margin-left: 4px;\n color: #7F7F7F;\n font-style: italic;\n height: 24px;\n border-top: solid lightgray;\n background-color: transparent;\n}\n.igv-roi-table > .igv-roi-table-column-titles {\n height: 24px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: stretch;\n align-items: stretch;\n padding-right: 16px;\n background-color: white;\n border-top-color: #7f7f7f;\n border-top-style: solid;\n border-top-width: thin;\n border-bottom-color: #7f7f7f;\n border-bottom-style: solid;\n border-bottom-width: thin;\n}\n.igv-roi-table > .igv-roi-table-column-titles > div {\n font-size: 14px;\n vertical-align: middle;\n line-height: 24px;\n text-align: left;\n margin-left: 4px;\n height: 24px;\n overflow: hidden;\n text-overflow: ellipsis;\n border-right-color: #7f7f7f;\n border-right-style: solid;\n border-right-width: thin;\n}\n.igv-roi-table > .igv-roi-table-column-titles > div:last-child {\n border-right: unset;\n}\n.igv-roi-table > .igv-roi-table-row-container {\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: stretch;\n overflow: auto;\n height: 360px;\n flex: 1 1 auto;\n background-color: transparent;\n}\n.igv-roi-table > .igv-roi-table-row-container > .igv-roi-table-row {\n height: 24px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: stretch;\n align-items: stretch;\n}\n.igv-roi-table > .igv-roi-table-row-container > .igv-roi-table-row > div {\n font-size: 14px;\n vertical-align: middle;\n line-height: 24px;\n text-align: left;\n margin-left: 4px;\n height: 24px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n border-right-color: transparent;\n border-right-style: solid;\n border-right-width: thin;\n}\n.igv-roi-table > .igv-roi-table-row-container > .igv-roi-table-row > div:last-child {\n border-right: unset;\n}\n.igv-roi-table > .igv-roi-table-row-container > .igv-roi-table-row-hover {\n background-color: rgba(0, 0, 0, 0.04);\n}\n.igv-roi-table > div:last-child {\n min-height: 32px;\n height: 32px;\n line-height: 32px;\n border-top-color: #7f7f7f;\n border-top-style: solid;\n border-top-width: thin;\n border-bottom-color: transparent;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n border-bottom-width: 0;\n background-color: #eee;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n}\n\n.igv-roi-table-row-selected {\n background-color: rgba(0, 0, 0, 0.125);\n}\n\n.igv-roi-table-button {\n cursor: pointer;\n height: 20px;\n user-select: none;\n line-height: 20px;\n text-align: center;\n vertical-align: middle;\n font-family: \"Open Sans\", sans-serif;\n font-size: 13px;\n font-weight: 400;\n color: black;\n padding-left: 6px;\n padding-right: 6px;\n background-color: rgb(239, 239, 239);\n border-color: black;\n border-style: solid;\n border-width: thin;\n border-radius: 3px;\n}\n\n.igv-roi-table-button:hover {\n font-weight: 400;\n background-color: rgba(0, 0, 0, 0.13);\n}\n\n.igv-roi-region {\n z-index: 64;\n position: absolute;\n top: 0;\n bottom: 0;\n pointer-events: none;\n overflow: visible;\n margin-top: 66px;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: stretch;\n}\n.igv-roi-region > div {\n position: relative;\n width: 100%;\n height: 8px;\n pointer-events: auto;\n}\n\n.igv-roi-menu-row {\n height: 24px;\n padding-left: 8px;\n font-size: small;\n text-align: start;\n vertical-align: middle;\n line-height: 24px;\n background-color: white;\n}\n\n.igv-roi-menu-row-edit-description {\n width: -webkit-fill-available;\n font-size: small;\n text-align: start;\n vertical-align: middle;\n background-color: white;\n padding-left: 4px;\n padding-right: 4px;\n padding-bottom: 4px;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: stretch;\n align-items: stretch;\n}\n.igv-roi-menu-row-edit-description > label {\n margin-left: 2px;\n margin-bottom: 0;\n display: block;\n width: -webkit-fill-available;\n}\n.igv-roi-menu-row-edit-description > input {\n display: block;\n margin-left: 2px;\n margin-right: 2px;\n margin-bottom: 1px;\n width: -webkit-fill-available;\n}\n\n.igv-container {\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n padding-top: 4px;\n user-select: none;\n -webkit-user-select: none;\n -ms-user-select: none;\n min-height: 160px;\n}\n\n.igv-viewport {\n position: relative;\n margin-top: 5px;\n line-height: 1;\n overflow-x: hidden;\n overflow-y: hidden;\n}\n\n.igv-viewport-content {\n position: relative;\n width: 100%;\n}\n.igv-viewport-content > canvas {\n position: relative;\n display: block;\n}\n\n.igv-column-container {\n position: relative;\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: stretch;\n width: 100%;\n}\n\n.igv-column-shim {\n width: 1px;\n margin-left: 2px;\n margin-right: 2px;\n background-color: #545453;\n}\n\n.igv-axis-column {\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n width: 50px;\n}\n.igv-axis-column > div {\n position: relative;\n margin-top: 5px;\n width: 100%;\n}\n.igv-axis-column > div > div {\n z-index: 512;\n position: absolute;\n top: 8px;\n left: 8px;\n width: fit-content;\n height: fit-content;\n background-color: transparent;\n display: grid;\n align-items: start;\n justify-items: center;\n}\n.igv-axis-column > div > div > input {\n display: block;\n margin: unset;\n cursor: pointer;\n}\n\n.igv-column {\n position: relative;\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n}\n\n.igv-sample-info-column {\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n}\n\n.igv-sample-name-column {\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n}\n\n.igv-scrollbar-column {\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n width: 14px;\n}\n.igv-scrollbar-column > div {\n position: relative;\n margin-top: 5px;\n width: 14px;\n}\n.igv-scrollbar-column > div > div {\n cursor: pointer;\n position: absolute;\n top: 0;\n left: 2px;\n width: 8px;\n border-width: 1px;\n border-style: solid;\n border-color: #c4c4c4;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n}\n.igv-scrollbar-column > div > div:hover {\n background-color: #c4c4c4;\n}\n\n.igv-track-drag-column {\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n width: 12px;\n background-color: white;\n}\n.igv-track-drag-column > .igv-track-drag-handle {\n z-index: 512;\n position: relative;\n cursor: pointer;\n margin-top: 5px;\n width: 100%;\n border-style: solid;\n border-width: 0;\n border-top-right-radius: 6px;\n border-bottom-right-radius: 6px;\n}\n.igv-track-drag-column .igv-track-drag-handle-color {\n background-color: #c4c4c4;\n}\n.igv-track-drag-column .igv-track-drag-handle-hover-color {\n background-color: #787878;\n}\n.igv-track-drag-column .igv-track-drag-handle-selected-color {\n background-color: #0963fa;\n}\n.igv-track-drag-column > .igv-track-drag-shim {\n position: relative;\n margin-top: 5px;\n width: 100%;\n border-style: solid;\n border-width: 0;\n}\n\n.igv-gear-menu-column {\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n width: 28px;\n}\n.igv-gear-menu-column > div {\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n margin-top: 5px;\n width: 100%;\n background: white;\n}\n.igv-gear-menu-column > div > div {\n position: relative;\n margin-top: 4px;\n width: 16px;\n height: 16px;\n color: #7F7F7F;\n}\n.igv-gear-menu-column > div > div:hover {\n cursor: pointer;\n color: #444;\n}\n\n/*# sourceMappingURL=igv.css.map */\n';
|
|
73745
|
+
var igvCss = '.igv-ui-dropdown {\n cursor: default;\n position: absolute;\n top: 0;\n left: 0;\n z-index: 2048;\n border-color: #7F7F7F;\n border-style: solid;\n border-width: 1px;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n font-weight: 400;\n background-color: white;\n}\n.igv-ui-dropdown > div {\n overflow-y: auto;\n overflow-x: hidden;\n background-color: white;\n}\n.igv-ui-dropdown > div > div {\n padding: 4px;\n width: 100%;\n overflow-x: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: 1px;\n background-color: white;\n}\n.igv-ui-dropdown > div > div:last-child {\n border-bottom-color: transparent;\n border-bottom-width: 0;\n}\n.igv-ui-dropdown > div > div:hover {\n cursor: pointer;\n background-color: rgba(0, 0, 0, 0.04);\n}\n\n.igv-ui-popover {\n cursor: default;\n position: absolute;\n z-index: 2048;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: 1px;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n background-color: white;\n}\n.igv-ui-popover > div:first-child {\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-width: 0;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n}\n.igv-ui-popover > div:first-child > div:first-child {\n margin-left: 4px;\n}\n.igv-ui-popover > div:first-child > div:last-child {\n margin-right: 4px;\n height: 12px;\n width: 12px;\n color: #7F7F7F;\n}\n.igv-ui-popover > div:first-child > div:last-child:hover {\n cursor: pointer;\n color: #444;\n}\n.igv-ui-popover > div:last-child {\n user-select: text;\n overflow-y: auto;\n overflow-x: hidden;\n max-height: 400px;\n max-width: 800px;\n background-color: white;\n border-bottom-width: 0;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n}\n.igv-ui-popover > div:last-child > div {\n margin-left: 4px;\n margin-right: 4px;\n min-width: 220px;\n overflow-x: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.igv-ui-popover > div:last-child > div > span {\n font-weight: bolder;\n}\n.igv-ui-popover > div:last-child hr {\n width: 100%;\n}\n\n.igv-ui-alert-dialog-container {\n box-sizing: content-box;\n position: absolute;\n z-index: 2048;\n top: 50%;\n left: 50%;\n width: 400px;\n height: 200px;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n outline: none;\n font-family: \"Open Sans\", sans-serif;\n font-size: 15px;\n font-weight: 400;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n}\n.igv-ui-alert-dialog-container > div:first-child {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n}\n.igv-ui-alert-dialog-container > div:first-child div:first-child {\n padding-left: 8px;\n}\n.igv-ui-alert-dialog-container .igv-ui-alert-dialog-body {\n -webkit-user-select: text;\n -moz-user-select: text;\n -ms-user-select: text;\n user-select: text;\n color: #373737;\n width: 100%;\n height: calc(100% - 24px - 64px);\n overflow-y: scroll;\n}\n.igv-ui-alert-dialog-container .igv-ui-alert-dialog-body .igv-ui-alert-dialog-body-copy {\n margin: 16px;\n width: auto;\n height: auto;\n overflow-wrap: break-word;\n word-break: break-word;\n background-color: white;\n border: unset;\n}\n.igv-ui-alert-dialog-container > div:last-child {\n width: 100%;\n margin-bottom: 10px;\n background-color: white;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: center;\n align-items: center;\n}\n.igv-ui-alert-dialog-container > div:last-child div {\n margin: unset;\n width: 40px;\n height: 30px;\n line-height: 30px;\n text-align: center;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n font-weight: 400;\n border-color: #2B81AF;\n border-style: solid;\n border-width: thin;\n border-radius: 4px;\n background-color: #2B81AF;\n}\n.igv-ui-alert-dialog-container > div:last-child div:hover {\n cursor: pointer;\n border-color: #25597f;\n background-color: #25597f;\n}\n\n.igv-ui-color-swatch {\n position: relative;\n box-sizing: content-box;\n display: flex;\n flex-flow: row;\n flex-wrap: wrap;\n justify-content: center;\n align-items: center;\n width: 32px;\n height: 32px;\n border-style: solid;\n border-width: 2px;\n border-color: white;\n border-radius: 4px;\n}\n\n.igv-ui-color-swatch:hover {\n border-color: dimgray;\n}\n\n.igv-ui-colorpicker-menu-close-button {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 32px;\n margin-top: 4px;\n margin-bottom: 4px;\n padding-right: 8px;\n}\n.igv-ui-colorpicker-menu-close-button i.fa {\n display: block;\n margin-left: 4px;\n margin-right: 4px;\n color: #5f5f5f;\n}\n.igv-ui-colorpicker-menu-close-button i.fa:hover,\n.igv-ui-colorpicker-menu-close-button i.fa:focus,\n.igv-ui-colorpicker-menu-close-button i.fa:active {\n cursor: pointer;\n color: #0f0f0f;\n}\n\n.igv-ui-generic-dialog-container {\n box-sizing: content-box;\n position: fixed;\n top: 0;\n left: 0;\n width: 300px;\n height: fit-content;\n padding-bottom: 16px;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n z-index: 2048;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-header {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-header div {\n margin-right: 4px;\n margin-bottom: 2px;\n height: 12px;\n width: 12px;\n color: #7F7F7F;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-header div:hover {\n cursor: pointer;\n color: #444;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-one-liner {\n color: #373737;\n width: 95%;\n height: 24px;\n line-height: 24px;\n text-align: left;\n margin-top: 8px;\n padding-left: 8px;\n overflow-wrap: break-word;\n background-color: white;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input {\n margin-top: 8px;\n width: 95%;\n height: 24px;\n color: #373737;\n line-height: 24px;\n padding-left: 8px;\n background-color: white;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input > div {\n width: fit-content;\n height: 100%;\n font-size: 16px;\n text-align: right;\n padding-right: 8px;\n background-color: white;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input input {\n display: block;\n height: 100%;\n width: 100%;\n padding-left: 4px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n color: #373737;\n text-align: left;\n outline: none;\n border-style: solid;\n border-width: thin;\n border-color: #7F7F7F;\n background-color: white;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input input {\n width: 50%;\n font-size: 16px;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-input {\n margin-top: 8px;\n width: calc(100% - 16px);\n height: 24px;\n color: #373737;\n line-height: 24px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-input input {\n display: block;\n height: 100%;\n width: 100%;\n padding-left: 4px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n color: #373737;\n text-align: left;\n outline: none;\n border-style: solid;\n border-width: thin;\n border-color: #7F7F7F;\n background-color: white;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-input input {\n font-size: 16px;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-input input[type=range] {\n width: 70%;\n -webkit-appearance: none;\n background: linear-gradient(90deg, white, black);\n outline: none;\n margin: 0;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-input output {\n display: block;\n height: 100%;\n width: 20%;\n font-size: 16px;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel {\n width: 100%;\n height: 28px;\n padding-top: 16px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel > div {\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n width: 75px;\n height: 28px;\n line-height: 28px;\n text-align: center;\n border-color: transparent;\n border-style: solid;\n border-width: thin;\n border-radius: 2px;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel > div:first-child {\n margin-left: 32px;\n margin-right: 0;\n background-color: #5ea4e0;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel > div:last-child {\n margin-left: 0;\n margin-right: 32px;\n background-color: #c4c4c4;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel > div:first-child:hover {\n cursor: pointer;\n background-color: #3b5c7f;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel > div:last-child:hover {\n cursor: pointer;\n background-color: #7f7f7f;\n}\n\n.igv-ui-generic-container {\n box-sizing: content-box;\n position: absolute;\n z-index: 2048;\n background-color: white;\n cursor: pointer;\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-ui-generic-container > div:first-child {\n cursor: move;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n height: 24px;\n width: 100%;\n background-color: #dddddd;\n}\n.igv-ui-generic-container > div:first-child > div {\n display: block;\n color: #5f5f5f;\n cursor: pointer;\n width: 14px;\n height: 14px;\n margin-right: 8px;\n margin-bottom: 4px;\n}\n\n.igv-ui-dialog {\n z-index: 2048;\n position: fixed;\n width: fit-content;\n height: fit-content;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n background-color: white;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n}\n.igv-ui-dialog .igv-ui-dialog-header {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n}\n.igv-ui-dialog .igv-ui-dialog-header div {\n margin-right: 4px;\n margin-bottom: 2px;\n height: 12px;\n width: 12px;\n color: #7F7F7F;\n}\n.igv-ui-dialog .igv-ui-dialog-header div:hover {\n cursor: pointer;\n color: #444;\n}\n.igv-ui-dialog .igv-ui-dialog-one-liner {\n width: 95%;\n height: 24px;\n line-height: 24px;\n text-align: left;\n margin: 8px;\n overflow-wrap: break-word;\n background-color: white;\n font-weight: bold;\n}\n.igv-ui-dialog .igv-ui-dialog-ok-cancel {\n width: 100%;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n}\n.igv-ui-dialog .igv-ui-dialog-ok-cancel div {\n margin: 16px;\n margin-top: 32px;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n width: 75px;\n height: 28px;\n line-height: 28px;\n text-align: center;\n border-color: transparent;\n border-style: solid;\n border-width: thin;\n border-radius: 2px;\n}\n.igv-ui-dialog .igv-ui-dialog-ok-cancel div:first-child {\n background-color: #5ea4e0;\n}\n.igv-ui-dialog .igv-ui-dialog-ok-cancel div:last-child {\n background-color: #c4c4c4;\n}\n.igv-ui-dialog .igv-ui-dialog-ok-cancel div:first-child:hover {\n cursor: pointer;\n background-color: #3b5c7f;\n}\n.igv-ui-dialog .igv-ui-dialog-ok-cancel div:last-child:hover {\n cursor: pointer;\n background-color: #7f7f7f;\n}\n.igv-ui-dialog .igv-ui-dialog-ok {\n width: 100%;\n height: 36px;\n margin-top: 32px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n}\n.igv-ui-dialog .igv-ui-dialog-ok div {\n width: 98px;\n height: 36px;\n line-height: 36px;\n text-align: center;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n border-color: white;\n border-style: solid;\n border-width: thin;\n border-radius: 4px;\n background-color: #2B81AF;\n}\n.igv-ui-dialog .igv-ui-dialog-ok div:hover {\n cursor: pointer;\n background-color: #25597f;\n}\n\n.igv-ui-panel, .igv-ui-panel-row, .igv-ui-panel-column {\n z-index: 2048;\n background-color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n display: flex;\n justify-content: flex-start;\n align-items: flex-start;\n}\n\n.igv-ui-panel-column {\n display: flex;\n flex-direction: column;\n}\n\n.igv-ui-panel-row {\n display: flex;\n flex-direction: row;\n}\n\n.igv-ui-textbox {\n background-color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n display: flex;\n justify-content: flex-start;\n align-items: flex-start;\n}\n\n.igv-ui-table {\n background-color: white;\n}\n\n.igv-ui-table thead {\n position: sticky;\n top: 0;\n}\n\n.igv-ui-table th {\n text-align: left;\n}\n\n.igv-ui-table td {\n padding-right: 20px;\n}\n\n.igv-ui-table tr:hover {\n background-color: lightblue;\n}\n\n.igv-ui-center-fixed {\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n}\n\n.igv-navbar {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n box-sizing: border-box;\n width: 100%;\n color: #444;\n font-size: 12px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n line-height: 32px;\n padding-left: 8px;\n padding-right: 8px;\n margin-top: 2px;\n margin-bottom: 6px;\n height: 32px;\n border-style: solid;\n border-radius: 3px;\n border-width: thin;\n border-color: #bfbfbf;\n background-color: #f3f3f3;\n}\n.igv-navbar .igv-navbar-left-container {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n height: 32px;\n line-height: 32px;\n}\n.igv-navbar .igv-navbar-left-container .igv-logo {\n width: 34px;\n height: 32px;\n margin-right: 8px;\n}\n.igv-navbar .igv-navbar-left-container .igv-current-genome {\n height: 32px;\n margin-left: 4px;\n margin-right: 4px;\n user-select: none;\n line-height: 32px;\n vertical-align: middle;\n text-align: center;\n}\n.igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n height: 100%;\n}\n.igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-chromosome-select-widget-container {\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n height: 100%;\n width: 125px;\n margin-right: 4px;\n}\n.igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-chromosome-select-widget-container select {\n display: block;\n cursor: pointer;\n width: 100px;\n height: 75%;\n outline: none;\n font-size: 12px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n}\n.igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-locus-size-group {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n margin-left: 8px;\n height: 22px;\n}\n.igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-locus-size-group .igv-search-container {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n width: 240px;\n height: 22px;\n line-height: 22px;\n}\n.igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-locus-size-group .igv-search-container input.igv-search-input {\n cursor: text;\n width: 85%;\n height: 22px;\n line-height: 22px;\n font-size: 12px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n text-align: left;\n padding-left: 8px;\n margin-right: 8px;\n outline: none;\n border-style: solid;\n border-radius: 3px;\n border-width: thin;\n border-color: #bfbfbf;\n background-color: white;\n}\n.igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-locus-size-group .igv-search-container .igv-search-icon-container {\n cursor: pointer;\n height: 16px;\n width: 16px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: center;\n align-items: center;\n}\n.igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-locus-size-group .igv-windowsize-panel-container {\n margin-left: 4px;\n user-select: none;\n}\n.igv-navbar .igv-navbar-right-container {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n}\n.igv-navbar .igv-navbar-right-container .igv-navbar-toggle-button-container {\n position: relative;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n}\n.igv-navbar .igv-navbar-right-container .igv-navbar-toggle-button-container-hidden {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n height: 100%;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget {\n color: #737373;\n font-size: 18px;\n margin-left: 8px;\n user-select: none;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget div {\n cursor: pointer;\n margin-left: unset;\n margin-right: unset;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget div:first-child {\n height: 20px;\n width: 20px;\n margin-left: unset;\n margin-right: 4px;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget div:last-child {\n height: 20px;\n width: 20px;\n margin-left: 4px;\n margin-right: unset;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget div:nth-child(even) {\n display: block;\n height: fit-content;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget input {\n display: block;\n width: 125px;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget svg {\n display: block;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 {\n color: #737373;\n font-size: 18px;\n height: 32px;\n line-height: 32px;\n margin-left: 8px;\n user-select: none;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 div {\n cursor: pointer;\n margin-left: unset;\n margin-right: unset;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 div:first-child {\n height: 20px;\n width: 20px;\n margin-left: unset;\n margin-right: 4px;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 div:last-child {\n height: 20px;\n width: 20px;\n margin-left: 4px;\n margin-right: unset;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 div:nth-child(even) {\n width: 0;\n height: 0;\n display: none;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 input {\n width: 0;\n height: 0;\n display: none;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 svg {\n display: block;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-hidden {\n display: none;\n}\n\n.igv-navbar-button {\n display: block;\n box-sizing: unset;\n padding-left: 6px;\n padding-right: 6px;\n height: 18px;\n text-transform: capitalize;\n user-select: none;\n line-height: 18px;\n text-align: center;\n vertical-align: middle;\n font-family: \"Open Sans\", sans-serif;\n font-size: 11px;\n font-weight: 200;\n color: #737373;\n background-color: #f3f3f3;\n border-color: #737373;\n border-style: solid;\n border-width: thin;\n border-radius: 6px;\n}\n\n.igv-navbar-button:hover {\n cursor: pointer;\n}\n\n.igv-navbar-button-clicked {\n color: white;\n background-color: #737373;\n}\n\n.igv-navbar-icon-button {\n cursor: pointer;\n position: relative;\n width: 24px;\n height: 24px;\n margin-left: 4px;\n margin-right: 4px;\n border: none;\n background-size: contain;\n background-repeat: no-repeat;\n background-position: center;\n}\n.igv-navbar-icon-button > div:first-child {\n z-index: 512;\n position: absolute;\n top: 36px;\n left: -18px;\n width: 24px;\n height: 24px;\n border: none;\n background-size: contain;\n background-repeat: no-repeat;\n background-position: center;\n}\n.igv-navbar-icon-button > div:last-child {\n z-index: 512;\n position: absolute;\n top: 36px;\n left: 18px;\n width: 24px;\n height: 24px;\n border: none;\n background-size: contain;\n background-repeat: no-repeat;\n background-position: center;\n}\n\n.igv-navbar-text-button {\n cursor: pointer;\n position: relative;\n margin-left: 2px;\n margin-right: 2px;\n border: none;\n display: flex;\n justify-content: center;\n align-items: center;\n}\n.igv-navbar-text-button > div:nth-child(2) {\n z-index: 512;\n position: absolute;\n top: 36px;\n left: 0;\n width: 38px;\n height: 18px;\n border: none;\n background-size: contain;\n background-repeat: no-repeat;\n background-position: center;\n}\n.igv-navbar-text-button > div:nth-child(3) {\n z-index: 512;\n position: absolute;\n top: 36px;\n left: 42px;\n width: 38px;\n height: 18px;\n border: none;\n background-size: contain;\n background-repeat: no-repeat;\n background-position: center;\n}\n\n#igv-text-button-label {\n text-anchor: middle;\n dominant-baseline: middle;\n}\n\n.igv-navbar-text-button-svg-inactive rect {\n stroke: #737373;\n fill: white;\n}\n.igv-navbar-text-button-svg-inactive text {\n fill: #737373;\n}\n.igv-navbar-text-button-svg-inactive tspan {\n dominant-baseline: middle;\n}\n\n.igv-navbar-text-button-svg-hover rect {\n stroke: #737373;\n fill: #737373;\n}\n.igv-navbar-text-button-svg-hover text {\n fill: white;\n}\n.igv-navbar-text-button-svg-hover tspan {\n dominant-baseline: middle;\n}\n\n#igv-save-svg-group rect {\n stroke: #737373;\n fill: white;\n}\n#igv-save-svg-group text {\n fill: #737373;\n}\n\n#igv-save-svg-group:hover rect {\n stroke: #737373;\n fill: #737373;\n}\n#igv-save-svg-group:hover text {\n fill: white;\n}\n\n#igv-save-png-group rect {\n stroke: #737373;\n fill: white;\n}\n#igv-save-png-group text {\n fill: #737373;\n}\n\n#igv-save-png-group:hover rect {\n stroke: #737373;\n fill: #737373;\n}\n#igv-save-png-group:hover text {\n fill: white;\n}\n\n.igv-zoom-in-notice-container {\n z-index: 256;\n position: absolute;\n top: 8px;\n left: 50%;\n transform: translate(-50%, 0%);\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n justify-content: center;\n align-items: center;\n background-color: white;\n}\n.igv-zoom-in-notice-container > div {\n padding-left: 4px;\n padding-right: 4px;\n padding-top: 2px;\n padding-bottom: 2px;\n width: 100%;\n height: 100%;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n color: #3f3f3f;\n}\n\n.igv-zoom-in-notice {\n position: absolute;\n top: 10px;\n left: 50%;\n}\n.igv-zoom-in-notice div {\n position: relative;\n left: -50%;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n color: #3f3f3f;\n background-color: rgba(255, 255, 255, 0.51);\n z-index: 64;\n}\n\n.igv-container-spinner {\n position: absolute;\n top: 90%;\n left: 50%;\n transform: translate(-50%, -50%);\n z-index: 1024;\n width: 24px;\n height: 24px;\n pointer-events: none;\n color: #737373;\n}\n\n.igv-multi-locus-close-button {\n position: absolute;\n top: 2px;\n right: 0;\n padding-left: 2px;\n padding-right: 2px;\n width: 12px;\n height: 12px;\n color: #666666;\n background-color: white;\n z-index: 1000;\n}\n.igv-multi-locus-close-button > svg {\n vertical-align: top;\n}\n\n.igv-multi-locus-close-button:hover {\n cursor: pointer;\n color: #434343;\n}\n\n.igv-multi-locus-ruler-label {\n z-index: 64;\n position: absolute;\n top: 2px;\n left: 0;\n width: 100%;\n height: 12px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: center;\n align-items: center;\n}\n.igv-multi-locus-ruler-label > div {\n font-family: \"Open Sans\", sans-serif;\n font-size: 12px;\n color: rgb(16, 16, 16);\n background-color: white;\n}\n.igv-multi-locus-ruler-label > div {\n cursor: pointer;\n}\n\n.igv-multi-locus-ruler-label-square-dot {\n z-index: 64;\n position: absolute;\n left: 50%;\n top: 5%;\n transform: translate(-50%, 0%);\n background-color: white;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-multi-locus-ruler-label-square-dot > div:first-child {\n width: 14px;\n height: 14px;\n}\n.igv-multi-locus-ruler-label-square-dot > div:last-child {\n margin-left: 16px;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n color: rgb(16, 16, 16);\n}\n\n.igv-ruler-sweeper {\n display: none;\n pointer-events: none;\n position: absolute;\n top: 26px;\n bottom: 0;\n left: 0;\n width: 0;\n z-index: 99999;\n background-color: rgba(68, 134, 247, 0.25);\n}\n\n.igv-ruler-tooltip {\n pointer-events: none;\n z-index: 128;\n position: absolute;\n top: 0;\n left: 0;\n width: 1px;\n height: 32px;\n background-color: transparent;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-ruler-tooltip > div {\n pointer-events: none;\n width: 128px;\n height: auto;\n padding: 1px;\n color: #373737;\n font-size: 10px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n background-color: white;\n border-style: solid;\n border-width: thin;\n border-color: #373737;\n}\n\n.igv-track-label {\n position: absolute;\n left: 8px;\n top: 8px;\n width: auto;\n height: auto;\n max-width: 50%;\n padding-left: 4px;\n padding-right: 4px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n font-weight: 400;\n text-align: center;\n user-select: none;\n -moz-user-select: none;\n -webkit-user-select: none;\n border-color: #444;\n border-radius: 2px;\n border-style: solid;\n border-width: thin;\n background-color: white;\n z-index: 128;\n cursor: pointer;\n}\n\n.igv-track-label:hover,\n.igv-track-label:focus,\n.igv-track-label:active {\n background-color: #e8e8e8;\n}\n\n.igv-track-label-popup-shim {\n padding-left: 8px;\n padding-right: 8px;\n padding-top: 4px;\n}\n\n.igv-center-line {\n display: none;\n pointer-events: none;\n position: absolute;\n top: 0;\n bottom: 0;\n left: 50%;\n transform: translateX(-50%);\n z-index: 8;\n user-select: none;\n -moz-user-select: none;\n -webkit-user-select: none;\n border-left-style: dashed;\n border-left-width: thin;\n border-right-style: dashed;\n border-right-width: thin;\n}\n\n.igv-center-line-wide {\n background-color: rgba(0, 0, 0, 0);\n border-left-color: rgba(127, 127, 127, 0.51);\n border-right-color: rgba(127, 127, 127, 0.51);\n}\n\n.igv-center-line-thin {\n background-color: rgba(0, 0, 0, 0);\n border-left-color: rgba(127, 127, 127, 0.51);\n border-right-color: rgba(0, 0, 0, 0);\n}\n\n.igv-cursor-guide-horizontal {\n display: none;\n pointer-events: none;\n user-select: none;\n -moz-user-select: none;\n -webkit-user-select: none;\n position: absolute;\n left: 0;\n right: 0;\n top: 50%;\n height: 1px;\n z-index: 32;\n margin-left: 50px;\n margin-right: 54px;\n border-top-style: dotted;\n border-top-width: thin;\n border-top-color: rgba(127, 127, 127, 0.76);\n}\n\n.igv-cursor-guide-vertical {\n pointer-events: none;\n user-select: none;\n -moz-user-select: none;\n -webkit-user-select: none;\n position: absolute;\n top: 0;\n bottom: 0;\n left: 50%;\n width: 1px;\n z-index: 32;\n border-left-style: dotted;\n border-left-width: thin;\n border-left-color: rgba(127, 127, 127, 0.76);\n display: none;\n}\n\n.igv-user-feedback {\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 512px;\n height: 360px;\n z-index: 2048;\n background-color: white;\n border-color: #a2a2a2;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n color: #444;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-user-feedback div:first-child {\n position: relative;\n height: 24px;\n width: 100%;\n background-color: white;\n border-bottom-color: #a2a2a2;\n border-bottom-style: solid;\n border-bottom-width: thin;\n}\n.igv-user-feedback div:first-child div {\n position: absolute;\n top: 2px;\n width: 16px;\n height: 16px;\n background-color: transparent;\n}\n.igv-user-feedback div:first-child div:first-child {\n left: 8px;\n}\n.igv-user-feedback div:first-child div:last-child {\n cursor: pointer;\n right: 8px;\n}\n.igv-user-feedback div:last-child {\n width: 100%;\n height: calc(100% - 24px);\n border-width: 0;\n}\n.igv-user-feedback div:last-child div {\n width: auto;\n height: auto;\n margin: 8px;\n}\n\n.igv-generic-dialog-container {\n position: absolute;\n top: 0;\n left: 0;\n width: 300px;\n height: 200px;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n z-index: 2048;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-generic-dialog-container .igv-generic-dialog-header {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n}\n.igv-generic-dialog-container .igv-generic-dialog-header div {\n margin-right: 4px;\n margin-bottom: 2px;\n height: 12px;\n width: 12px;\n color: #7F7F7F;\n}\n.igv-generic-dialog-container .igv-generic-dialog-header div:hover {\n cursor: pointer;\n color: #444;\n}\n.igv-generic-dialog-container .igv-generic-dialog-one-liner {\n color: #373737;\n width: 95%;\n height: 24px;\n line-height: 24px;\n text-align: left;\n margin-top: 8px;\n padding-left: 8px;\n overflow-wrap: break-word;\n background-color: white;\n}\n.igv-generic-dialog-container .igv-generic-dialog-label-input {\n margin-top: 8px;\n width: 95%;\n height: 24px;\n color: #373737;\n line-height: 24px;\n padding-left: 8px;\n background-color: white;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-generic-dialog-container .igv-generic-dialog-label-input div {\n width: 30%;\n height: 100%;\n font-size: 16px;\n text-align: right;\n padding-right: 8px;\n background-color: white;\n}\n.igv-generic-dialog-container .igv-generic-dialog-label-input input {\n display: block;\n height: 100%;\n width: 100%;\n padding-left: 4px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n color: #373737;\n text-align: left;\n outline: none;\n border-style: solid;\n border-width: thin;\n border-color: #7F7F7F;\n background-color: white;\n}\n.igv-generic-dialog-container .igv-generic-dialog-label-input input {\n width: 50%;\n font-size: 16px;\n}\n.igv-generic-dialog-container .igv-generic-dialog-input {\n margin-top: 8px;\n width: calc(100% - 16px);\n height: 24px;\n color: #373737;\n line-height: 24px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n}\n.igv-generic-dialog-container .igv-generic-dialog-input input {\n display: block;\n height: 100%;\n width: 100%;\n padding-left: 4px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n color: #373737;\n text-align: left;\n outline: none;\n border-style: solid;\n border-width: thin;\n border-color: #7F7F7F;\n background-color: white;\n}\n.igv-generic-dialog-container .igv-generic-dialog-input input {\n font-size: 16px;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok-cancel {\n width: 100%;\n height: 28px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok-cancel div {\n margin-top: 32px;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n width: 75px;\n height: 28px;\n line-height: 28px;\n text-align: center;\n border-color: transparent;\n border-style: solid;\n border-width: thin;\n border-radius: 2px;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok-cancel div:first-child {\n margin-left: 32px;\n margin-right: 0;\n background-color: #5ea4e0;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok-cancel div:last-child {\n margin-left: 0;\n margin-right: 32px;\n background-color: #c4c4c4;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok-cancel div:first-child:hover {\n cursor: pointer;\n background-color: #3b5c7f;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok-cancel div:last-child:hover {\n cursor: pointer;\n background-color: #7f7f7f;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok {\n width: 100%;\n height: 36px;\n margin-top: 32px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok div {\n width: 98px;\n height: 36px;\n line-height: 36px;\n text-align: center;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n border-color: white;\n border-style: solid;\n border-width: thin;\n border-radius: 4px;\n background-color: #2B81AF;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok div:hover {\n cursor: pointer;\n background-color: #25597f;\n}\n\n.igv-generic-container {\n position: absolute;\n top: 0;\n left: 0;\n z-index: 2048;\n background-color: white;\n cursor: pointer;\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-generic-container div:first-child {\n cursor: move;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n height: 24px;\n width: 100%;\n background-color: #dddddd;\n}\n.igv-generic-container div:first-child i {\n display: block;\n color: #5f5f5f;\n cursor: pointer;\n width: 14px;\n height: 14px;\n margin-right: 8px;\n margin-bottom: 4px;\n}\n\n.igv-menu-popup {\n position: absolute;\n top: 0;\n left: 0;\n width: max-content;\n max-width: 400px;\n z-index: 512;\n cursor: pointer;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n font-weight: 400;\n color: #4b4b4b;\n background: white;\n border-radius: 4px;\n border-color: #7F7F7F;\n border-style: solid;\n border-width: thin;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-end;\n text-align: left;\n}\n.igv-menu-popup > div:not(:first-child) {\n width: 100%;\n}\n.igv-menu-popup > div:not(:first-child) > div {\n background: white;\n}\n.igv-menu-popup > div:not(:first-child) > div.context-menu {\n padding-left: 4px;\n padding-right: 4px;\n}\n.igv-menu-popup > div:not(:first-child) > div:last-child {\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n border-bottom-color: transparent;\n border-bottom-style: solid;\n border-bottom-width: thin;\n}\n.igv-menu-popup > div:not(:first-child) > div:hover {\n background: #efefef;\n}\n\n.igv-menu-popup-shim {\n padding-left: 8px;\n padding-right: 8px;\n padding-bottom: 1px;\n padding-top: 1px;\n}\n\n.igv-menu-popup-header {\n position: relative;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-color: transparent;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n}\n.igv-menu-popup-header div {\n margin-right: 4px;\n height: 12px;\n width: 12px;\n color: #7F7F7F;\n}\n.igv-menu-popup-header div:hover {\n cursor: pointer;\n color: #444;\n}\n\n.igv-menu-popup-check-container {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n width: 100%;\n height: 20px;\n margin-right: 4px;\n background-color: transparent;\n}\n.igv-menu-popup-check-container div {\n padding-top: 2px;\n padding-left: 8px;\n}\n.igv-menu-popup-check-container div:first-child {\n position: relative;\n width: 12px;\n height: 12px;\n}\n.igv-menu-popup-check-container div:first-child svg {\n position: absolute;\n width: 12px;\n height: 12px;\n}\n\n.igv-user-feedback {\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 512px;\n height: 360px;\n z-index: 2048;\n background-color: white;\n border-color: #a2a2a2;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n color: #444;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-user-feedback div:first-child {\n position: relative;\n height: 24px;\n width: 100%;\n background-color: white;\n border-bottom-color: #a2a2a2;\n border-bottom-style: solid;\n border-bottom-width: thin;\n}\n.igv-user-feedback div:first-child div {\n position: absolute;\n top: 2px;\n width: 16px;\n height: 16px;\n background-color: transparent;\n}\n.igv-user-feedback div:first-child div:first-child {\n left: 8px;\n}\n.igv-user-feedback div:first-child div:last-child {\n cursor: pointer;\n right: 8px;\n}\n.igv-user-feedback div:last-child {\n width: 100%;\n height: calc(100% - 24px);\n border-width: 0;\n}\n.igv-user-feedback div:last-child div {\n width: auto;\n height: auto;\n margin: 8px;\n}\n\n.igv-loading-spinner-container {\n z-index: 1024;\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 32px;\n height: 32px;\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n justify-content: center;\n align-items: center;\n}\n.igv-loading-spinner-container > div {\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n border-radius: 50%;\n border: 4px solid rgba(128, 128, 128, 0.5);\n border-top-color: rgb(255, 255, 255);\n animation: spin 1s ease-in-out infinite;\n -webkit-animation: spin 1s ease-in-out infinite;\n}\n\n@keyframes spin {\n to {\n -webkit-transform: rotate(360deg);\n transform: rotate(360deg);\n }\n}\n@-webkit-keyframes spin {\n to {\n -webkit-transform: rotate(360deg);\n transform: rotate(360deg);\n }\n}\n.igv-roi-menu {\n position: absolute;\n z-index: 512;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n font-weight: 400;\n color: #4b4b4b;\n background-color: white;\n width: 192px;\n border-radius: 4px;\n border-color: #7F7F7F;\n border-style: solid;\n border-width: thin;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: stretch;\n}\n.igv-roi-menu > div:first-child {\n height: 24px;\n border-top-color: transparent;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n}\n.igv-roi-menu > div:first-child > div {\n margin-right: 4px;\n height: 12px;\n width: 12px;\n color: #7F7F7F;\n}\n.igv-roi-menu > div:first-child > div:hover {\n cursor: pointer;\n color: #444;\n}\n.igv-roi-menu > div:last-child {\n background-color: white;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n border-bottom-color: transparent;\n border-bottom-style: solid;\n border-bottom-width: 0;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: stretch;\n text-align: start;\n vertical-align: middle;\n}\n.igv-roi-menu > div:last-child > div {\n height: 24px;\n padding-left: 4px;\n border-bottom-style: solid;\n border-bottom-width: thin;\n border-bottom-color: #7f7f7f;\n}\n.igv-roi-menu > div:last-child > div:not(:first-child):hover {\n cursor: pointer;\n background-color: rgba(127, 127, 127, 0.1);\n}\n.igv-roi-menu > div:last-child div:first-child {\n font-style: italic;\n text-align: center;\n padding-right: 4px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.igv-roi-menu > div:last-child > div:last-child {\n border-bottom-width: 0;\n border-bottom-color: transparent;\n}\n\n.igv-roi-placeholder {\n font-style: normal;\n color: rgba(75, 75, 75, 0.6);\n}\n\n.igv-roi-table {\n position: absolute;\n z-index: 1024;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: stretch;\n resize: both;\n overflow: hidden;\n width: min-content;\n max-width: 1600px;\n border-color: #7f7f7f;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: 12px;\n font-weight: 400;\n background-color: white;\n cursor: default;\n}\n.igv-roi-table > div {\n height: 24px;\n font-size: 14px;\n text-align: start;\n vertical-align: middle;\n line-height: 24px;\n}\n.igv-roi-table > div:first-child {\n border-color: transparent;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-top-width: 0;\n border-bottom-color: #7f7f7f;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n cursor: move;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n}\n.igv-roi-table > div:first-child > div:first-child {\n text-align: center;\n white-space: nowrap;\n text-overflow: ellipsis;\n overflow: hidden;\n margin-left: 4px;\n margin-right: 4px;\n width: calc(100% - 4px - 12px);\n}\n.igv-roi-table > div:first-child > div:last-child {\n margin-right: 4px;\n margin-bottom: 2px;\n height: 12px;\n width: 12px;\n color: #7f7f7f;\n}\n.igv-roi-table > div:first-child > div:last-child > svg {\n display: block;\n}\n.igv-roi-table > div:first-child > div:last-child:hover {\n cursor: pointer;\n color: #444;\n}\n.igv-roi-table > .igv-roi-table-description {\n padding: 4px;\n margin-left: 4px;\n word-break: break-all;\n overflow-y: auto;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n background-color: transparent;\n}\n.igv-roi-table > .igv-roi-table-goto-explainer {\n margin-top: 5px;\n margin-left: 4px;\n color: #7F7F7F;\n font-style: italic;\n height: 24px;\n border-top: solid lightgray;\n background-color: transparent;\n}\n.igv-roi-table > .igv-roi-table-column-titles {\n height: 24px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: stretch;\n align-items: stretch;\n padding-right: 16px;\n background-color: white;\n border-top-color: #7f7f7f;\n border-top-style: solid;\n border-top-width: thin;\n border-bottom-color: #7f7f7f;\n border-bottom-style: solid;\n border-bottom-width: thin;\n}\n.igv-roi-table > .igv-roi-table-column-titles > div {\n font-size: 14px;\n vertical-align: middle;\n line-height: 24px;\n text-align: left;\n margin-left: 4px;\n height: 24px;\n overflow: hidden;\n text-overflow: ellipsis;\n border-right-color: #7f7f7f;\n border-right-style: solid;\n border-right-width: thin;\n}\n.igv-roi-table > .igv-roi-table-column-titles > div:last-child {\n border-right: unset;\n}\n.igv-roi-table > .igv-roi-table-row-container {\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: stretch;\n overflow: auto;\n height: 360px;\n flex: 1 1 auto;\n background-color: transparent;\n}\n.igv-roi-table > .igv-roi-table-row-container > .igv-roi-table-row {\n height: 24px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: stretch;\n align-items: stretch;\n}\n.igv-roi-table > .igv-roi-table-row-container > .igv-roi-table-row > div {\n font-size: 14px;\n vertical-align: middle;\n line-height: 24px;\n text-align: left;\n margin-left: 4px;\n height: 24px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n border-right-color: transparent;\n border-right-style: solid;\n border-right-width: thin;\n}\n.igv-roi-table > .igv-roi-table-row-container > .igv-roi-table-row > div:last-child {\n border-right: unset;\n}\n.igv-roi-table > .igv-roi-table-row-container > .igv-roi-table-row-hover {\n background-color: rgba(0, 0, 0, 0.04);\n}\n.igv-roi-table > div:last-child {\n min-height: 32px;\n height: 32px;\n line-height: 32px;\n border-top-color: #7f7f7f;\n border-top-style: solid;\n border-top-width: thin;\n border-bottom-color: transparent;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n border-bottom-width: 0;\n background-color: #eee;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n}\n\n.igv-roi-table-row-selected {\n background-color: rgba(0, 0, 0, 0.125);\n}\n\n.igv-roi-table-button {\n cursor: pointer;\n height: 20px;\n user-select: none;\n line-height: 20px;\n text-align: center;\n vertical-align: middle;\n font-family: \"Open Sans\", sans-serif;\n font-size: 13px;\n font-weight: 400;\n color: black;\n padding-left: 6px;\n padding-right: 6px;\n background-color: rgb(239, 239, 239);\n border-color: black;\n border-style: solid;\n border-width: thin;\n border-radius: 3px;\n}\n\n.igv-roi-table-button:hover {\n font-weight: 400;\n background-color: rgba(0, 0, 0, 0.13);\n}\n\n.igv-roi-region {\n z-index: 64;\n position: absolute;\n top: 0;\n bottom: 0;\n pointer-events: none;\n overflow: visible;\n margin-top: 66px;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: stretch;\n}\n.igv-roi-region > div {\n position: relative;\n width: 100%;\n height: 8px;\n pointer-events: auto;\n}\n\n.igv-roi-menu-row {\n height: 24px;\n padding-left: 8px;\n font-size: small;\n text-align: start;\n vertical-align: middle;\n line-height: 24px;\n background-color: white;\n}\n\n.igv-roi-menu-row-edit-description {\n width: -webkit-fill-available;\n font-size: small;\n text-align: start;\n vertical-align: middle;\n background-color: white;\n padding-left: 4px;\n padding-right: 4px;\n padding-bottom: 4px;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: stretch;\n align-items: stretch;\n}\n.igv-roi-menu-row-edit-description > label {\n margin-left: 2px;\n margin-bottom: 0;\n display: block;\n width: -webkit-fill-available;\n}\n.igv-roi-menu-row-edit-description > input {\n display: block;\n margin-left: 2px;\n margin-right: 2px;\n margin-bottom: 1px;\n width: -webkit-fill-available;\n}\n\n.igv-container {\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n padding-top: 4px;\n user-select: none;\n -webkit-user-select: none;\n -ms-user-select: none;\n min-height: 160px;\n}\n\n.igv-viewport {\n position: relative;\n margin-top: 5px;\n line-height: 1;\n overflow-x: hidden;\n overflow-y: hidden;\n}\n\n.igv-viewport-content {\n position: relative;\n width: 100%;\n}\n.igv-viewport-content > canvas {\n position: relative;\n display: block;\n}\n\n.igv-column-container {\n position: relative;\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: stretch;\n width: 100%;\n}\n\n.igv-column-shim {\n width: 1px;\n margin-left: 2px;\n margin-right: 2px;\n background-color: #545453;\n}\n\n.igv-axis-column {\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n width: 50px;\n}\n.igv-axis-column > div {\n position: relative;\n margin-top: 5px;\n width: 100%;\n}\n.igv-axis-column > div > div {\n z-index: 512;\n position: absolute;\n top: 8px;\n left: 8px;\n width: fit-content;\n height: fit-content;\n background-color: transparent;\n display: grid;\n align-items: start;\n justify-items: center;\n}\n.igv-axis-column > div > div > input {\n display: block;\n margin: unset;\n cursor: pointer;\n}\n\n.igv-column {\n position: relative;\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n}\n\n.igv-sample-info-column {\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n}\n\n.igv-sample-name-column {\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n}\n\n.igv-scrollbar-column {\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n width: 14px;\n}\n.igv-scrollbar-column > div {\n position: relative;\n margin-top: 5px;\n width: 14px;\n}\n.igv-scrollbar-column > div > div {\n cursor: pointer;\n position: absolute;\n top: 0;\n left: 2px;\n width: 8px;\n border-width: 1px;\n border-style: solid;\n border-color: #c4c4c4;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n}\n.igv-scrollbar-column > div > div:hover {\n background-color: #c4c4c4;\n}\n\n.igv-track-drag-column {\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n width: 12px;\n background-color: white;\n}\n.igv-track-drag-column > .igv-track-drag-handle {\n z-index: 512;\n position: relative;\n cursor: pointer;\n margin-top: 5px;\n width: 100%;\n border-style: solid;\n border-width: 0;\n border-top-right-radius: 6px;\n border-bottom-right-radius: 6px;\n}\n.igv-track-drag-column .igv-track-drag-handle-color {\n background-color: #c4c4c4;\n}\n.igv-track-drag-column .igv-track-drag-handle-hover-color {\n background-color: #787878;\n}\n.igv-track-drag-column .igv-track-drag-handle-selected-color {\n background-color: #0963fa;\n}\n.igv-track-drag-column > .igv-track-drag-shim {\n position: relative;\n margin-top: 5px;\n width: 100%;\n border-style: solid;\n border-width: 0;\n}\n\n.igv-gear-menu-column {\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n width: 28px;\n}\n.igv-gear-menu-column > div {\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n margin-top: 5px;\n width: 100%;\n background: white;\n}\n.igv-gear-menu-column > div > div {\n position: relative;\n margin-top: 4px;\n width: 16px;\n height: 16px;\n color: #7F7F7F;\n}\n.igv-gear-menu-column > div > div:hover {\n cursor: pointer;\n color: #444;\n}\n\n/*# sourceMappingURL=igv.css.map */\n';
|
|
73699
73746
|
|
|
73700
73747
|
/**
|
|
73701
73748
|
* Manages XQTL selections.
|
|
@@ -74578,6 +74625,10 @@
|
|
|
74578
74625
|
*/
|
|
74579
74626
|
async loadGenome(idOrConfig) {
|
|
74580
74627
|
|
|
74628
|
+
if(idOrConfig.genarkAccession) {
|
|
74629
|
+
idOrConfig.url = convertToHubURL(idOrConfig.genarkAccession);
|
|
74630
|
+
}
|
|
74631
|
+
|
|
74581
74632
|
// Translate the generic "url" field, used by clients such as igv-webapp
|
|
74582
74633
|
if (idOrConfig.url) {
|
|
74583
74634
|
if (isString$2(idOrConfig.url) && idOrConfig.url.endsWith("/hub.txt")) {
|