igv 3.0.2 → 3.0.4
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 +1360 -1348
- package/dist/igv.esm.min.js +10 -10
- package/dist/igv.esm.min.js.map +1 -1
- package/dist/igv.js +1360 -1348
- package/dist/igv.min.js +10 -10
- package/dist/igv.min.js.map +1 -1
- package/package.json +1 -1
package/dist/igv.esm.js
CHANGED
|
@@ -25216,9 +25216,7 @@ class GFFHelper {
|
|
|
25216
25216
|
} else {
|
|
25217
25217
|
combinedFeatures = this.combineFeaturesByType(features);
|
|
25218
25218
|
}
|
|
25219
|
-
|
|
25220
|
-
return a.start - b.start
|
|
25221
|
-
});
|
|
25219
|
+
|
|
25222
25220
|
this.numberExons(combinedFeatures, genomicInterval);
|
|
25223
25221
|
this.nameFeatures(combinedFeatures);
|
|
25224
25222
|
return combinedFeatures
|
|
@@ -29775,20 +29773,30 @@ class FeatureFileReader {
|
|
|
29775
29773
|
await this.readHeader();
|
|
29776
29774
|
}
|
|
29777
29775
|
|
|
29776
|
+
let allFeatures;
|
|
29778
29777
|
const index = await this.getIndex();
|
|
29779
29778
|
if (index) {
|
|
29780
29779
|
this.indexed = true;
|
|
29781
|
-
|
|
29780
|
+
allFeatures = await this.loadFeaturesWithIndex(chr, start, end);
|
|
29782
29781
|
} else if (this.dataURI) {
|
|
29783
29782
|
this.indexed = false;
|
|
29784
|
-
|
|
29783
|
+
allFeatures = await this.loadFeaturesFromDataURI();
|
|
29785
29784
|
} else if ("service" === this.config.sourceType) {
|
|
29786
|
-
|
|
29785
|
+
allFeatures = await this.loadFeaturesFromService(chr, start, end);
|
|
29787
29786
|
} else {
|
|
29788
29787
|
this.indexed = false;
|
|
29789
|
-
|
|
29788
|
+
allFeatures = await this.loadFeaturesNoIndex();
|
|
29790
29789
|
}
|
|
29791
29790
|
|
|
29791
|
+
allFeatures.sort(function (a, b) {
|
|
29792
|
+
if (a.chr === b.chr) {
|
|
29793
|
+
return a.start - b.start
|
|
29794
|
+
} else {
|
|
29795
|
+
return a.chr.localeCompare(b.chr)
|
|
29796
|
+
}
|
|
29797
|
+
});
|
|
29798
|
+
|
|
29799
|
+
return allFeatures
|
|
29792
29800
|
}
|
|
29793
29801
|
|
|
29794
29802
|
async readHeader() {
|
|
@@ -29960,9 +29968,6 @@ class FeatureFileReader {
|
|
|
29960
29968
|
await this._parse(allFeatures, dataWrapper, chr, end, start);
|
|
29961
29969
|
|
|
29962
29970
|
}
|
|
29963
|
-
allFeatures.sort(function (a, b) {
|
|
29964
|
-
return a.start - b.start
|
|
29965
|
-
});
|
|
29966
29971
|
|
|
29967
29972
|
return allFeatures
|
|
29968
29973
|
}
|
|
@@ -29992,8 +29997,13 @@ class FeatureFileReader {
|
|
|
29992
29997
|
|
|
29993
29998
|
let features = await this.parser.parseFeatures(dataWrapper);
|
|
29994
29999
|
|
|
29995
|
-
|
|
29996
|
-
|
|
30000
|
+
features.sort(function (a, b) {
|
|
30001
|
+
if (a.chr === b.chr) {
|
|
30002
|
+
return a.start - b.start
|
|
30003
|
+
} else {
|
|
30004
|
+
return a.chr.localeCompare(b.chr)
|
|
30005
|
+
}
|
|
30006
|
+
});
|
|
29997
30007
|
|
|
29998
30008
|
// Filter features not in requested range.
|
|
29999
30009
|
if (undefined === chr) {
|
|
@@ -30002,26 +30012,21 @@ class FeatureFileReader {
|
|
|
30002
30012
|
let inInterval = false;
|
|
30003
30013
|
for (let i = 0; i < features.length; i++) {
|
|
30004
30014
|
const f = features[i];
|
|
30005
|
-
if (f.chr
|
|
30006
|
-
if (
|
|
30007
|
-
|
|
30008
|
-
|
|
30009
|
-
break //adjacent chr to the right
|
|
30015
|
+
if (f.chr === chr) {
|
|
30016
|
+
if (f.start > end) {
|
|
30017
|
+
allFeatures.push(f); // First feature beyond interval
|
|
30018
|
+
break
|
|
30010
30019
|
}
|
|
30011
|
-
|
|
30012
|
-
|
|
30013
|
-
|
|
30014
|
-
|
|
30015
|
-
|
|
30016
|
-
|
|
30017
|
-
|
|
30018
|
-
if (!inInterval) {
|
|
30019
|
-
inInterval = true;
|
|
30020
|
-
if (i > 0) {
|
|
30021
|
-
allFeatures.push(features[i - 1]);
|
|
30020
|
+
if (f.end >= start && f.start <= end) {
|
|
30021
|
+
// All this to grab first feature before start of interval. Needed for some track renderers, like line plot
|
|
30022
|
+
if (!inInterval) {
|
|
30023
|
+
inInterval = true;
|
|
30024
|
+
if (i > 0) {
|
|
30025
|
+
allFeatures.push(features[i - 1]);
|
|
30026
|
+
}
|
|
30022
30027
|
}
|
|
30028
|
+
allFeatures.push(f);
|
|
30023
30029
|
}
|
|
30024
|
-
allFeatures.push(f);
|
|
30025
30030
|
}
|
|
30026
30031
|
}
|
|
30027
30032
|
}
|
|
@@ -30744,286 +30749,686 @@ class BaseFeatureSource {
|
|
|
30744
30749
|
|
|
30745
30750
|
}
|
|
30746
30751
|
|
|
30747
|
-
|
|
30748
|
-
|
|
30749
|
-
/**
|
|
30750
|
-
* feature source for "bed like" files (tab or whitespace delimited files with 1 feature per line: bed, gff, vcf, etc)
|
|
30752
|
+
/*
|
|
30753
|
+
* The MIT License (MIT)
|
|
30751
30754
|
*
|
|
30752
|
-
*
|
|
30753
|
-
*
|
|
30755
|
+
* Copyright (c) 2016 University of California San Diego
|
|
30756
|
+
* Author: Jim Robinson
|
|
30757
|
+
*
|
|
30758
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
30759
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
30760
|
+
* in the Software without restriction, including without limitation the rights
|
|
30761
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
30762
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
30763
|
+
* furnished to do so, subject to the following conditions:
|
|
30764
|
+
*
|
|
30765
|
+
* The above copyright notice and this permission notice shall be included in
|
|
30766
|
+
* all copies or substantial portions of the Software.
|
|
30767
|
+
*
|
|
30768
|
+
*
|
|
30769
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
30770
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
30771
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
30772
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
30773
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
30774
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
30775
|
+
* THE SOFTWARE.
|
|
30754
30776
|
*/
|
|
30755
|
-
class TextFeatureSource extends BaseFeatureSource {
|
|
30756
30777
|
|
|
30757
|
-
|
|
30778
|
+
const GZIP_FLAG = 0x1;
|
|
30758
30779
|
|
|
30759
|
-
|
|
30780
|
+
class TDFReader {
|
|
30760
30781
|
|
|
30761
|
-
|
|
30782
|
+
constructor(config, genome) {
|
|
30783
|
+
this.config = config;
|
|
30762
30784
|
this.genome = genome;
|
|
30763
|
-
this.
|
|
30764
|
-
this.
|
|
30765
|
-
this.
|
|
30785
|
+
this.path = config.url;
|
|
30786
|
+
this.groupCache = {};
|
|
30787
|
+
this.datasetCache = {};
|
|
30788
|
+
}
|
|
30766
30789
|
|
|
30767
|
-
const queryableFormats = new Set(["bigwig", "bw", "bigbed", "bb", "biginteract", "biggenepred", "bignarrowpeak", "tdf"]);
|
|
30768
30790
|
|
|
30769
|
-
|
|
30770
|
-
|
|
30771
|
-
|
|
30772
|
-
this
|
|
30773
|
-
this.queryable = config.queryable !== false;
|
|
30774
|
-
} else if (config.sourceType === "ga4gh") {
|
|
30775
|
-
throw Error("Unsupported source type 'ga4gh'")
|
|
30776
|
-
} else if ((config.type === "eqtl" || config.type === "qtl") && config.sourceType === "gtex-ws") {
|
|
30777
|
-
this.reader = new GtexReader(config);
|
|
30778
|
-
this.queryable = true;
|
|
30779
|
-
} else if ("htsget" === config.sourceType) {
|
|
30780
|
-
this.reader = new HtsgetVariantReader(config, genome);
|
|
30781
|
-
this.queryable = true;
|
|
30782
|
-
} else if (config.sourceType === 'ucscservice') {
|
|
30783
|
-
this.reader = new UCSCServiceReader(config.source);
|
|
30784
|
-
this.queryable = true;
|
|
30785
|
-
} else if (config.sourceType === 'custom') {
|
|
30786
|
-
this.reader = new CustomServiceReader(config.source);
|
|
30787
|
-
this.queryable = false !== config.source.queryable;
|
|
30788
|
-
} else if ('service' === config.sourceType) {
|
|
30789
|
-
this.reader = new FeatureFileReader(config, genome);
|
|
30790
|
-
this.queryable = true;
|
|
30791
|
-
} else {
|
|
30792
|
-
// File of some type (i.e. not a webservice)
|
|
30793
|
-
this.reader = new FeatureFileReader(config, genome);
|
|
30794
|
-
if (config.queryable !== undefined) {
|
|
30795
|
-
this.queryable = config.queryable;
|
|
30796
|
-
} else if (queryableFormats.has(config.format) || this.reader.indexed) {
|
|
30797
|
-
this.queryable = true;
|
|
30798
|
-
} else ;
|
|
30791
|
+
async readHeader() {
|
|
30792
|
+
|
|
30793
|
+
if (this.magic !== undefined) {
|
|
30794
|
+
return this // Already read
|
|
30799
30795
|
}
|
|
30800
30796
|
|
|
30801
|
-
|
|
30802
|
-
|
|
30797
|
+
let data = await igvxhr.loadArrayBuffer(this.path, buildOptions(this.config, {range: {start: 0, size: 64000}}));
|
|
30798
|
+
let binaryParser = new BinaryParser$1(new DataView(data));
|
|
30799
|
+
this.magic = binaryParser.getInt();
|
|
30800
|
+
this.version = binaryParser.getInt();
|
|
30801
|
+
this.indexPos = binaryParser.getLong();
|
|
30802
|
+
this.indexSize = binaryParser.getInt();
|
|
30803
|
+
binaryParser.getInt();
|
|
30803
30804
|
|
|
30804
|
-
}
|
|
30805
30805
|
|
|
30806
|
-
|
|
30807
|
-
|
|
30808
|
-
|
|
30806
|
+
if (this.version >= 2) {
|
|
30807
|
+
let nWindowFunctions = binaryParser.getInt();
|
|
30808
|
+
this.windowFunctions = [];
|
|
30809
|
+
while (nWindowFunctions-- > 0) {
|
|
30810
|
+
this.windowFunctions.push(binaryParser.getString());
|
|
30811
|
+
}
|
|
30809
30812
|
}
|
|
30810
|
-
}
|
|
30811
30813
|
|
|
30812
|
-
|
|
30813
|
-
|
|
30814
|
-
if (header) {
|
|
30815
|
-
return header.type
|
|
30816
|
-
} else {
|
|
30817
|
-
return undefined // Convention for unknown or unspecified
|
|
30818
|
-
}
|
|
30819
|
-
}
|
|
30814
|
+
this.trackType = binaryParser.getString();
|
|
30815
|
+
this.trackLine = binaryParser.getString();
|
|
30820
30816
|
|
|
30821
|
-
|
|
30822
|
-
|
|
30817
|
+
let nTracks = binaryParser.getInt();
|
|
30818
|
+
this.trackNames = [];
|
|
30819
|
+
while (nTracks-- > 0) {
|
|
30820
|
+
this.trackNames.push(binaryParser.getString());
|
|
30821
|
+
}
|
|
30822
|
+
this.genomeID = binaryParser.getString();
|
|
30823
|
+
this.flags = binaryParser.getInt();
|
|
30824
|
+
this.compressed = (this.flags & GZIP_FLAG) !== 0;
|
|
30823
30825
|
|
|
30824
|
-
|
|
30825
|
-
|
|
30826
|
-
|
|
30827
|
-
|
|
30828
|
-
|
|
30829
|
-
this.config.format = header.format;
|
|
30830
|
-
}
|
|
30831
|
-
} else {
|
|
30832
|
-
this.header = {};
|
|
30833
|
-
}
|
|
30834
|
-
} else {
|
|
30835
|
-
this.header = {};
|
|
30826
|
+
// Now read index
|
|
30827
|
+
data = await igvxhr.loadArrayBuffer(this.path, buildOptions(this.config, {
|
|
30828
|
+
range: {
|
|
30829
|
+
start: this.indexPos,
|
|
30830
|
+
size: this.indexSize
|
|
30836
30831
|
}
|
|
30832
|
+
}));
|
|
30833
|
+
binaryParser = new BinaryParser$1(new DataView(data));
|
|
30834
|
+
this.datasetIndex = {};
|
|
30835
|
+
let nEntries = binaryParser.getInt();
|
|
30836
|
+
while (nEntries-- > 0) {
|
|
30837
|
+
const name = binaryParser.getString();
|
|
30838
|
+
const pos = binaryParser.getLong();
|
|
30839
|
+
const size = binaryParser.getInt();
|
|
30840
|
+
this.datasetIndex[name] = {position: pos, size: size};
|
|
30837
30841
|
}
|
|
30838
|
-
|
|
30842
|
+
|
|
30843
|
+
this.groupIndex = {};
|
|
30844
|
+
nEntries = binaryParser.getInt();
|
|
30845
|
+
while (nEntries-- > 0) {
|
|
30846
|
+
const name = binaryParser.getString();
|
|
30847
|
+
const pos = binaryParser.getLong();
|
|
30848
|
+
const size = binaryParser.getInt();
|
|
30849
|
+
this.groupIndex[name] = {position: pos, size: size};
|
|
30850
|
+
}
|
|
30851
|
+
|
|
30852
|
+
return this
|
|
30839
30853
|
}
|
|
30840
30854
|
|
|
30841
|
-
|
|
30842
|
-
* Required function for all data source objects. Fetches features for the
|
|
30843
|
-
* range requested.
|
|
30844
|
-
*
|
|
30845
|
-
* This function is quite complex due to the variety of reader types backing it, some indexed, some queryable,
|
|
30846
|
-
* some not.
|
|
30847
|
-
*
|
|
30848
|
-
* @param chr
|
|
30849
|
-
* @param start
|
|
30850
|
-
* @param end
|
|
30851
|
-
* @param bpPerPixel
|
|
30852
|
-
*/
|
|
30853
|
-
async getFeatures({chr, start, end, bpPerPixel, visibilityWindow}) {
|
|
30855
|
+
async readDataset(chr, windowFunction, zoom) {
|
|
30854
30856
|
|
|
30855
|
-
const
|
|
30857
|
+
const key = chr + "_" + windowFunction + "_" + zoom;
|
|
30856
30858
|
|
|
30857
|
-
|
|
30858
|
-
|
|
30859
|
+
if (this.datasetCache[key]) {
|
|
30860
|
+
return this.datasetCache[key]
|
|
30859
30861
|
|
|
30860
|
-
|
|
30861
|
-
|
|
30862
|
-
|
|
30863
|
-
|
|
30864
|
-
// const containsRange = this.featureCache.containsRange(new GenomicInterval(queryChr, start, end))
|
|
30865
|
-
if ((isWholeGenome && !this.wgFeatures && this.supportsWholeGenome()) ||
|
|
30866
|
-
this.config.disableCache ||
|
|
30867
|
-
!this.featureCache ||
|
|
30868
|
-
!this.featureCache.containsRange(new GenomicInterval(chr, start, end))) {
|
|
30869
|
-
await this.loadFeatures(chr, start, end, visibilityWindow);
|
|
30870
|
-
}
|
|
30862
|
+
} else {
|
|
30863
|
+
await this.readHeader();
|
|
30864
|
+
const wf = (this.version < 2) ? "" : "/" + windowFunction;
|
|
30865
|
+
const zoomString = (chr.toLowerCase() === "all" || zoom === undefined) ? "0" : zoom.toString();
|
|
30871
30866
|
|
|
30872
|
-
|
|
30873
|
-
if (
|
|
30874
|
-
|
|
30875
|
-
|
|
30876
|
-
|
|
30877
|
-
|
|
30867
|
+
let dsName;
|
|
30868
|
+
if (windowFunction === "raw") {
|
|
30869
|
+
dsName = "/" + chr + "/raw";
|
|
30870
|
+
} else {
|
|
30871
|
+
dsName = "/" + chr + "/z" + zoomString + wf;
|
|
30872
|
+
}
|
|
30873
|
+
const indexEntry = this.datasetIndex[dsName];
|
|
30874
|
+
|
|
30875
|
+
if (indexEntry === undefined) {
|
|
30876
|
+
return undefined
|
|
30877
|
+
}
|
|
30878
|
+
|
|
30879
|
+
const data = await igvxhr.loadArrayBuffer(this.path, buildOptions(this.config, {
|
|
30880
|
+
range: {
|
|
30881
|
+
start: indexEntry.position,
|
|
30882
|
+
size: indexEntry.size
|
|
30878
30883
|
}
|
|
30884
|
+
}));
|
|
30885
|
+
|
|
30886
|
+
if (!data) {
|
|
30887
|
+
return undefined
|
|
30879
30888
|
}
|
|
30880
|
-
return this.wgFeatures
|
|
30881
|
-
} else {
|
|
30882
|
-
return this.featureCache.queryFeatures(chr, start, end)
|
|
30883
|
-
}
|
|
30884
|
-
}
|
|
30885
30889
|
|
|
30886
|
-
|
|
30887
|
-
|
|
30888
|
-
|
|
30890
|
+
const binaryParser = new BinaryParser$1(new DataView(data));
|
|
30891
|
+
let nAttributes = binaryParser.getInt();
|
|
30892
|
+
const attributes = {};
|
|
30893
|
+
while (nAttributes-- > 0) {
|
|
30894
|
+
attributes[binaryParser.getString()] = binaryParser.getString();
|
|
30895
|
+
}
|
|
30896
|
+
const dataType = binaryParser.getString();
|
|
30897
|
+
const tileWidth = binaryParser.getFloat();
|
|
30898
|
+
let nTiles = binaryParser.getInt();
|
|
30899
|
+
const tiles = [];
|
|
30900
|
+
while (nTiles-- > 0) {
|
|
30901
|
+
tiles.push({position: binaryParser.getLong(), size: binaryParser.getInt()});
|
|
30902
|
+
}
|
|
30889
30903
|
|
|
30890
|
-
|
|
30891
|
-
|
|
30892
|
-
|
|
30904
|
+
const dataset = {
|
|
30905
|
+
name: dsName,
|
|
30906
|
+
attributes: attributes,
|
|
30907
|
+
dataType: dataType,
|
|
30908
|
+
tileWidth: tileWidth,
|
|
30909
|
+
tiles: tiles
|
|
30910
|
+
};
|
|
30893
30911
|
|
|
30894
|
-
|
|
30895
|
-
|
|
30896
|
-
if (this.queryable || !this.featureCache) { // queryable sources don't support all features
|
|
30897
|
-
return []
|
|
30898
|
-
} else {
|
|
30899
|
-
return this.featureCache.getAllFeatures()
|
|
30912
|
+
this.datasetCache[key] = dataset;
|
|
30913
|
+
return dataset
|
|
30900
30914
|
}
|
|
30901
30915
|
}
|
|
30902
30916
|
|
|
30917
|
+
async readRootGroup() {
|
|
30903
30918
|
|
|
30904
|
-
|
|
30919
|
+
const genome = this.genome;
|
|
30920
|
+
const rootGroup = this.groupCache["/"];
|
|
30921
|
+
if (rootGroup) {
|
|
30922
|
+
return rootGroup
|
|
30923
|
+
} else {
|
|
30905
30924
|
|
|
30906
|
-
|
|
30925
|
+
const group = await this.readGroup("/");
|
|
30926
|
+
const names = group["chromosomes"];
|
|
30927
|
+
const maxZoomString = group["maxZoom"];
|
|
30907
30928
|
|
|
30908
|
-
|
|
30909
|
-
|
|
30910
|
-
|
|
30929
|
+
// Now parse out interesting attributes.
|
|
30930
|
+
if (maxZoomString) {
|
|
30931
|
+
this.maxZoom = Number(maxZoomString);
|
|
30932
|
+
}
|
|
30911
30933
|
|
|
30912
|
-
|
|
30913
|
-
|
|
30914
|
-
|
|
30915
|
-
|
|
30916
|
-
}
|
|
30917
|
-
if (this.chrAliasManager) {
|
|
30918
|
-
queryChr = await this.chrAliasManager.getAliasName(chr);
|
|
30919
|
-
}
|
|
30934
|
+
const totalCountString = group["totalCount"];
|
|
30935
|
+
if (totalCountString) {
|
|
30936
|
+
group.totalCount = Number(totalCountString);
|
|
30937
|
+
}
|
|
30920
30938
|
|
|
30921
|
-
|
|
30922
|
-
|
|
30923
|
-
|
|
30924
|
-
|
|
30925
|
-
|
|
30926
|
-
|
|
30927
|
-
|
|
30928
|
-
intervalEnd = Math.max(chromosome ? chromosome.bpLength : Number.MAX_SAFE_INTEGER, end);
|
|
30929
|
-
} else if (visibilityWindow > (end - start) && this.config.expandQuery !== false) {
|
|
30930
|
-
let expansionWindow = Math.min(4.1 * (end - start), visibilityWindow);
|
|
30931
|
-
if(this.config.minQuerySize && expansionWindow < this.config.minQuerySize) {
|
|
30932
|
-
expansionWindow = this.config.minQuerySize;
|
|
30939
|
+
// Chromosome names
|
|
30940
|
+
const chrAliasTable = {};
|
|
30941
|
+
if (names) {
|
|
30942
|
+
names.split(",").forEach(function (chr) {
|
|
30943
|
+
const canonicalName = genome.getChromosomeName(chr);
|
|
30944
|
+
chrAliasTable[canonicalName] = chr;
|
|
30945
|
+
});
|
|
30933
30946
|
}
|
|
30934
|
-
|
|
30935
|
-
intervalEnd = intervalStart + expansionWindow;
|
|
30936
|
-
}
|
|
30947
|
+
this.chrAliasTable = chrAliasTable;
|
|
30937
30948
|
|
|
30938
|
-
|
|
30939
|
-
|
|
30940
|
-
this.queryable = reader.indexed;
|
|
30949
|
+
this.groupCache["/"] = group;
|
|
30950
|
+
return group
|
|
30941
30951
|
}
|
|
30952
|
+
}
|
|
30942
30953
|
|
|
30943
|
-
|
|
30944
|
-
new GenomicInterval(chr, intervalStart, intervalEnd) :
|
|
30945
|
-
undefined;
|
|
30954
|
+
async readGroup(name) {
|
|
30946
30955
|
|
|
30947
|
-
|
|
30956
|
+
const group = this.groupCache[name];
|
|
30957
|
+
if (group) {
|
|
30958
|
+
return group
|
|
30959
|
+
} else {
|
|
30948
30960
|
|
|
30949
|
-
|
|
30950
|
-
|
|
30951
|
-
|
|
30952
|
-
|
|
30961
|
+
await this.readHeader();
|
|
30962
|
+
const indexEntry = this.groupIndex[name];
|
|
30963
|
+
if (indexEntry === undefined) {
|
|
30964
|
+
return undefined
|
|
30953
30965
|
}
|
|
30954
30966
|
|
|
30955
|
-
|
|
30956
|
-
|
|
30967
|
+
const data = await igvxhr.loadArrayBuffer(this.path, buildOptions(this.config, {
|
|
30968
|
+
range: {
|
|
30969
|
+
start: indexEntry.position,
|
|
30970
|
+
size: indexEntry.size
|
|
30971
|
+
}
|
|
30972
|
+
}));
|
|
30957
30973
|
|
|
30958
|
-
|
|
30959
|
-
|
|
30960
|
-
this.addFeaturesToDB(features, this.config);
|
|
30974
|
+
if (!data) {
|
|
30975
|
+
return undefined
|
|
30961
30976
|
}
|
|
30962
|
-
|
|
30963
|
-
|
|
30977
|
+
|
|
30978
|
+
const binaryParser = new BinaryParser$1(new DataView(data));
|
|
30979
|
+
const group = {name: name};
|
|
30980
|
+
let nAttributes = binaryParser.getInt();
|
|
30981
|
+
while (nAttributes-- > 0) {
|
|
30982
|
+
const key = binaryParser.getString();
|
|
30983
|
+
const value = binaryParser.getString();
|
|
30984
|
+
group[key] = value;
|
|
30985
|
+
}
|
|
30986
|
+
this.groupCache[name] = group;
|
|
30987
|
+
return group
|
|
30964
30988
|
}
|
|
30965
30989
|
}
|
|
30966
30990
|
|
|
30967
|
-
|
|
30968
|
-
|
|
30969
|
-
|
|
30991
|
+
async readTiles(tileIndeces, nTracks) {
|
|
30992
|
+
|
|
30993
|
+
tileIndeces.sort(function (a, b) {
|
|
30994
|
+
return a.position - b.position
|
|
30995
|
+
});
|
|
30996
|
+
|
|
30997
|
+
tileIndeces = tileIndeces.filter(function (idx) {
|
|
30998
|
+
return idx.size > 0
|
|
30999
|
+
});
|
|
31000
|
+
|
|
31001
|
+
if (tileIndeces.length === 0) {
|
|
31002
|
+
return []
|
|
30970
31003
|
}
|
|
30971
|
-
|
|
30972
|
-
|
|
30973
|
-
|
|
30974
|
-
|
|
30975
|
-
|
|
30976
|
-
|
|
30977
|
-
|
|
30978
|
-
|
|
30979
|
-
|
|
30980
|
-
// If feature is already present keep largest one
|
|
30981
|
-
if (this.featureMap.has(key)) {
|
|
30982
|
-
const f2 = this.featureMap.get(key);
|
|
30983
|
-
if (feature.end - feature.start < f2.end - f2.start) {
|
|
30984
|
-
continue
|
|
30985
|
-
}
|
|
30986
|
-
}
|
|
30987
|
-
this.featureMap.set(key, feature);
|
|
31004
|
+
|
|
31005
|
+
const tiles = [];
|
|
31006
|
+
|
|
31007
|
+
for (let indexEntry of tileIndeces) {
|
|
31008
|
+
|
|
31009
|
+
const data = await igvxhr.loadArrayBuffer(this.path, buildOptions(this.config, {
|
|
31010
|
+
range: {
|
|
31011
|
+
start: indexEntry.position,
|
|
31012
|
+
size: indexEntry.size
|
|
30988
31013
|
}
|
|
31014
|
+
}));
|
|
31015
|
+
|
|
31016
|
+
let tileData;
|
|
31017
|
+
try {
|
|
31018
|
+
tileData = this.compressed ? inflate_1$3(data).buffer : data;
|
|
31019
|
+
} catch (e) {
|
|
31020
|
+
console.error(e);
|
|
31021
|
+
continue
|
|
31022
|
+
}
|
|
31023
|
+
|
|
31024
|
+
const binaryParser = new BinaryParser$1(new DataView(tileData));
|
|
31025
|
+
const type = binaryParser.getString();
|
|
31026
|
+
let tile;
|
|
31027
|
+
switch (type) {
|
|
31028
|
+
case "fixedStep":
|
|
31029
|
+
tile = createFixedStep(binaryParser, nTracks);
|
|
31030
|
+
break
|
|
31031
|
+
case "variableStep":
|
|
31032
|
+
tile = createVariableStep(binaryParser, nTracks);
|
|
31033
|
+
break
|
|
31034
|
+
case "bed":
|
|
31035
|
+
case "bedWithName":
|
|
31036
|
+
tile = createBed(binaryParser, nTracks, type);
|
|
31037
|
+
break
|
|
31038
|
+
default:
|
|
31039
|
+
throw "Unknown tile type: " + type
|
|
30989
31040
|
}
|
|
31041
|
+
tiles.push(tile);
|
|
30990
31042
|
}
|
|
31043
|
+
return tiles
|
|
30991
31044
|
}
|
|
30992
31045
|
|
|
30993
|
-
|
|
30994
|
-
|
|
30995
|
-
|
|
31046
|
+
async readTile(indexEntry, nTracks) {
|
|
31047
|
+
|
|
31048
|
+
let data = await igvxhr.loadArrayBuffer(this.path, buildOptions(this.config, {
|
|
31049
|
+
range: {
|
|
31050
|
+
start: indexEntry.position,
|
|
31051
|
+
size: indexEntry.size
|
|
31052
|
+
}
|
|
31053
|
+
}));
|
|
31054
|
+
|
|
31055
|
+
if (this.compressed) {
|
|
31056
|
+
const plain = inflate_1$3(data);
|
|
31057
|
+
data = plain.buffer;
|
|
30996
31058
|
}
|
|
30997
31059
|
|
|
31060
|
+
const binaryParser = new BinaryParser$1(new DataView(data));
|
|
31061
|
+
const type = binaryParser.getString();
|
|
31062
|
+
switch (type) {
|
|
31063
|
+
case "fixedStep":
|
|
31064
|
+
return createFixedStep(binaryParser, nTracks)
|
|
31065
|
+
case "variableStep":
|
|
31066
|
+
return createVariableStep(binaryParser, nTracks)
|
|
31067
|
+
case "bed":
|
|
31068
|
+
case "bedWithName":
|
|
31069
|
+
return createBed(binaryParser, nTracks, type)
|
|
31070
|
+
default:
|
|
31071
|
+
throw "Unknown tile type: " + type
|
|
31072
|
+
}
|
|
30998
31073
|
}
|
|
31074
|
+
|
|
30999
31075
|
}
|
|
31000
31076
|
|
|
31001
|
-
|
|
31002
|
-
|
|
31003
|
-
|
|
31004
|
-
|
|
31005
|
-
*
|
|
31006
|
-
* Both maps are needed by IGV
|
|
31007
|
-
*
|
|
31008
|
-
* The chromosome tree is a B+ index, but is located continguously in memory in the header section of the file. This
|
|
31009
|
-
* makes it feasible to parse the whole tree with data from a single fetch. In the end the tree is discarded
|
|
31010
|
-
* leaving only the mapps.
|
|
31011
|
-
*/
|
|
31012
|
-
class ChromTree {
|
|
31077
|
+
function createFixedStep(binaryParser, nTracks) {
|
|
31078
|
+
const nPositions = binaryParser.getInt();
|
|
31079
|
+
const start = binaryParser.getInt();
|
|
31080
|
+
const span = binaryParser.getFloat();
|
|
31013
31081
|
|
|
31014
|
-
|
|
31015
|
-
|
|
31016
|
-
|
|
31017
|
-
|
|
31018
|
-
|
|
31082
|
+
const data = [];
|
|
31083
|
+
let nt = nTracks;
|
|
31084
|
+
while (nt-- > 0) {
|
|
31085
|
+
let np = nPositions;
|
|
31086
|
+
const dtrack = [];
|
|
31087
|
+
while (np-- > 0) {
|
|
31088
|
+
dtrack.push(binaryParser.getFloat());
|
|
31089
|
+
}
|
|
31090
|
+
data.push(dtrack);
|
|
31019
31091
|
}
|
|
31020
31092
|
|
|
31021
|
-
|
|
31022
|
-
|
|
31023
|
-
|
|
31024
|
-
|
|
31025
|
-
|
|
31026
|
-
|
|
31093
|
+
return {
|
|
31094
|
+
type: "fixedStep",
|
|
31095
|
+
start: start,
|
|
31096
|
+
span: span,
|
|
31097
|
+
data: data,
|
|
31098
|
+
nTracks: nTracks,
|
|
31099
|
+
nPositions: nPositions
|
|
31100
|
+
}
|
|
31101
|
+
}
|
|
31102
|
+
|
|
31103
|
+
function createVariableStep(binaryParser, nTracks) {
|
|
31104
|
+
|
|
31105
|
+
const tileStart = binaryParser.getInt();
|
|
31106
|
+
const span = binaryParser.getFloat();
|
|
31107
|
+
const nPositions = binaryParser.getInt();
|
|
31108
|
+
const start = [];
|
|
31109
|
+
|
|
31110
|
+
let np = nPositions;
|
|
31111
|
+
while (np-- > 0) {
|
|
31112
|
+
start.push(binaryParser.getInt());
|
|
31113
|
+
}
|
|
31114
|
+
binaryParser.getInt(); // # of samples, ignored but should === nTracks
|
|
31115
|
+
|
|
31116
|
+
const data = [];
|
|
31117
|
+
let nt = nTracks;
|
|
31118
|
+
while (nt-- > 0) {
|
|
31119
|
+
np = nPositions;
|
|
31120
|
+
const dtrack = [];
|
|
31121
|
+
while (np-- > 0) {
|
|
31122
|
+
dtrack.push(binaryParser.getFloat());
|
|
31123
|
+
}
|
|
31124
|
+
data.push(dtrack);
|
|
31125
|
+
}
|
|
31126
|
+
|
|
31127
|
+
return {
|
|
31128
|
+
type: "variableStep",
|
|
31129
|
+
tileStart: tileStart,
|
|
31130
|
+
span: span,
|
|
31131
|
+
start: start,
|
|
31132
|
+
data: data,
|
|
31133
|
+
nTracks: nTracks,
|
|
31134
|
+
nPositions: nPositions
|
|
31135
|
+
}
|
|
31136
|
+
}
|
|
31137
|
+
|
|
31138
|
+
function createBed(binaryParser, nTracks, type) {
|
|
31139
|
+
|
|
31140
|
+
const nPositions = binaryParser.getInt();
|
|
31141
|
+
|
|
31142
|
+
let n = nPositions;
|
|
31143
|
+
const start = [];
|
|
31144
|
+
while (n-- > 0) {
|
|
31145
|
+
start.push(binaryParser.getInt());
|
|
31146
|
+
}
|
|
31147
|
+
|
|
31148
|
+
n = nPositions;
|
|
31149
|
+
const end = [];
|
|
31150
|
+
while (n-- > 0) {
|
|
31151
|
+
end.push(binaryParser.getInt());
|
|
31152
|
+
}
|
|
31153
|
+
|
|
31154
|
+
binaryParser.getInt(); // # of samples, ignored but should === nTracks
|
|
31155
|
+
const data = [];
|
|
31156
|
+
let nt = nTracks;
|
|
31157
|
+
while (nt-- > 0) {
|
|
31158
|
+
let np = nPositions;
|
|
31159
|
+
const dtrack = [];
|
|
31160
|
+
while (np-- > 0) {
|
|
31161
|
+
dtrack.push(binaryParser.getFloat());
|
|
31162
|
+
}
|
|
31163
|
+
data.push(dtrack);
|
|
31164
|
+
}
|
|
31165
|
+
|
|
31166
|
+
if (type === "bedWithName") {
|
|
31167
|
+
n = nPositions;
|
|
31168
|
+
const name = [];
|
|
31169
|
+
while (n-- > 0) {
|
|
31170
|
+
name.push(binaryParser.getString());
|
|
31171
|
+
}
|
|
31172
|
+
}
|
|
31173
|
+
|
|
31174
|
+
return {
|
|
31175
|
+
type: type,
|
|
31176
|
+
start: start,
|
|
31177
|
+
end: end,
|
|
31178
|
+
data: data,
|
|
31179
|
+
nTracks: nTracks,
|
|
31180
|
+
nPositions: nPositions
|
|
31181
|
+
}
|
|
31182
|
+
}
|
|
31183
|
+
|
|
31184
|
+
/*
|
|
31185
|
+
* The MIT License (MIT)
|
|
31186
|
+
*
|
|
31187
|
+
* Copyright (c) 2016 University of California San Diego
|
|
31188
|
+
* Author: Jim Robinson
|
|
31189
|
+
*
|
|
31190
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
31191
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
31192
|
+
* in the Software without restriction, including without limitation the rights
|
|
31193
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
31194
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
31195
|
+
* furnished to do so, subject to the following conditions:
|
|
31196
|
+
*
|
|
31197
|
+
* The above copyright notice and this permission notice shall be included in
|
|
31198
|
+
* all copies or substantial portions of the Software.
|
|
31199
|
+
*
|
|
31200
|
+
*
|
|
31201
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
31202
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
31203
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
31204
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
31205
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
31206
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
31207
|
+
* THE SOFTWARE.
|
|
31208
|
+
*/
|
|
31209
|
+
|
|
31210
|
+
class TDFSource extends BaseFeatureSource {
|
|
31211
|
+
|
|
31212
|
+
#wgValues = {}
|
|
31213
|
+
searchable = false
|
|
31214
|
+
|
|
31215
|
+
|
|
31216
|
+
constructor(config, genome) {
|
|
31217
|
+
super(genome);
|
|
31218
|
+
this.genome = genome;
|
|
31219
|
+
this.reader = new TDFReader(config, genome);
|
|
31220
|
+
this.queryable = true;
|
|
31221
|
+
}
|
|
31222
|
+
|
|
31223
|
+
async getFeatures({chr, start, end, bpPerPixel, windowFunction = "mean"}) {
|
|
31224
|
+
|
|
31225
|
+
if (chr.toLowerCase() === "all") {
|
|
31226
|
+
return this.getWGValues(windowFunction, bpPerPixel)
|
|
31227
|
+
} else {
|
|
31228
|
+
return this._getFeatures(chr, start, end, bpPerPixel, windowFunction)
|
|
31229
|
+
}
|
|
31230
|
+
}
|
|
31231
|
+
async _getFeatures(chr, start, end, bpPerPixel, windowFunction) {
|
|
31232
|
+
const genomicInterval = new GenomicInterval(chr, start, end);
|
|
31233
|
+
const genome = this.genome;
|
|
31234
|
+
|
|
31235
|
+
|
|
31236
|
+
if (!this.rootGroup) {
|
|
31237
|
+
this.rootGroup = await this.reader.readRootGroup();
|
|
31238
|
+
if (!this.normalizationFactor) {
|
|
31239
|
+
const totalCount = this.rootGroup.totalCount;
|
|
31240
|
+
if (totalCount) {
|
|
31241
|
+
this.normalizationFactor = 1.0e6 / totalCount;
|
|
31242
|
+
}
|
|
31243
|
+
}
|
|
31244
|
+
}
|
|
31245
|
+
|
|
31246
|
+
genomicInterval.bpPerPixel = bpPerPixel;
|
|
31247
|
+
const zoom = zoomLevelForScale$1(chr, bpPerPixel, genome);
|
|
31248
|
+
let queryChr = this.reader.chrAliasTable[chr];
|
|
31249
|
+
let maxZoom = this.reader.maxZoom;
|
|
31250
|
+
if (queryChr === undefined) queryChr = chr;
|
|
31251
|
+
if (maxZoom === undefined) maxZoom = -1;
|
|
31252
|
+
|
|
31253
|
+
const wf = zoom > maxZoom ? "raw" : windowFunction;
|
|
31254
|
+
const dataset = await this.reader.readDataset(queryChr, wf, zoom);
|
|
31255
|
+
if (dataset == null) {
|
|
31256
|
+
return []
|
|
31257
|
+
}
|
|
31258
|
+
|
|
31259
|
+
const tileWidth = dataset.tileWidth;
|
|
31260
|
+
const startTile = Math.floor(start / tileWidth);
|
|
31261
|
+
const endTile = Math.floor(end / tileWidth);
|
|
31262
|
+
const NTRACKS = 1; // TODO read this
|
|
31263
|
+
const tiles = await this.reader.readTiles(dataset.tiles.slice(startTile, endTile + 1), NTRACKS);
|
|
31264
|
+
const features = [];
|
|
31265
|
+
for (let tile of tiles) {
|
|
31266
|
+
switch (tile.type) {
|
|
31267
|
+
case "bed":
|
|
31268
|
+
decodeBedTile(tile, chr, start, end, bpPerPixel, features);
|
|
31269
|
+
break
|
|
31270
|
+
case "variableStep":
|
|
31271
|
+
decodeVaryTile(tile, chr, start, end, bpPerPixel, features);
|
|
31272
|
+
break
|
|
31273
|
+
case "fixedStep":
|
|
31274
|
+
decodeFixedTile(tile, chr, start, end, bpPerPixel, features);
|
|
31275
|
+
break
|
|
31276
|
+
default:
|
|
31277
|
+
throw ("Unknown tile type: " + tile.type)
|
|
31278
|
+
}
|
|
31279
|
+
}
|
|
31280
|
+
features.sort(function (a, b) {
|
|
31281
|
+
return a.start - b.start
|
|
31282
|
+
});
|
|
31283
|
+
|
|
31284
|
+
return features
|
|
31285
|
+
}
|
|
31286
|
+
|
|
31287
|
+
get supportsWholeGenome() {
|
|
31288
|
+
return true
|
|
31289
|
+
}
|
|
31290
|
+
|
|
31291
|
+
get windowFunctions() {
|
|
31292
|
+
return this.reader.windowFunctions
|
|
31293
|
+
}
|
|
31294
|
+
|
|
31295
|
+
async getWGValues(windowFunction, bpPerPixel) {
|
|
31296
|
+
|
|
31297
|
+
const cached = this.#wgValues[windowFunction];
|
|
31298
|
+
if (cached && cached.bpPerPixel > 0.8 * bpPerPixel && cached.bpPerPixel < 1.2 * bpPerPixel) {
|
|
31299
|
+
return cached.values
|
|
31300
|
+
} else {
|
|
31301
|
+
const wgFeatures = [];
|
|
31302
|
+
const genome = this.genome;
|
|
31303
|
+
const chrNames = this.genome.wgChromosomeNames;
|
|
31304
|
+
if (chrNames) {
|
|
31305
|
+
for (let c of genome.wgChromosomeNames) {
|
|
31306
|
+
const len = genome.getChromosome(c).bpLength;
|
|
31307
|
+
bpPerPixel = len / 1000;
|
|
31308
|
+
const chrFeatures = await this._getFeatures(c, 0, len, bpPerPixel, windowFunction);
|
|
31309
|
+
if (chrFeatures) {
|
|
31310
|
+
for (let f of chrFeatures) {
|
|
31311
|
+
const wg = Object.assign({}, f);
|
|
31312
|
+
wg.chr = "all";
|
|
31313
|
+
wg.start = genome.getGenomeCoordinate(f.chr, f.start);
|
|
31314
|
+
wg.end = genome.getGenomeCoordinate(f.chr, f.end);
|
|
31315
|
+
wg._f = f;
|
|
31316
|
+
wgFeatures.push(wg);
|
|
31317
|
+
}
|
|
31318
|
+
}
|
|
31319
|
+
}
|
|
31320
|
+
}
|
|
31321
|
+
this.#wgValues[windowFunction] = {values: wgFeatures, bpPerPixel};
|
|
31322
|
+
return wgFeatures
|
|
31323
|
+
}
|
|
31324
|
+
}
|
|
31325
|
+
|
|
31326
|
+
}
|
|
31327
|
+
|
|
31328
|
+
function decodeBedTile(tile, chr, bpStart, bpEnd, bpPerPixel, features) {
|
|
31329
|
+
|
|
31330
|
+
const nPositions = tile.nPositions;
|
|
31331
|
+
const starts = tile.start;
|
|
31332
|
+
const ends = tile.end;
|
|
31333
|
+
const data = tile.data[0]; // Single track for now
|
|
31334
|
+
for (let i = 0; i < nPositions; i++) {
|
|
31335
|
+
const s = starts[i];
|
|
31336
|
+
const e = ends[i];
|
|
31337
|
+
if (e < bpStart) continue
|
|
31338
|
+
if (s > bpEnd) break
|
|
31339
|
+
features.push({
|
|
31340
|
+
chr: chr,
|
|
31341
|
+
start: s,
|
|
31342
|
+
end: e,
|
|
31343
|
+
value: data[i]
|
|
31344
|
+
});
|
|
31345
|
+
}
|
|
31346
|
+
}
|
|
31347
|
+
|
|
31348
|
+
function decodeVaryTile(tile, chr, bpStart, bpEnd, bpPerPixel, features) {
|
|
31349
|
+
|
|
31350
|
+
const nPositions = tile.nPositions;
|
|
31351
|
+
const starts = tile.start;
|
|
31352
|
+
const span = tile.span;
|
|
31353
|
+
const data = tile.data[0]; // Single track for now
|
|
31354
|
+
for (let i = 0; i < nPositions; i++) {
|
|
31355
|
+
const s = starts[i];
|
|
31356
|
+
const e = s + span;
|
|
31357
|
+
if (e < bpStart) continue
|
|
31358
|
+
if (s > bpEnd) break
|
|
31359
|
+
features.push({
|
|
31360
|
+
chr: chr,
|
|
31361
|
+
start: s,
|
|
31362
|
+
end: e,
|
|
31363
|
+
value: data[i]
|
|
31364
|
+
});
|
|
31365
|
+
}
|
|
31366
|
+
}
|
|
31367
|
+
|
|
31368
|
+
function decodeFixedTile(tile, chr, bpStart, bpEnd, bpPerPixel, features) {
|
|
31369
|
+
|
|
31370
|
+
const nPositions = tile.nPositions;
|
|
31371
|
+
let s = tile.start;
|
|
31372
|
+
const span = tile.span;
|
|
31373
|
+
const data = tile.data[0]; // Single track for now
|
|
31374
|
+
|
|
31375
|
+
for (let i = 0; i < nPositions; i++) {
|
|
31376
|
+
const e = s + span;
|
|
31377
|
+
if (s > bpEnd) break
|
|
31378
|
+
if (e >= bpStart) {
|
|
31379
|
+
if (!Number.isNaN(data[i])) {
|
|
31380
|
+
features.push({
|
|
31381
|
+
chr: chr,
|
|
31382
|
+
start: s,
|
|
31383
|
+
end: e,
|
|
31384
|
+
value: data[i]
|
|
31385
|
+
});
|
|
31386
|
+
}
|
|
31387
|
+
}
|
|
31388
|
+
s = e;
|
|
31389
|
+
}
|
|
31390
|
+
}
|
|
31391
|
+
|
|
31392
|
+
|
|
31393
|
+
var log2 = Math.log(2);
|
|
31394
|
+
|
|
31395
|
+
function zoomLevelForScale$1(chr, bpPerPixel, genome) {
|
|
31396
|
+
|
|
31397
|
+
// Convert bpPerPixel to IGV "zoom" level. This is a bit convoluted, TDF is computed zoom levels assuming
|
|
31398
|
+
// display in a 700 pixel window. The fully zoomed out view of a chromosome is zoom level "0".
|
|
31399
|
+
// Zoom level 1 is magnified 2X, and so forth
|
|
31400
|
+
|
|
31401
|
+
var chrSize = genome.getChromosome(chr).bpLength;
|
|
31402
|
+
|
|
31403
|
+
return Math.ceil(Math.log(Math.max(0, (chrSize / (bpPerPixel * 700)))) / log2)
|
|
31404
|
+
}
|
|
31405
|
+
|
|
31406
|
+
/**
|
|
31407
|
+
* A ChromTree parses a UCSC bigbed/bigwig "chromosomeTree" section of the header to produce 2 maps,
|
|
31408
|
+
* (1) ID -> chromosome names, and its
|
|
31409
|
+
* (2) chromsome name -> ID
|
|
31410
|
+
*
|
|
31411
|
+
* Both maps are needed by IGV
|
|
31412
|
+
*
|
|
31413
|
+
* The chromosome tree is a B+ index, but is located continguously in memory in the header section of the file. This
|
|
31414
|
+
* makes it feasible to parse the whole tree with data from a single fetch. In the end the tree is discarded
|
|
31415
|
+
* leaving only the mapps.
|
|
31416
|
+
*/
|
|
31417
|
+
class ChromTree {
|
|
31418
|
+
|
|
31419
|
+
constructor(header, nameToID, valueToKey, sumLengths) {
|
|
31420
|
+
this.header = header;
|
|
31421
|
+
this.nameToId = nameToID;
|
|
31422
|
+
this.idToName = valueToKey;
|
|
31423
|
+
this.sumLengths = sumLengths;
|
|
31424
|
+
}
|
|
31425
|
+
|
|
31426
|
+
static parseTree(binaryParser, startOffset, genome = false) {
|
|
31427
|
+
{
|
|
31428
|
+
const magic = binaryParser.getInt();
|
|
31429
|
+
const blockSize = binaryParser.getInt();
|
|
31430
|
+
const keySize = binaryParser.getInt();
|
|
31431
|
+
const valSize = binaryParser.getInt();
|
|
31027
31432
|
const itemCount = binaryParser.getLong();
|
|
31028
31433
|
const reserved = binaryParser.getLong();
|
|
31029
31434
|
|
|
@@ -31569,7 +31974,7 @@ class BWReader {
|
|
|
31569
31974
|
if (this.type === "bigwig") {
|
|
31570
31975
|
// Select a biwig "zoom level" appropriate for the current resolution.
|
|
31571
31976
|
const zoomLevelHeaders = await this.getZoomHeaders();
|
|
31572
|
-
let zoomLevelHeader = bpPerPixel ? zoomLevelForScale
|
|
31977
|
+
let zoomLevelHeader = bpPerPixel ? zoomLevelForScale(bpPerPixel, zoomLevelHeaders) : undefined;
|
|
31573
31978
|
if (zoomLevelHeader) {
|
|
31574
31979
|
treeOffset = zoomLevelHeader.indexOffset;
|
|
31575
31980
|
decodeFunction = decodeZoomData;
|
|
@@ -32025,7 +32430,7 @@ function computeStats() {
|
|
|
32025
32430
|
}
|
|
32026
32431
|
}
|
|
32027
32432
|
|
|
32028
|
-
function zoomLevelForScale
|
|
32433
|
+
function zoomLevelForScale(bpPerPixel, zoomLevelHeaders) {
|
|
32029
32434
|
let level;
|
|
32030
32435
|
for (let i = 0; i < zoomLevelHeaders.length; i++) {
|
|
32031
32436
|
const zl = zoomLevelHeaders[i];
|
|
@@ -32213,7 +32618,7 @@ class DataBuffer {
|
|
|
32213
32618
|
class BWSource extends BaseFeatureSource {
|
|
32214
32619
|
|
|
32215
32620
|
queryable = true
|
|
32216
|
-
wgValues = {}
|
|
32621
|
+
#wgValues = {}
|
|
32217
32622
|
windowFunctions = ["mean", "min", "max"]
|
|
32218
32623
|
|
|
32219
32624
|
constructor(config, genome) {
|
|
@@ -32225,12 +32630,15 @@ class BWSource extends BaseFeatureSource {
|
|
|
32225
32630
|
|
|
32226
32631
|
async getFeatures({chr, start, end, bpPerPixel, windowFunction}) {
|
|
32227
32632
|
|
|
32228
|
-
await
|
|
32633
|
+
await this.reader.loadHeader();
|
|
32229
32634
|
const isBigWig = this.reader.type === "bigwig";
|
|
32230
32635
|
|
|
32231
|
-
|
|
32232
|
-
|
|
32233
|
-
await this.
|
|
32636
|
+
let features;
|
|
32637
|
+
if ("all" === chr.toLowerCase()) {
|
|
32638
|
+
features = isBigWig ? await this.getWGValues(windowFunction, bpPerPixel) : [];
|
|
32639
|
+
} else {
|
|
32640
|
+
features = await this.reader.readFeatures(chr, start, chr, end, bpPerPixel, windowFunction);
|
|
32641
|
+
}
|
|
32234
32642
|
|
|
32235
32643
|
if (!isBigWig) {
|
|
32236
32644
|
pack(features);
|
|
@@ -32246,26 +32654,25 @@ class BWSource extends BaseFeatureSource {
|
|
|
32246
32654
|
if (this.reader.type === "bigwig") {
|
|
32247
32655
|
return -1
|
|
32248
32656
|
} else {
|
|
32249
|
-
return this.reader.featureDensity ?
|
|
32657
|
+
return this.reader.featureDensity ? Math.floor(10000 / this.reader.featureDensity) : -1
|
|
32250
32658
|
}
|
|
32251
32659
|
|
|
32252
32660
|
}
|
|
32253
32661
|
|
|
32254
|
-
async getWGValues(windowFunction) {
|
|
32662
|
+
async getWGValues(windowFunction, bpPerPixel) {
|
|
32255
32663
|
|
|
32256
|
-
const numberOfBins = 1000; // This doesn't need to be precise
|
|
32257
32664
|
const genome = this.genome;
|
|
32258
|
-
|
|
32259
|
-
if (
|
|
32260
|
-
return
|
|
32665
|
+
const cached = this.#wgValues[windowFunction];
|
|
32666
|
+
if (cached && cached.bpPerPixel > 0.8 * bpPerPixel && cached.bpPerPixel < 1.2 * bpPerPixel) {
|
|
32667
|
+
return cached.values
|
|
32261
32668
|
} else {
|
|
32262
32669
|
|
|
32263
|
-
const bpPerPixel = genome.getGenomeLength() / numberOfBins;
|
|
32264
32670
|
const features = await this.reader.readWGFeatures(bpPerPixel, windowFunction);
|
|
32265
32671
|
let wgValues = [];
|
|
32266
32672
|
for (let f of features) {
|
|
32267
32673
|
const chr = f.chr;
|
|
32268
32674
|
const offset = genome.getCumulativeOffset(chr);
|
|
32675
|
+
if (undefined === offset) continue
|
|
32269
32676
|
const wgFeature = Object.assign({}, f);
|
|
32270
32677
|
wgFeature.chr = "all";
|
|
32271
32678
|
wgFeature.start = offset + f.start;
|
|
@@ -32274,7 +32681,7 @@ class BWSource extends BaseFeatureSource {
|
|
|
32274
32681
|
wgValues.push(wgFeature);
|
|
32275
32682
|
}
|
|
32276
32683
|
wgValues.sort((a, b) => a.start - b.start);
|
|
32277
|
-
this
|
|
32684
|
+
this.#wgValues[windowFunction] = {values: wgValues, bpPerPixel};
|
|
32278
32685
|
return wgValues
|
|
32279
32686
|
}
|
|
32280
32687
|
}
|
|
@@ -32296,658 +32703,780 @@ class BWSource extends BaseFeatureSource {
|
|
|
32296
32703
|
}
|
|
32297
32704
|
}
|
|
32298
32705
|
|
|
32299
|
-
|
|
32300
|
-
|
|
32301
|
-
|
|
32302
|
-
|
|
32303
|
-
* Author: Jim Robinson
|
|
32304
|
-
*
|
|
32305
|
-
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
32306
|
-
* of this software and associated documentation files (the "Software"), to deal
|
|
32307
|
-
* in the Software without restriction, including without limitation the rights
|
|
32308
|
-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
32309
|
-
* copies of the Software, and to permit persons to whom the Software is
|
|
32310
|
-
* furnished to do so, subject to the following conditions:
|
|
32311
|
-
*
|
|
32312
|
-
* The above copyright notice and this permission notice shall be included in
|
|
32313
|
-
* all copies or substantial portions of the Software.
|
|
32314
|
-
*
|
|
32315
|
-
*
|
|
32316
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
32317
|
-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
32318
|
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
32319
|
-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
32320
|
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
32321
|
-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
32322
|
-
* THE SOFTWARE.
|
|
32323
|
-
*/
|
|
32324
|
-
|
|
32325
|
-
const GZIP_FLAG = 0x1;
|
|
32706
|
+
const shim = .01;
|
|
32707
|
+
const colorStripWidth = 4;
|
|
32708
|
+
const axesXOffset = colorStripWidth + 1;
|
|
32709
|
+
function paintAxis(ctx, width, height, colorOrUndefined) {
|
|
32326
32710
|
|
|
32327
|
-
|
|
32711
|
+
if (undefined === this.dataRange || undefined === this.dataRange.max || undefined === this.dataRange.min) {
|
|
32712
|
+
return
|
|
32713
|
+
}
|
|
32328
32714
|
|
|
32329
|
-
|
|
32330
|
-
|
|
32331
|
-
|
|
32332
|
-
this.path = config.url;
|
|
32333
|
-
this.groupCache = {};
|
|
32334
|
-
this.datasetCache = {};
|
|
32715
|
+
IGVGraphics.fillRect(ctx, 0, 0, width, height, { fillStyle: 'white' });
|
|
32716
|
+
if (colorOrUndefined) {
|
|
32717
|
+
IGVGraphics.fillRect(ctx, width - colorStripWidth - 2, 0, colorStripWidth, height, { fillStyle: colorOrUndefined });
|
|
32335
32718
|
}
|
|
32336
32719
|
|
|
32720
|
+
const flipAxis = (undefined === this.flipAxis) ? false : this.flipAxis;
|
|
32337
32721
|
|
|
32338
|
-
|
|
32722
|
+
const xTickStart = 0.95 * width - 8 - axesXOffset;
|
|
32723
|
+
const xTickEnd = 0.95 * width - axesXOffset;
|
|
32339
32724
|
|
|
32340
|
-
|
|
32341
|
-
|
|
32342
|
-
|
|
32725
|
+
const properties =
|
|
32726
|
+
{
|
|
32727
|
+
font: 'normal 10px Arial',
|
|
32728
|
+
textAlign: 'right',
|
|
32729
|
+
fillStyle: 'black',
|
|
32730
|
+
strokeStyle: 'black',
|
|
32731
|
+
};
|
|
32343
32732
|
|
|
32344
|
-
|
|
32345
|
-
|
|
32346
|
-
|
|
32347
|
-
this.version = binaryParser.getInt();
|
|
32348
|
-
this.indexPos = binaryParser.getLong();
|
|
32349
|
-
this.indexSize = binaryParser.getInt();
|
|
32350
|
-
binaryParser.getInt();
|
|
32733
|
+
// tick
|
|
32734
|
+
IGVGraphics.strokeLine(ctx, xTickStart, shim * height, xTickEnd, shim * height, properties);
|
|
32735
|
+
IGVGraphics.fillText(ctx, prettyPrint(flipAxis ? this.dataRange.min : this.dataRange.max), xTickStart + 4, shim * height + 12, properties);
|
|
32351
32736
|
|
|
32737
|
+
const y = (1.0 - shim) * height;
|
|
32352
32738
|
|
|
32353
|
-
|
|
32354
|
-
|
|
32355
|
-
|
|
32356
|
-
while (nWindowFunctions-- > 0) {
|
|
32357
|
-
this.windowFunctions.push(binaryParser.getString());
|
|
32358
|
-
}
|
|
32359
|
-
}
|
|
32739
|
+
// tick
|
|
32740
|
+
IGVGraphics.strokeLine(ctx, xTickStart, y, xTickEnd, y, properties);
|
|
32741
|
+
IGVGraphics.fillText(ctx, prettyPrint(flipAxis ? this.dataRange.max : this.dataRange.min), xTickStart + 4, y - 4, properties);
|
|
32360
32742
|
|
|
32361
|
-
|
|
32362
|
-
|
|
32743
|
+
// vertical axis
|
|
32744
|
+
IGVGraphics.strokeLine(ctx, xTickEnd, shim * height, xTickEnd, y, properties);
|
|
32363
32745
|
|
|
32364
|
-
|
|
32365
|
-
this.trackNames = [];
|
|
32366
|
-
while (nTracks-- > 0) {
|
|
32367
|
-
this.trackNames.push(binaryParser.getString());
|
|
32368
|
-
}
|
|
32369
|
-
this.genomeID = binaryParser.getString();
|
|
32370
|
-
this.flags = binaryParser.getInt();
|
|
32371
|
-
this.compressed = (this.flags & GZIP_FLAG) !== 0;
|
|
32746
|
+
function prettyPrint(number) {
|
|
32372
32747
|
|
|
32373
|
-
|
|
32374
|
-
|
|
32375
|
-
|
|
32376
|
-
|
|
32377
|
-
|
|
32378
|
-
|
|
32379
|
-
}))
|
|
32380
|
-
|
|
32381
|
-
|
|
32382
|
-
|
|
32383
|
-
while (nEntries-- > 0) {
|
|
32384
|
-
const name = binaryParser.getString();
|
|
32385
|
-
const pos = binaryParser.getLong();
|
|
32386
|
-
const size = binaryParser.getInt();
|
|
32387
|
-
this.datasetIndex[name] = {position: pos, size: size};
|
|
32748
|
+
if (number === 0) {
|
|
32749
|
+
return "0"
|
|
32750
|
+
} else if (Math.abs(number) >= 10) {
|
|
32751
|
+
return number.toFixed()
|
|
32752
|
+
} else if (Math.abs(number) >= 1) {
|
|
32753
|
+
return number.toFixed(1)
|
|
32754
|
+
} else if (Math.abs(number) >= 0.1) {
|
|
32755
|
+
return number.toFixed(2)
|
|
32756
|
+
} else {
|
|
32757
|
+
return number.toExponential(1)
|
|
32388
32758
|
}
|
|
32759
|
+
}
|
|
32760
|
+
}
|
|
32389
32761
|
|
|
32390
|
-
|
|
32391
|
-
nEntries = binaryParser.getInt();
|
|
32392
|
-
while (nEntries-- > 0) {
|
|
32393
|
-
const name = binaryParser.getString();
|
|
32394
|
-
const pos = binaryParser.getLong();
|
|
32395
|
-
const size = binaryParser.getInt();
|
|
32396
|
-
this.groupIndex[name] = {position: pos, size: size};
|
|
32397
|
-
}
|
|
32762
|
+
const DEFAULT_COLOR$2 = 'rgb(150, 150, 150)';
|
|
32398
32763
|
|
|
32399
|
-
|
|
32764
|
+
|
|
32765
|
+
class WigTrack extends TrackBase {
|
|
32766
|
+
|
|
32767
|
+
static defaults = {
|
|
32768
|
+
height: 50,
|
|
32769
|
+
flipAxis: false,
|
|
32770
|
+
logScale: false,
|
|
32771
|
+
windowFunction: 'mean',
|
|
32772
|
+
graphType: 'bar',
|
|
32773
|
+
normalize: undefined,
|
|
32774
|
+
scaleFactor: undefined,
|
|
32775
|
+
overflowColor: `rgb(255, 32, 255)`,
|
|
32776
|
+
baselineColor: 'lightGray',
|
|
32777
|
+
summarize: true
|
|
32400
32778
|
}
|
|
32401
32779
|
|
|
32402
|
-
|
|
32780
|
+
constructor(config, browser) {
|
|
32781
|
+
super(config, browser);
|
|
32782
|
+
}
|
|
32403
32783
|
|
|
32404
|
-
|
|
32784
|
+
init(config) {
|
|
32785
|
+
|
|
32786
|
+
super.init(config);
|
|
32787
|
+
|
|
32788
|
+
this.type = "wig";
|
|
32789
|
+
this.featureType = 'numeric';
|
|
32790
|
+
this.resolutionAware = true;
|
|
32791
|
+
this.paintAxis = paintAxis;
|
|
32792
|
+
|
|
32793
|
+
const format = config.format ? config.format.toLowerCase() : config.format;
|
|
32794
|
+
if (config.featureSource) {
|
|
32795
|
+
this.featureSource = config.featureSource;
|
|
32796
|
+
delete config.featureSource;
|
|
32797
|
+
} else if ("bigwig" === format) {
|
|
32798
|
+
this.featureSource = new BWSource(config, this.browser.genome);
|
|
32799
|
+
} else if ("tdf" === format) {
|
|
32800
|
+
this.featureSource = new TDFSource(config, this.browser.genome);
|
|
32801
|
+
} else {
|
|
32802
|
+
this.featureSource = FeatureSource(config, this.browser.genome);
|
|
32803
|
+
}
|
|
32405
32804
|
|
|
32406
|
-
if (this.datasetCache[key]) {
|
|
32407
|
-
return this.datasetCache[key]
|
|
32408
32805
|
|
|
32806
|
+
// Override autoscale default
|
|
32807
|
+
if (config.max === undefined || config.autoscale === true) {
|
|
32808
|
+
this.autoscale = true;
|
|
32409
32809
|
} else {
|
|
32410
|
-
|
|
32411
|
-
|
|
32412
|
-
|
|
32810
|
+
this.dataRange = {
|
|
32811
|
+
min: config.min || 0,
|
|
32812
|
+
max: config.max
|
|
32813
|
+
};
|
|
32814
|
+
}
|
|
32815
|
+
}
|
|
32413
32816
|
|
|
32414
|
-
|
|
32415
|
-
|
|
32416
|
-
|
|
32417
|
-
|
|
32418
|
-
|
|
32419
|
-
}
|
|
32420
|
-
const indexEntry = this.datasetIndex[dsName];
|
|
32817
|
+
async postInit() {
|
|
32818
|
+
const header = await this.getHeader();
|
|
32819
|
+
if (this.disposed) return // This track was removed during async load
|
|
32820
|
+
if (header) this.setTrackProperties(header);
|
|
32821
|
+
}
|
|
32421
32822
|
|
|
32422
|
-
|
|
32423
|
-
return undefined
|
|
32424
|
-
}
|
|
32823
|
+
async getFeatures(chr, start, end, bpPerPixel) {
|
|
32425
32824
|
|
|
32426
|
-
|
|
32427
|
-
range: {
|
|
32428
|
-
start: indexEntry.position,
|
|
32429
|
-
size: indexEntry.size
|
|
32430
|
-
}
|
|
32431
|
-
}));
|
|
32825
|
+
const windowFunction = this.windowFunction;
|
|
32432
32826
|
|
|
32433
|
-
|
|
32434
|
-
|
|
32827
|
+
const features = await this.featureSource.getFeatures({
|
|
32828
|
+
chr,
|
|
32829
|
+
start,
|
|
32830
|
+
end,
|
|
32831
|
+
bpPerPixel,
|
|
32832
|
+
visibilityWindow: this.visibilityWindow,
|
|
32833
|
+
windowFunction
|
|
32834
|
+
});
|
|
32835
|
+
if (this.normalize && this.featureSource.normalizationFactor) {
|
|
32836
|
+
const scaleFactor = this.featureSource.normalizationFactor;
|
|
32837
|
+
for (let f of features) {
|
|
32838
|
+
f.value *= scaleFactor;
|
|
32435
32839
|
}
|
|
32436
|
-
|
|
32437
|
-
|
|
32438
|
-
|
|
32439
|
-
|
|
32440
|
-
|
|
32441
|
-
attributes[binaryParser.getString()] = binaryParser.getString();
|
|
32840
|
+
}
|
|
32841
|
+
if (this.scaleFactor) {
|
|
32842
|
+
const scaleFactor = this.scaleFactor;
|
|
32843
|
+
for (let f of features) {
|
|
32844
|
+
f.value *= scaleFactor;
|
|
32442
32845
|
}
|
|
32443
|
-
|
|
32444
|
-
|
|
32445
|
-
|
|
32446
|
-
|
|
32447
|
-
|
|
32448
|
-
|
|
32846
|
+
}
|
|
32847
|
+
|
|
32848
|
+
// Summarize features to current resolution. This needs to be done here, rather than in the "draw" function,
|
|
32849
|
+
// for group autoscale to work.
|
|
32850
|
+
if (this.summarize && ("mean" === windowFunction || "min" === windowFunction || "max" === windowFunction)) {
|
|
32851
|
+
return summarizeData(features, start, bpPerPixel, windowFunction)
|
|
32852
|
+
} else {
|
|
32853
|
+
return features
|
|
32854
|
+
}
|
|
32855
|
+
}
|
|
32856
|
+
|
|
32857
|
+
menuItemList() {
|
|
32858
|
+
const items = [];
|
|
32859
|
+
|
|
32860
|
+
if (this.flipAxis !== undefined) {
|
|
32861
|
+
items.push('<hr>');
|
|
32862
|
+
|
|
32863
|
+
function click() {
|
|
32864
|
+
this.flipAxis = !this.flipAxis;
|
|
32865
|
+
this.trackView.repaintViews();
|
|
32449
32866
|
}
|
|
32450
32867
|
|
|
32451
|
-
|
|
32452
|
-
|
|
32453
|
-
attributes: attributes,
|
|
32454
|
-
dataType: dataType,
|
|
32455
|
-
tileWidth: tileWidth,
|
|
32456
|
-
tiles: tiles
|
|
32457
|
-
};
|
|
32868
|
+
items.push({label: 'Flip y-axis', click});
|
|
32869
|
+
}
|
|
32458
32870
|
|
|
32459
|
-
|
|
32460
|
-
|
|
32871
|
+
if(this.featureSource.windowFunctions) {
|
|
32872
|
+
items.push(...this.wigSummarizationItems());
|
|
32461
32873
|
}
|
|
32874
|
+
|
|
32875
|
+
items.push(...this.numericDataMenuItems());
|
|
32876
|
+
|
|
32877
|
+
return items
|
|
32462
32878
|
}
|
|
32463
32879
|
|
|
32464
|
-
|
|
32880
|
+
wigSummarizationItems() {
|
|
32465
32881
|
|
|
32466
|
-
const
|
|
32467
|
-
const rootGroup = this.groupCache["/"];
|
|
32468
|
-
if (rootGroup) {
|
|
32469
|
-
return rootGroup
|
|
32470
|
-
} else {
|
|
32882
|
+
const windowFunctions = this.featureSource.windowFunctions;
|
|
32471
32883
|
|
|
32472
|
-
|
|
32473
|
-
|
|
32474
|
-
|
|
32884
|
+
const menuItems = [];
|
|
32885
|
+
menuItems.push('<hr/>');
|
|
32886
|
+
menuItems.push("<div>Windowing function</div>");
|
|
32887
|
+
for (const wf of windowFunctions) {
|
|
32888
|
+
const object = $$1(createCheckbox(wf, this.windowFunction === wf));
|
|
32475
32889
|
|
|
32476
|
-
|
|
32477
|
-
|
|
32478
|
-
this.
|
|
32890
|
+
function clickHandler() {
|
|
32891
|
+
this.windowFunction = wf;
|
|
32892
|
+
this.trackView.updateViews();
|
|
32479
32893
|
}
|
|
32480
32894
|
|
|
32481
|
-
|
|
32482
|
-
|
|
32483
|
-
group.totalCount = Number(totalCountString);
|
|
32484
|
-
}
|
|
32895
|
+
menuItems.push({object, click: clickHandler});
|
|
32896
|
+
}
|
|
32485
32897
|
|
|
32486
|
-
|
|
32487
|
-
|
|
32488
|
-
if (names) {
|
|
32489
|
-
names.split(",").forEach(function (chr) {
|
|
32490
|
-
const canonicalName = genome.getChromosomeName(chr);
|
|
32491
|
-
chrAliasTable[canonicalName] = chr;
|
|
32492
|
-
});
|
|
32493
|
-
}
|
|
32494
|
-
this.chrAliasTable = chrAliasTable;
|
|
32898
|
+
return menuItems
|
|
32899
|
+
}
|
|
32495
32900
|
|
|
32496
|
-
|
|
32497
|
-
|
|
32901
|
+
|
|
32902
|
+
async getHeader() {
|
|
32903
|
+
|
|
32904
|
+
if (typeof this.featureSource.getHeader === "function") {
|
|
32905
|
+
this.header = await this.featureSource.getHeader();
|
|
32498
32906
|
}
|
|
32907
|
+
return this.header
|
|
32499
32908
|
}
|
|
32500
32909
|
|
|
32501
|
-
|
|
32910
|
+
// TODO: refactor to igvUtils.js
|
|
32911
|
+
getScaleFactor(min, max, height, logScale) {
|
|
32912
|
+
const minValue = (logScale === true) ? ((min < 0) ? -Math.log10(Math.abs(min) + 1) : Math.log10(Math.abs(min) + 1)) : min;
|
|
32913
|
+
const maxValue = (logScale === true) ? Math.log10(Math.abs(max) + 1) : max;
|
|
32914
|
+
const scale = height / (maxValue - minValue);
|
|
32915
|
+
return scale
|
|
32916
|
+
}
|
|
32502
32917
|
|
|
32503
|
-
|
|
32504
|
-
|
|
32505
|
-
|
|
32506
|
-
} else {
|
|
32918
|
+
computeYPixelValue(yValue, yScaleFactor) {
|
|
32919
|
+
return (this.flipAxis ? (yValue - this.dataRange.min) : (this.dataRange.max - yValue)) * yScaleFactor
|
|
32920
|
+
}
|
|
32507
32921
|
|
|
32508
|
-
|
|
32509
|
-
|
|
32510
|
-
|
|
32511
|
-
|
|
32512
|
-
|
|
32922
|
+
computeYPixelValueInLogScale(yValue, yScaleFactor) {
|
|
32923
|
+
let maxValue = this.dataRange.max;
|
|
32924
|
+
let minValue = this.dataRange.min;
|
|
32925
|
+
minValue = (minValue < 0) ? -Math.log10(Math.abs(minValue) + 1) : Math.log10(Math.abs(minValue) + 1);
|
|
32926
|
+
maxValue = (maxValue < 0) ? -Math.log10(Math.abs(maxValue) + 1) : Math.log10(Math.abs(maxValue) + 1);
|
|
32927
|
+
|
|
32928
|
+
yValue = (yValue < 0) ? -Math.log10(Math.abs(yValue) +1) : Math.log10(yValue + 1);
|
|
32929
|
+
return ((this.flipAxis ? (yValue - minValue) : (maxValue - yValue)) * yScaleFactor)
|
|
32930
|
+
}
|
|
32513
32931
|
|
|
32514
|
-
|
|
32515
|
-
|
|
32516
|
-
|
|
32517
|
-
|
|
32932
|
+
draw(options) {
|
|
32933
|
+
|
|
32934
|
+
const features = options.features;
|
|
32935
|
+
const ctx = options.context;
|
|
32936
|
+
const bpPerPixel = options.bpPerPixel;
|
|
32937
|
+
const bpStart = options.bpStart;
|
|
32938
|
+
const pixelWidth = options.pixelWidth;
|
|
32939
|
+
const pixelHeight = options.pixelHeight - 1;
|
|
32940
|
+
const bpEnd = bpStart + pixelWidth * bpPerPixel + 1;
|
|
32941
|
+
|
|
32942
|
+
const scaleFactor = this.getScaleFactor(this.dataRange.min, this.dataRange.max, pixelHeight, this.logScale);
|
|
32943
|
+
const yScale = (yValue) => this.logScale
|
|
32944
|
+
? this.computeYPixelValueInLogScale(yValue, scaleFactor)
|
|
32945
|
+
: this.computeYPixelValue(yValue, scaleFactor);
|
|
32946
|
+
|
|
32947
|
+
if (features && features.length > 0) {
|
|
32948
|
+
|
|
32949
|
+
if (this.dataRange.min === undefined) this.dataRange.min = 0;
|
|
32950
|
+
|
|
32951
|
+
// Max can be less than min if config.min is set but max left to autoscale. If that's the case there is
|
|
32952
|
+
// nothing to paint.
|
|
32953
|
+
if (this.dataRange.max > this.dataRange.min) {
|
|
32954
|
+
|
|
32955
|
+
let lastPixelEnd = -1;
|
|
32956
|
+
let lastY;
|
|
32957
|
+
const y0 = yScale(0);
|
|
32958
|
+
|
|
32959
|
+
for (let f of features) {
|
|
32960
|
+
|
|
32961
|
+
if (f.end < bpStart) continue
|
|
32962
|
+
if (f.start > bpEnd) break
|
|
32963
|
+
|
|
32964
|
+
const x = (f.start - bpStart) / bpPerPixel;
|
|
32965
|
+
if (isNaN(x)) continue
|
|
32966
|
+
|
|
32967
|
+
let y = yScale(f.value);
|
|
32968
|
+
|
|
32969
|
+
const rectEnd = (f.end - bpStart) / bpPerPixel;
|
|
32970
|
+
const width = rectEnd - x;
|
|
32971
|
+
|
|
32972
|
+
const color = options.alpha ? IGVColor.addAlpha(this.getColorForFeature(f), options.alpha) : this.getColorForFeature(f);
|
|
32973
|
+
|
|
32974
|
+
if (this.graphType === "line") {
|
|
32975
|
+
if (lastY !== undefined) {
|
|
32976
|
+
IGVGraphics.strokeLine(ctx, lastPixelEnd, lastY, x, y, {
|
|
32977
|
+
"fillStyle": color,
|
|
32978
|
+
"strokeStyle": color
|
|
32979
|
+
});
|
|
32980
|
+
}
|
|
32981
|
+
IGVGraphics.strokeLine(ctx, x, y, x + width, y, {"fillStyle": color, "strokeStyle": color});
|
|
32982
|
+
} else if (this.graphType === "points") {
|
|
32983
|
+
const pointSize = this.config.pointSize || 3;
|
|
32984
|
+
const px = x + width / 2;
|
|
32985
|
+
IGVGraphics.fillCircle(ctx, px, y, pointSize / 2, {"fillStyle": color, "strokeStyle": color});
|
|
32986
|
+
|
|
32987
|
+
if (f.value > this.dataRange.max) {
|
|
32988
|
+
IGVGraphics.fillCircle(ctx, px, pointSize / 2, pointSize / 2, 3, {fillStyle: this.overflowColor});
|
|
32989
|
+
} else if (f.value < this.dataRange.min) {
|
|
32990
|
+
IGVGraphics.fillCircle(ctx, px, pixelHeight - pointSize / 2, pointSize / 2, 3, {fillStyle: this.overflowColor});
|
|
32991
|
+
}
|
|
32992
|
+
|
|
32993
|
+
} else {
|
|
32994
|
+
// Default graph type (bar)
|
|
32995
|
+
const height = Math.min(pixelHeight, y - y0);
|
|
32996
|
+
IGVGraphics.fillRect(ctx, x, y0, width, height, {fillStyle: color});
|
|
32997
|
+
if (f.value > this.dataRange.max) {
|
|
32998
|
+
IGVGraphics.fillRect(ctx, x, 0, width, 3, {fillStyle: this.overflowColor});
|
|
32999
|
+
} else if (f.value < this.dataRange.min) {
|
|
33000
|
+
IGVGraphics.fillRect(ctx, x, pixelHeight - 2, width, 3, {fillStyle: this.overflowColor});
|
|
33001
|
+
}
|
|
33002
|
+
|
|
33003
|
+
}
|
|
33004
|
+
lastPixelEnd = x + width;
|
|
33005
|
+
lastY = y;
|
|
32518
33006
|
}
|
|
32519
|
-
}));
|
|
32520
33007
|
|
|
32521
|
-
|
|
32522
|
-
|
|
33008
|
+
// If the track includes negative values draw a baseline
|
|
33009
|
+
if (this.dataRange.min < 0) {
|
|
33010
|
+
let maxValue = this.dataRange.max;
|
|
33011
|
+
let minValue = this.dataRange.min;
|
|
33012
|
+
minValue = (this.logScale === true) ? ((minValue < 0) ? -Math.log10(Math.abs(minValue) + 1) : Math.log10(Math.abs(minValue) + 1)) : minValue;
|
|
33013
|
+
maxValue = (this.logScale === true) ? ((maxValue < 0) ? -Math.log10(Math.abs(maxValue) + 1) : Math.log10(Math.abs(maxValue) + 1)) : maxValue;
|
|
33014
|
+
const ratio = maxValue / (maxValue - minValue);
|
|
33015
|
+
const basepx = this.flipAxis ? (1 - ratio) * pixelHeight : ratio * pixelHeight;
|
|
33016
|
+
IGVGraphics.strokeLine(ctx, 0, basepx, options.pixelWidth, basepx, {strokeStyle: this.baselineColor});
|
|
33017
|
+
}
|
|
32523
33018
|
}
|
|
33019
|
+
}
|
|
32524
33020
|
|
|
32525
|
-
|
|
32526
|
-
|
|
32527
|
-
let
|
|
32528
|
-
|
|
32529
|
-
|
|
32530
|
-
|
|
32531
|
-
|
|
33021
|
+
// Draw guidelines
|
|
33022
|
+
if (this.config.hasOwnProperty('guideLines')) {
|
|
33023
|
+
for (let line of this.config.guideLines) {
|
|
33024
|
+
if (line.hasOwnProperty('color') && line.hasOwnProperty('y') && line.hasOwnProperty('dotted')) {
|
|
33025
|
+
let y = yScale(line.y);
|
|
33026
|
+
let props = {
|
|
33027
|
+
'strokeStyle': line['color'],
|
|
33028
|
+
'strokeWidth': 2
|
|
33029
|
+
};
|
|
33030
|
+
if (line['dotted']) IGVGraphics.dashedLine(options.context, 0, y, options.pixelWidth, y, 5, props);
|
|
33031
|
+
else IGVGraphics.strokeLine(options.context, 0, y, options.pixelWidth, y, props);
|
|
33032
|
+
}
|
|
32532
33033
|
}
|
|
32533
|
-
this.groupCache[name] = group;
|
|
32534
|
-
return group
|
|
32535
33034
|
}
|
|
32536
33035
|
}
|
|
32537
33036
|
|
|
32538
|
-
|
|
33037
|
+
popupData(clickState, features) {
|
|
32539
33038
|
|
|
32540
|
-
|
|
32541
|
-
return a.position - b.position
|
|
32542
|
-
});
|
|
33039
|
+
if (features === undefined) features = this.clickedFeatures(clickState);
|
|
32543
33040
|
|
|
32544
|
-
|
|
32545
|
-
return idx.size > 0
|
|
32546
|
-
});
|
|
33041
|
+
if (features && features.length > 0) {
|
|
32547
33042
|
|
|
32548
|
-
|
|
32549
|
-
|
|
32550
|
-
}
|
|
33043
|
+
const genomicLocation = clickState.genomicLocation;
|
|
33044
|
+
const popupData = [];
|
|
32551
33045
|
|
|
32552
|
-
|
|
33046
|
+
// Sort features based on distance from click
|
|
33047
|
+
features.sort(function (a, b) {
|
|
33048
|
+
const distA = Math.abs((a.start + a.end) / 2 - genomicLocation);
|
|
33049
|
+
const distB = Math.abs((b.start + b.end) / 2 - genomicLocation);
|
|
33050
|
+
return distA - distB
|
|
33051
|
+
});
|
|
32553
33052
|
|
|
32554
|
-
|
|
33053
|
+
// Display closest 10
|
|
33054
|
+
const displayFeatures = features.length > 10 ? features.slice(0, 10) : features;
|
|
32555
33055
|
|
|
32556
|
-
|
|
33056
|
+
// Resort in ascending order
|
|
33057
|
+
displayFeatures.sort(function (a, b) {
|
|
33058
|
+
return a.start - b.start
|
|
33059
|
+
});
|
|
32557
33060
|
|
|
32558
|
-
|
|
32559
|
-
|
|
32560
|
-
|
|
32561
|
-
|
|
33061
|
+
for (let selectedFeature of displayFeatures) {
|
|
33062
|
+
if (selectedFeature) {
|
|
33063
|
+
if (popupData.length > 0) {
|
|
33064
|
+
popupData.push('<hr/>');
|
|
33065
|
+
}
|
|
33066
|
+
let posString = (selectedFeature.end - selectedFeature.start) === 1 ?
|
|
33067
|
+
numberFormatter$1(Math.floor(selectedFeature.start) + 1)
|
|
33068
|
+
: numberFormatter$1(Math.floor(selectedFeature.start) + 1) + "-" + numberFormatter$1(Math.floor(selectedFeature.end));
|
|
33069
|
+
popupData.push({name: "Position:", value: posString});
|
|
33070
|
+
popupData.push({
|
|
33071
|
+
name: "Value: ",
|
|
33072
|
+
value: numberFormatter$1(selectedFeature.value.toFixed(4))
|
|
33073
|
+
});
|
|
32562
33074
|
}
|
|
32563
|
-
}));
|
|
32564
|
-
|
|
32565
|
-
const tileData = this.compressed ? inflate_1$3(data).buffer : data;
|
|
32566
|
-
|
|
32567
|
-
const binaryParser = new BinaryParser$1(new DataView(tileData));
|
|
32568
|
-
const type = binaryParser.getString();
|
|
32569
|
-
let tile;
|
|
32570
|
-
switch (type) {
|
|
32571
|
-
case "fixedStep":
|
|
32572
|
-
tile = createFixedStep(binaryParser, nTracks);
|
|
32573
|
-
break
|
|
32574
|
-
case "variableStep":
|
|
32575
|
-
tile = createVariableStep(binaryParser, nTracks);
|
|
32576
|
-
break
|
|
32577
|
-
case "bed":
|
|
32578
|
-
case "bedWithName":
|
|
32579
|
-
tile = createBed(binaryParser, nTracks, type);
|
|
32580
|
-
break
|
|
32581
|
-
default:
|
|
32582
|
-
throw "Unknown tile type: " + type
|
|
32583
33075
|
}
|
|
32584
|
-
|
|
33076
|
+
if (displayFeatures.length < features.length) {
|
|
33077
|
+
popupData.push("<hr/>...");
|
|
33078
|
+
}
|
|
33079
|
+
|
|
33080
|
+
return popupData
|
|
32585
33081
|
|
|
33082
|
+
} else {
|
|
33083
|
+
return []
|
|
32586
33084
|
}
|
|
32587
|
-
return tiles
|
|
32588
33085
|
}
|
|
32589
33086
|
|
|
32590
|
-
|
|
33087
|
+
get supportsWholeGenome() {
|
|
33088
|
+
return !this.config.indexURL && this.config.supportsWholeGenome !== false
|
|
33089
|
+
}
|
|
32591
33090
|
|
|
32592
|
-
|
|
32593
|
-
|
|
32594
|
-
|
|
32595
|
-
|
|
32596
|
-
|
|
32597
|
-
}));
|
|
33091
|
+
/**
|
|
33092
|
+
* Return color for feature.
|
|
33093
|
+
* @param feature
|
|
33094
|
+
* @returns {string}
|
|
33095
|
+
*/
|
|
32598
33096
|
|
|
32599
|
-
|
|
32600
|
-
|
|
32601
|
-
|
|
32602
|
-
|
|
33097
|
+
getColorForFeature(f) {
|
|
33098
|
+
let c = (f.value < 0 && this.altColor) ? this.altColor : this.color || DEFAULT_COLOR$2;
|
|
33099
|
+
return (typeof c === "function") ? c(f.value) : c
|
|
33100
|
+
}
|
|
32603
33101
|
|
|
32604
|
-
|
|
32605
|
-
|
|
32606
|
-
|
|
32607
|
-
|
|
32608
|
-
|
|
32609
|
-
case "variableStep":
|
|
32610
|
-
return createVariableStep(binaryParser, nTracks)
|
|
32611
|
-
case "bed":
|
|
32612
|
-
case "bedWithName":
|
|
32613
|
-
return createBed(binaryParser, nTracks, type)
|
|
32614
|
-
default:
|
|
32615
|
-
throw "Unknown tile type: " + type
|
|
32616
|
-
}
|
|
33102
|
+
/**
|
|
33103
|
+
* Called when the track is removed. Do any needed cleanup here
|
|
33104
|
+
*/
|
|
33105
|
+
dispose() {
|
|
33106
|
+
this.trackView = undefined;
|
|
32617
33107
|
}
|
|
32618
33108
|
|
|
32619
33109
|
}
|
|
32620
33110
|
|
|
32621
|
-
|
|
32622
|
-
|
|
32623
|
-
|
|
32624
|
-
|
|
33111
|
+
/**
|
|
33112
|
+
* Summarize wig data in bins of size "bpPerPixel" with the given window function.
|
|
33113
|
+
*
|
|
33114
|
+
* @param features wig (numeric) data -- features cannot overlap, and are in ascending order by start position
|
|
33115
|
+
* @param startBP bp start position for computing binned data
|
|
33116
|
+
* @param bpPerPixel bp per pixel (bin)
|
|
33117
|
+
* @param windowFunction mean, min, or max
|
|
33118
|
+
* @returns {*|*[]}
|
|
33119
|
+
*/
|
|
33120
|
+
function summarizeData(features, startBP, bpPerPixel, windowFunction = "mean") {
|
|
32625
33121
|
|
|
32626
|
-
|
|
32627
|
-
|
|
32628
|
-
while (nt-- > 0) {
|
|
32629
|
-
let np = nPositions;
|
|
32630
|
-
const dtrack = [];
|
|
32631
|
-
while (np-- > 0) {
|
|
32632
|
-
dtrack.push(binaryParser.getFloat());
|
|
32633
|
-
}
|
|
32634
|
-
data.push(dtrack);
|
|
33122
|
+
if (bpPerPixel <= 1 || !features || features.length === 0) {
|
|
33123
|
+
return features
|
|
32635
33124
|
}
|
|
32636
33125
|
|
|
32637
|
-
|
|
32638
|
-
|
|
32639
|
-
|
|
32640
|
-
|
|
32641
|
-
data: data,
|
|
32642
|
-
nTracks: nTracks,
|
|
32643
|
-
nPositions: nPositions
|
|
32644
|
-
}
|
|
32645
|
-
}
|
|
33126
|
+
// Assume features are sorted by position. Wig features cannot overlap. Note, UCSC "reductionLevel" == bpPerPixel
|
|
33127
|
+
const chr = features[0].chr;
|
|
33128
|
+
const binSize = bpPerPixel;
|
|
33129
|
+
const summaryFeatures = [];
|
|
32646
33130
|
|
|
32647
|
-
|
|
33131
|
+
const finishBin = (bin) => {
|
|
33132
|
+
const start = startBP + bin.bin * binSize;
|
|
33133
|
+
const end = start + binSize;
|
|
33134
|
+
let value;
|
|
33135
|
+
switch (windowFunction) {
|
|
33136
|
+
case "mean":
|
|
33137
|
+
value = bin.sumData / bin.count;
|
|
33138
|
+
break
|
|
33139
|
+
case "max":
|
|
33140
|
+
value = bin.max;
|
|
33141
|
+
break
|
|
33142
|
+
case "min":
|
|
33143
|
+
value = bin.min;
|
|
33144
|
+
break
|
|
33145
|
+
default:
|
|
33146
|
+
throw Error(`Unknown window function: ${windowFunction}`)
|
|
33147
|
+
}
|
|
33148
|
+
const description = `${windowFunction} of ${bin.count} values`;
|
|
33149
|
+
summaryFeatures.push({chr, start, end, value, description});
|
|
33150
|
+
};
|
|
32648
33151
|
|
|
32649
|
-
|
|
32650
|
-
|
|
32651
|
-
const nPositions = binaryParser.getInt();
|
|
32652
|
-
const start = [];
|
|
33152
|
+
let currentBinData;
|
|
33153
|
+
for (let f of features) {
|
|
32653
33154
|
|
|
32654
|
-
|
|
32655
|
-
|
|
32656
|
-
|
|
32657
|
-
|
|
32658
|
-
binaryParser.getInt(); // # of samples, ignored but should === nTracks
|
|
33155
|
+
// Loop through bins this feature overlaps, updating the weighted sum for each bin or min/max,
|
|
33156
|
+
// depending on window function
|
|
33157
|
+
let startBin = Math.floor((f.start - startBP) / binSize);
|
|
33158
|
+
const endBin = Math.floor((f.end - startBP) / binSize);
|
|
32659
33159
|
|
|
32660
|
-
|
|
32661
|
-
|
|
32662
|
-
|
|
32663
|
-
np = nPositions;
|
|
32664
|
-
const dtrack = [];
|
|
32665
|
-
while (np-- > 0) {
|
|
32666
|
-
dtrack.push(binaryParser.getFloat());
|
|
33160
|
+
if (currentBinData && startBin === currentBinData.bin) {
|
|
33161
|
+
currentBinData.add(f);
|
|
33162
|
+
startBin++;
|
|
32667
33163
|
}
|
|
32668
|
-
data.push(dtrack);
|
|
32669
|
-
}
|
|
32670
33164
|
|
|
32671
|
-
|
|
32672
|
-
|
|
32673
|
-
|
|
32674
|
-
|
|
32675
|
-
|
|
32676
|
-
data: data,
|
|
32677
|
-
nTracks: nTracks,
|
|
32678
|
-
nPositions: nPositions
|
|
32679
|
-
}
|
|
32680
|
-
}
|
|
33165
|
+
if (!currentBinData || endBin > currentBinData.bin) {
|
|
33166
|
+
|
|
33167
|
+
if(currentBinData) {
|
|
33168
|
+
finishBin(currentBinData);
|
|
33169
|
+
}
|
|
32681
33170
|
|
|
32682
|
-
|
|
33171
|
+
// Feature stretches across multiple bins.
|
|
33172
|
+
if (endBin > startBin) {
|
|
33173
|
+
const end = startBP + endBin * binSize;
|
|
33174
|
+
summaryFeatures.push({chr, start: f.start, end, value: f.value});
|
|
33175
|
+
}
|
|
32683
33176
|
|
|
32684
|
-
|
|
33177
|
+
currentBinData = new SummaryBinData(endBin, f);
|
|
33178
|
+
}
|
|
32685
33179
|
|
|
32686
|
-
let n = nPositions;
|
|
32687
|
-
const start = [];
|
|
32688
|
-
while (n-- > 0) {
|
|
32689
|
-
start.push(binaryParser.getInt());
|
|
32690
33180
|
}
|
|
32691
|
-
|
|
32692
|
-
|
|
32693
|
-
const end = [];
|
|
32694
|
-
while (n-- > 0) {
|
|
32695
|
-
end.push(binaryParser.getInt());
|
|
33181
|
+
if(currentBinData) {
|
|
33182
|
+
finishBin(currentBinData);
|
|
32696
33183
|
}
|
|
32697
33184
|
|
|
32698
|
-
|
|
32699
|
-
const
|
|
32700
|
-
let
|
|
32701
|
-
|
|
32702
|
-
|
|
32703
|
-
|
|
32704
|
-
|
|
32705
|
-
|
|
33185
|
+
// Consolidate
|
|
33186
|
+
const c = [];
|
|
33187
|
+
let lastFeature = summaryFeatures[0];
|
|
33188
|
+
for (let f of summaryFeatures) {
|
|
33189
|
+
if (lastFeature.value === f.value && f.start <= lastFeature.end) {
|
|
33190
|
+
lastFeature.end = f.end;
|
|
33191
|
+
} else {
|
|
33192
|
+
c.push(lastFeature);
|
|
33193
|
+
lastFeature = f;
|
|
32706
33194
|
}
|
|
32707
|
-
data.push(dtrack);
|
|
32708
33195
|
}
|
|
33196
|
+
c.push(lastFeature);
|
|
32709
33197
|
|
|
32710
|
-
|
|
32711
|
-
n = nPositions;
|
|
32712
|
-
const name = [];
|
|
32713
|
-
while (n-- > 0) {
|
|
32714
|
-
name.push(binaryParser.getString());
|
|
32715
|
-
}
|
|
32716
|
-
}
|
|
33198
|
+
return c
|
|
32717
33199
|
|
|
32718
|
-
return {
|
|
32719
|
-
type: type,
|
|
32720
|
-
start: start,
|
|
32721
|
-
end: end,
|
|
32722
|
-
data: data,
|
|
32723
|
-
nTracks: nTracks,
|
|
32724
|
-
nPositions: nPositions
|
|
32725
|
-
}
|
|
32726
33200
|
}
|
|
32727
33201
|
|
|
32728
|
-
|
|
33202
|
+
class SummaryBinData {
|
|
33203
|
+
constructor(bin, feature) {
|
|
33204
|
+
this.bin = bin;
|
|
33205
|
+
this.sumData = feature.value;
|
|
33206
|
+
this.count = 1;
|
|
33207
|
+
this.min = feature.value;
|
|
33208
|
+
this.max = feature.value;
|
|
33209
|
+
}
|
|
33210
|
+
|
|
33211
|
+
add(feature) {
|
|
33212
|
+
this.sumData += feature.value;
|
|
33213
|
+
this.max = Math.max(feature.value, this.max);
|
|
33214
|
+
this.min = Math.min(feature.value, this.min);
|
|
33215
|
+
this.count++;
|
|
33216
|
+
}
|
|
32729
33217
|
|
|
32730
|
-
|
|
32731
|
-
|
|
32732
|
-
for (let i = 1; i < tiles.length; i++) {
|
|
32733
|
-
const t = tiles[i];
|
|
32734
|
-
if (t.position > current.position + current.size) {
|
|
32735
|
-
consolidated.push(current);
|
|
32736
|
-
current = t;
|
|
32737
|
-
} else {
|
|
32738
|
-
current.size = t.position + t.size - current.position;
|
|
32739
|
-
}
|
|
33218
|
+
get mean() {
|
|
33219
|
+
return this.sumData / this.count
|
|
32740
33220
|
}
|
|
32741
|
-
consolidated.push(current);
|
|
32742
|
-
return consolidated
|
|
32743
33221
|
}
|
|
32744
33222
|
|
|
32745
|
-
|
|
32746
|
-
|
|
32747
|
-
|
|
32748
|
-
*
|
|
32749
|
-
* Author: Jim Robinson
|
|
32750
|
-
*
|
|
32751
|
-
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
32752
|
-
* of this software and associated documentation files (the "Software"), to deal
|
|
32753
|
-
* in the Software without restriction, including without limitation the rights
|
|
32754
|
-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
32755
|
-
* copies of the Software, and to permit persons to whom the Software is
|
|
32756
|
-
* furnished to do so, subject to the following conditions:
|
|
32757
|
-
*
|
|
32758
|
-
* The above copyright notice and this permission notice shall be included in
|
|
32759
|
-
* all copies or substantial portions of the Software.
|
|
32760
|
-
*
|
|
33223
|
+
const DEFAULT_MAX_WG_COUNT = 10000;
|
|
33224
|
+
|
|
33225
|
+
/**
|
|
33226
|
+
* feature source for "bed like" files (tab or whitespace delimited files with 1 feature per line: bed, gff, vcf, etc)
|
|
32761
33227
|
*
|
|
32762
|
-
*
|
|
32763
|
-
*
|
|
32764
|
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
32765
|
-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
32766
|
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
32767
|
-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
32768
|
-
* THE SOFTWARE.
|
|
33228
|
+
* @param config
|
|
33229
|
+
* @constructor
|
|
32769
33230
|
*/
|
|
33231
|
+
class TextFeatureSource extends BaseFeatureSource {
|
|
32770
33232
|
|
|
32771
|
-
class TDFSource extends BaseFeatureSource {
|
|
32772
|
-
|
|
32773
|
-
searchable = false
|
|
32774
33233
|
constructor(config, genome) {
|
|
33234
|
+
|
|
32775
33235
|
super(genome);
|
|
33236
|
+
|
|
33237
|
+
this.config = config || {};
|
|
32776
33238
|
this.genome = genome;
|
|
32777
|
-
this.
|
|
32778
|
-
this.
|
|
32779
|
-
|
|
33239
|
+
this.sourceType = (config.sourceType === undefined ? "file" : config.sourceType);
|
|
33240
|
+
this.maxWGCount = config.maxWGCount || DEFAULT_MAX_WG_COUNT;
|
|
33241
|
+
this.windowFunctions = ["mean", "min", "max", "none"];
|
|
32780
33242
|
|
|
32781
|
-
|
|
33243
|
+
const queryableFormats = new Set(["bigwig", "bw", "bigbed", "bb", "biginteract", "biggenepred", "bignarrowpeak", "tdf"]);
|
|
32782
33244
|
|
|
32783
|
-
|
|
32784
|
-
|
|
32785
|
-
|
|
32786
|
-
|
|
32787
|
-
|
|
32788
|
-
|
|
32789
|
-
|
|
32790
|
-
|
|
32791
|
-
|
|
32792
|
-
|
|
32793
|
-
|
|
32794
|
-
|
|
32795
|
-
|
|
32796
|
-
|
|
32797
|
-
|
|
32798
|
-
|
|
32799
|
-
|
|
32800
|
-
|
|
32801
|
-
|
|
32802
|
-
|
|
32803
|
-
|
|
32804
|
-
|
|
33245
|
+
this.queryable = config.indexURL || config.queryable === true; // False by default, unless explicitly set
|
|
33246
|
+
if (config.reader) {
|
|
33247
|
+
// Explicit reader implementation
|
|
33248
|
+
this.reader = config.reader;
|
|
33249
|
+
this.queryable = config.queryable !== false;
|
|
33250
|
+
} else if (config.sourceType === "ga4gh") {
|
|
33251
|
+
throw Error("Unsupported source type 'ga4gh'")
|
|
33252
|
+
} else if ((config.type === "eqtl" || config.type === "qtl") && config.sourceType === "gtex-ws") {
|
|
33253
|
+
this.reader = new GtexReader(config);
|
|
33254
|
+
this.queryable = true;
|
|
33255
|
+
} else if ("htsget" === config.sourceType) {
|
|
33256
|
+
this.reader = new HtsgetVariantReader(config, genome);
|
|
33257
|
+
this.queryable = true;
|
|
33258
|
+
} else if (config.sourceType === 'ucscservice') {
|
|
33259
|
+
this.reader = new UCSCServiceReader(config.source);
|
|
33260
|
+
this.queryable = true;
|
|
33261
|
+
} else if (config.sourceType === 'custom') {
|
|
33262
|
+
this.reader = new CustomServiceReader(config.source);
|
|
33263
|
+
this.queryable = false !== config.source.queryable;
|
|
33264
|
+
} else if ('service' === config.sourceType) {
|
|
33265
|
+
this.reader = new FeatureFileReader(config, genome);
|
|
33266
|
+
this.queryable = true;
|
|
33267
|
+
} else {
|
|
33268
|
+
// File of some type (i.e. not a webservice)
|
|
33269
|
+
this.reader = new FeatureFileReader(config, genome);
|
|
33270
|
+
if (config.queryable !== undefined) {
|
|
33271
|
+
this.queryable = config.queryable;
|
|
33272
|
+
} else if (queryableFormats.has(config.format) || this.reader.indexed) {
|
|
33273
|
+
this.queryable = true;
|
|
33274
|
+
} else ;
|
|
33275
|
+
}
|
|
33276
|
+
|
|
33277
|
+
// Flag indicating if features loaded by this source can be searched for by name or attribute, true by default
|
|
33278
|
+
this.searchable = config.searchable !== false;
|
|
33279
|
+
|
|
33280
|
+
}
|
|
33281
|
+
|
|
33282
|
+
async defaultVisibilityWindow() {
|
|
33283
|
+
if (this.reader && typeof this.reader.defaultVisibilityWindow === 'function') {
|
|
33284
|
+
return this.reader.defaultVisibilityWindow()
|
|
33285
|
+
}
|
|
33286
|
+
}
|
|
32805
33287
|
|
|
33288
|
+
async trackType() {
|
|
33289
|
+
const header = await this.getHeader();
|
|
33290
|
+
if (header) {
|
|
33291
|
+
return header.type
|
|
32806
33292
|
} else {
|
|
32807
|
-
return
|
|
33293
|
+
return undefined // Convention for unknown or unspecified
|
|
32808
33294
|
}
|
|
32809
33295
|
}
|
|
32810
|
-
async _getFeatures(chr, start, end, bpPerPixel, windowFunction) {
|
|
32811
|
-
const genomicInterval = new GenomicInterval(chr, start, end);
|
|
32812
|
-
const genome = this.genome;
|
|
32813
33296
|
|
|
33297
|
+
async getHeader() {
|
|
33298
|
+
if (!this.header) {
|
|
32814
33299
|
|
|
32815
|
-
|
|
32816
|
-
|
|
32817
|
-
|
|
32818
|
-
|
|
32819
|
-
|
|
32820
|
-
|
|
33300
|
+
if (this.reader && typeof this.reader.readHeader === "function") {
|
|
33301
|
+
const header = await this.reader.readHeader();
|
|
33302
|
+
if (header) {
|
|
33303
|
+
this.header = header;
|
|
33304
|
+
if (header.format) {
|
|
33305
|
+
this.config.format = header.format;
|
|
33306
|
+
}
|
|
33307
|
+
} else {
|
|
33308
|
+
this.header = {};
|
|
32821
33309
|
}
|
|
33310
|
+
} else {
|
|
33311
|
+
this.header = {};
|
|
32822
33312
|
}
|
|
32823
33313
|
}
|
|
33314
|
+
return this.header
|
|
33315
|
+
}
|
|
32824
33316
|
|
|
32825
|
-
|
|
32826
|
-
|
|
32827
|
-
|
|
32828
|
-
|
|
32829
|
-
|
|
32830
|
-
|
|
33317
|
+
/**
|
|
33318
|
+
* Required function for all data source objects. Fetches features for the
|
|
33319
|
+
* range requested.
|
|
33320
|
+
*
|
|
33321
|
+
* This function is quite complex due to the variety of reader types backing it, some indexed, some queryable,
|
|
33322
|
+
* some not.
|
|
33323
|
+
*
|
|
33324
|
+
* @param chr
|
|
33325
|
+
* @param start
|
|
33326
|
+
* @param end
|
|
33327
|
+
* @param bpPerPixel
|
|
33328
|
+
*/
|
|
33329
|
+
async getFeatures({chr, start, end, bpPerPixel, visibilityWindow, windowFunction}) {
|
|
32831
33330
|
|
|
32832
|
-
const
|
|
32833
|
-
|
|
32834
|
-
|
|
32835
|
-
|
|
33331
|
+
const isWholeGenome = ("all" === chr.toLowerCase());
|
|
33332
|
+
|
|
33333
|
+
start = start || 0;
|
|
33334
|
+
end = end || Number.MAX_SAFE_INTEGER;
|
|
33335
|
+
|
|
33336
|
+
// Various conditions that can require a feature load
|
|
33337
|
+
// * view is "whole genome" but no features are loaded
|
|
33338
|
+
// * cache is disabled
|
|
33339
|
+
// * cache does not contain requested range
|
|
33340
|
+
// const containsRange = this.featureCache.containsRange(new GenomicInterval(queryChr, start, end))
|
|
33341
|
+
if ((isWholeGenome && !this.wgFeatures && this.supportsWholeGenome()) ||
|
|
33342
|
+
this.config.disableCache ||
|
|
33343
|
+
!this.featureCache ||
|
|
33344
|
+
!this.featureCache.containsRange(new GenomicInterval(chr, start, end))) {
|
|
33345
|
+
await this.loadFeatures(chr, start, end, visibilityWindow);
|
|
32836
33346
|
}
|
|
32837
33347
|
|
|
32838
|
-
|
|
32839
|
-
|
|
32840
|
-
|
|
32841
|
-
|
|
32842
|
-
|
|
32843
|
-
|
|
32844
|
-
|
|
32845
|
-
|
|
32846
|
-
|
|
32847
|
-
|
|
32848
|
-
|
|
32849
|
-
|
|
32850
|
-
decodeVaryTile(tile, chr, start, end, bpPerPixel, features);
|
|
32851
|
-
break
|
|
32852
|
-
case "fixedStep":
|
|
32853
|
-
decodeFixedTile(tile, chr, start, end, bpPerPixel, features);
|
|
32854
|
-
break
|
|
32855
|
-
default:
|
|
32856
|
-
throw ("Unknown tile type: " + tile.type)
|
|
33348
|
+
if (isWholeGenome) {
|
|
33349
|
+
if (!this.wgFeatures) {
|
|
33350
|
+
if (this.supportsWholeGenome()) {
|
|
33351
|
+
if("wig" === this.config.type) {
|
|
33352
|
+
const allWgFeatures = await computeWGFeatures(this.featureCache.getAllFeatures(), this.genome, 1000000);
|
|
33353
|
+
this.wgFeatures = summarizeData(allWgFeatures, 0, bpPerPixel, windowFunction);
|
|
33354
|
+
} else {
|
|
33355
|
+
this.wgFeatures = await computeWGFeatures(this.featureCache.getAllFeatures(), this.genome, this.maxWGCount);
|
|
33356
|
+
}
|
|
33357
|
+
} else {
|
|
33358
|
+
this.wgFeatures = [];
|
|
33359
|
+
}
|
|
32857
33360
|
}
|
|
33361
|
+
return this.wgFeatures
|
|
33362
|
+
} else {
|
|
33363
|
+
return this.featureCache.queryFeatures(chr, start, end)
|
|
32858
33364
|
}
|
|
32859
|
-
features.sort(function (a, b) {
|
|
32860
|
-
return a.start - b.start
|
|
32861
|
-
});
|
|
32862
|
-
|
|
32863
|
-
return features
|
|
32864
33365
|
}
|
|
32865
33366
|
|
|
32866
|
-
|
|
32867
|
-
return
|
|
33367
|
+
async findFeatures(fn) {
|
|
33368
|
+
return this.featureCache ? this.featureCache.findFeatures(fn) : []
|
|
32868
33369
|
}
|
|
32869
33370
|
|
|
32870
|
-
|
|
32871
|
-
return this.
|
|
33371
|
+
supportsWholeGenome() {
|
|
33372
|
+
return !this.queryable // queryable (indexed, web services) sources don't support whole genome view
|
|
32872
33373
|
}
|
|
32873
|
-
}
|
|
32874
|
-
|
|
32875
|
-
function decodeBedTile(tile, chr, bpStart, bpEnd, bpPerPixel, features) {
|
|
32876
33374
|
|
|
32877
|
-
|
|
32878
|
-
|
|
32879
|
-
|
|
32880
|
-
|
|
32881
|
-
|
|
32882
|
-
|
|
32883
|
-
|
|
32884
|
-
if (e < bpStart) continue
|
|
32885
|
-
if (s > bpEnd) break
|
|
32886
|
-
features.push({
|
|
32887
|
-
chr: chr,
|
|
32888
|
-
start: s,
|
|
32889
|
-
end: e,
|
|
32890
|
-
value: data[i]
|
|
32891
|
-
});
|
|
33375
|
+
// TODO -- experimental, will only work for non-indexed sources
|
|
33376
|
+
getAllFeatures() {
|
|
33377
|
+
if (this.queryable || !this.featureCache) { // queryable sources don't support all features
|
|
33378
|
+
return []
|
|
33379
|
+
} else {
|
|
33380
|
+
return this.featureCache.getAllFeatures()
|
|
33381
|
+
}
|
|
32892
33382
|
}
|
|
32893
|
-
}
|
|
32894
33383
|
|
|
32895
|
-
function decodeVaryTile(tile, chr, bpStart, bpEnd, bpPerPixel, features) {
|
|
32896
33384
|
|
|
32897
|
-
|
|
32898
|
-
const starts = tile.start;
|
|
32899
|
-
const span = tile.span;
|
|
32900
|
-
const data = tile.data[0]; // Single track for now
|
|
32901
|
-
for (let i = 0; i < nPositions; i++) {
|
|
32902
|
-
const s = starts[i];
|
|
32903
|
-
const e = s + span;
|
|
32904
|
-
if (e < bpStart) continue
|
|
32905
|
-
if (s > bpEnd) break
|
|
32906
|
-
features.push({
|
|
32907
|
-
chr: chr,
|
|
32908
|
-
start: s,
|
|
32909
|
-
end: e,
|
|
32910
|
-
value: data[i]
|
|
32911
|
-
});
|
|
32912
|
-
}
|
|
32913
|
-
}
|
|
33385
|
+
async loadFeatures(chr, start, end, visibilityWindow) {
|
|
32914
33386
|
|
|
32915
|
-
|
|
33387
|
+
await this.getHeader();
|
|
32916
33388
|
|
|
32917
|
-
|
|
32918
|
-
|
|
32919
|
-
|
|
32920
|
-
const data = tile.data[0]; // Single track for now
|
|
33389
|
+
const reader = this.reader;
|
|
33390
|
+
let intervalStart = start;
|
|
33391
|
+
let intervalEnd = end;
|
|
32921
33392
|
|
|
32922
|
-
|
|
32923
|
-
|
|
32924
|
-
if (
|
|
32925
|
-
|
|
32926
|
-
|
|
32927
|
-
|
|
32928
|
-
|
|
32929
|
-
|
|
32930
|
-
|
|
32931
|
-
|
|
32932
|
-
|
|
33393
|
+
// chr aliasing
|
|
33394
|
+
let queryChr = chr;
|
|
33395
|
+
if (!this.chrAliasManager && this.reader && this.reader.sequenceNames) {
|
|
33396
|
+
this.chrAliasManager = new ChromAliasManager(this.reader.sequenceNames, this.genome);
|
|
33397
|
+
}
|
|
33398
|
+
if (this.chrAliasManager) {
|
|
33399
|
+
queryChr = await this.chrAliasManager.getAliasName(chr);
|
|
33400
|
+
}
|
|
33401
|
+
|
|
33402
|
+
// Use visibility window to potentially expand query interval.
|
|
33403
|
+
// This can save re-queries as we zoom out. Visibility window <= 0 is a special case
|
|
33404
|
+
// indicating whole chromosome should be read at once.
|
|
33405
|
+
if ((!visibilityWindow || visibilityWindow <= 0) && this.config.expandQuery !== false) {
|
|
33406
|
+
// Whole chromosome
|
|
33407
|
+
const chromosome = this.genome ? this.genome.getChromosome(queryChr) : undefined;
|
|
33408
|
+
intervalStart = 0;
|
|
33409
|
+
intervalEnd = Math.max(chromosome ? chromosome.bpLength : Number.MAX_SAFE_INTEGER, end);
|
|
33410
|
+
} else if (visibilityWindow > (end - start) && this.config.expandQuery !== false) {
|
|
33411
|
+
let expansionWindow = Math.min(4.1 * (end - start), visibilityWindow);
|
|
33412
|
+
if(this.config.minQuerySize && expansionWindow < this.config.minQuerySize) {
|
|
33413
|
+
expansionWindow = this.config.minQuerySize;
|
|
32933
33414
|
}
|
|
33415
|
+
intervalStart = Math.max(0, (start + end - expansionWindow) / 2);
|
|
33416
|
+
intervalEnd = intervalStart + expansionWindow;
|
|
32934
33417
|
}
|
|
32935
|
-
s = e;
|
|
32936
|
-
}
|
|
32937
|
-
}
|
|
32938
33418
|
|
|
33419
|
+
let features = await reader.readFeatures(queryChr, intervalStart, intervalEnd);
|
|
33420
|
+
if (this.queryable === undefined) {
|
|
33421
|
+
this.queryable = reader.indexed;
|
|
33422
|
+
}
|
|
32939
33423
|
|
|
32940
|
-
|
|
33424
|
+
const genomicInterval = this.queryable ?
|
|
33425
|
+
new GenomicInterval(chr, intervalStart, intervalEnd) :
|
|
33426
|
+
undefined;
|
|
32941
33427
|
|
|
32942
|
-
|
|
33428
|
+
if (features) {
|
|
32943
33429
|
|
|
32944
|
-
|
|
32945
|
-
|
|
32946
|
-
|
|
33430
|
+
// Assign overlapping features to rows
|
|
33431
|
+
if (this.config.format !== "wig" && this.config.type !== "junctions") {
|
|
33432
|
+
const maxRows = this.config.maxRows || Number.MAX_SAFE_INTEGER;
|
|
33433
|
+
packFeatures(features, maxRows);
|
|
33434
|
+
}
|
|
32947
33435
|
|
|
32948
|
-
|
|
33436
|
+
// Note - replacing previous cache with new one. genomicInterval is optional (might be undefined => includes all features)
|
|
33437
|
+
this.featureCache = new FeatureCache$1(features, this.genome, genomicInterval);
|
|
32949
33438
|
|
|
32950
|
-
|
|
33439
|
+
// If track is marked "searchable"< cache features by name -- use this with caution, memory intensive
|
|
33440
|
+
if (this.searchable) {
|
|
33441
|
+
this.addFeaturesToDB(features, this.config);
|
|
33442
|
+
}
|
|
33443
|
+
} else {
|
|
33444
|
+
this.featureCache = new FeatureCache$1([], genomicInterval); // Empty cache
|
|
33445
|
+
}
|
|
33446
|
+
}
|
|
33447
|
+
|
|
33448
|
+
addFeaturesToDB(featureList, config) {
|
|
33449
|
+
if (!this.featureMap) {
|
|
33450
|
+
this.featureMap = new Map();
|
|
33451
|
+
}
|
|
33452
|
+
const searchableFields = config.searchableFields || ["name", "transcript_id", "gene_id", "gene_name", "id"];
|
|
33453
|
+
for (let feature of featureList) {
|
|
33454
|
+
for (let field of searchableFields) {
|
|
33455
|
+
let key;
|
|
33456
|
+
if (typeof feature.getAttributeValue === 'function') {
|
|
33457
|
+
key = feature.getAttributeValue(field);
|
|
33458
|
+
}
|
|
33459
|
+
if (key) {
|
|
33460
|
+
key = key.replaceAll(' ', '+').toUpperCase();
|
|
33461
|
+
// If feature is already present keep largest one
|
|
33462
|
+
if (this.featureMap.has(key)) {
|
|
33463
|
+
const f2 = this.featureMap.get(key);
|
|
33464
|
+
if (feature.end - feature.start < f2.end - f2.start) {
|
|
33465
|
+
continue
|
|
33466
|
+
}
|
|
33467
|
+
}
|
|
33468
|
+
this.featureMap.set(key, feature);
|
|
33469
|
+
}
|
|
33470
|
+
}
|
|
33471
|
+
}
|
|
33472
|
+
}
|
|
33473
|
+
|
|
33474
|
+
search(term) {
|
|
33475
|
+
if (this.featureMap) {
|
|
33476
|
+
return this.featureMap.get(term.toUpperCase())
|
|
33477
|
+
}
|
|
33478
|
+
|
|
33479
|
+
}
|
|
32951
33480
|
}
|
|
32952
33481
|
|
|
32953
33482
|
/*
|
|
@@ -33971,7 +34500,11 @@ function renderFeatureLabel(ctx, feature, featureX, featureX1, featureY, referen
|
|
|
33971
34500
|
if (name === undefined) name = feature.id || feature.ID;
|
|
33972
34501
|
if (!name || name === '.') return
|
|
33973
34502
|
|
|
33974
|
-
let
|
|
34503
|
+
let pixelXOffset = options.pixelXOffset || 0;
|
|
34504
|
+
const t1 = Math.max(featureX, -pixelXOffset);
|
|
34505
|
+
const t2 = Math.min(featureX1, -pixelXOffset + options.viewportWidth);
|
|
34506
|
+
let centerX = (t1 + t2) / 2;
|
|
34507
|
+
//let centerX = (featureX + featureX1) / 2
|
|
33975
34508
|
|
|
33976
34509
|
let transform;
|
|
33977
34510
|
if (this.displayMode === "COLLAPSED" && this.labelDisplayMode === "SLANT") {
|
|
@@ -34239,7 +34772,7 @@ function renderFusionJuncSpan(feature, bpStart, xScale, pixelHeight, ctx) {
|
|
|
34239
34772
|
}
|
|
34240
34773
|
}
|
|
34241
34774
|
|
|
34242
|
-
const DEFAULT_COLOR$
|
|
34775
|
+
const DEFAULT_COLOR$1 = 'rgb(0, 0, 150)';
|
|
34243
34776
|
|
|
34244
34777
|
|
|
34245
34778
|
class FeatureTrack extends TrackBase {
|
|
@@ -34250,7 +34783,6 @@ class FeatureTrack extends TrackBase {
|
|
|
34250
34783
|
displayMode: "EXPANDED", // COLLAPSED | EXPANDED | SQUISHED
|
|
34251
34784
|
margin: 10,
|
|
34252
34785
|
featureHeight: 14,
|
|
34253
|
-
autoHeight: false,
|
|
34254
34786
|
useScore: false
|
|
34255
34787
|
}
|
|
34256
34788
|
|
|
@@ -34736,7 +35268,7 @@ class FeatureTrack extends TrackBase {
|
|
|
34736
35268
|
|
|
34737
35269
|
// If no explicit setting use the default
|
|
34738
35270
|
if (!color) {
|
|
34739
|
-
color = DEFAULT_COLOR$
|
|
35271
|
+
color = DEFAULT_COLOR$1; // Track default
|
|
34740
35272
|
}
|
|
34741
35273
|
|
|
34742
35274
|
if (feature.alpha && feature.alpha !== 1) {
|
|
@@ -34782,7 +35314,8 @@ function monitorTrackDrag(track) {
|
|
|
34782
35314
|
|
|
34783
35315
|
function onDragEnd() {
|
|
34784
35316
|
if (track.trackView && track.displayMode !== "SQUISHED") {
|
|
34785
|
-
|
|
35317
|
+
// Repaint views to adjust feature name if center is moved out of view
|
|
35318
|
+
track.trackView.repaintViews();
|
|
34786
35319
|
}
|
|
34787
35320
|
}
|
|
34788
35321
|
|
|
@@ -37863,7 +38396,6 @@ function indentLevel(str) {
|
|
|
37863
38396
|
}
|
|
37864
38397
|
|
|
37865
38398
|
const DEFAULT_GENOMES_URL = "https://igv.org/genomes/genomes.json";
|
|
37866
|
-
const BACKUP_GENOMES_URL = "https://s3.amazonaws.com/igv.org.genomes/genomes.json";
|
|
37867
38399
|
|
|
37868
38400
|
const GenomeUtils = {
|
|
37869
38401
|
|
|
@@ -37875,21 +38407,9 @@ const GenomeUtils = {
|
|
|
37875
38407
|
|
|
37876
38408
|
// Get default genomes
|
|
37877
38409
|
if (config.loadDefaultGenomes !== false) {
|
|
37878
|
-
|
|
37879
|
-
|
|
37880
|
-
|
|
37881
|
-
processJson(jsonArray);
|
|
37882
|
-
} catch (e) {
|
|
37883
|
-
console.error(e);
|
|
37884
|
-
try {
|
|
37885
|
-
const url = BACKUP_GENOMES_URL;
|
|
37886
|
-
const jsonArray = await igvxhr.loadJson(url, {});
|
|
37887
|
-
processJson(jsonArray);
|
|
37888
|
-
} catch (e) {
|
|
37889
|
-
console.error(e);
|
|
37890
|
-
console.warn("Errors loading default genome definitions.");
|
|
37891
|
-
}
|
|
37892
|
-
}
|
|
38410
|
+
const url = DEFAULT_GENOMES_URL;
|
|
38411
|
+
const jsonArray = await igvxhr.loadJson(url, {timeout: 5000});
|
|
38412
|
+
processJson(jsonArray);
|
|
37893
38413
|
}
|
|
37894
38414
|
|
|
37895
38415
|
// Add user-defined genomes
|
|
@@ -37954,7 +38474,7 @@ const GenomeUtils = {
|
|
|
37954
38474
|
}
|
|
37955
38475
|
}
|
|
37956
38476
|
|
|
37957
|
-
if(!reference) {
|
|
38477
|
+
if (!reference) {
|
|
37958
38478
|
alert.present(new Error(`Unknown genome id: ${genomeID}`), undefined);
|
|
37959
38479
|
}
|
|
37960
38480
|
}
|
|
@@ -41532,62 +42052,6 @@ const sampleNameButtonLabel =
|
|
|
41532
42052
|
</g>
|
|
41533
42053
|
</svg>`;
|
|
41534
42054
|
|
|
41535
|
-
const shim = .01;
|
|
41536
|
-
const colorStripWidth = 4;
|
|
41537
|
-
const axesXOffset = colorStripWidth + 1;
|
|
41538
|
-
function paintAxis(ctx, width, height, colorOrUndefined) {
|
|
41539
|
-
|
|
41540
|
-
if (undefined === this.dataRange || undefined === this.dataRange.max || undefined === this.dataRange.min) {
|
|
41541
|
-
return
|
|
41542
|
-
}
|
|
41543
|
-
|
|
41544
|
-
IGVGraphics.fillRect(ctx, 0, 0, width, height, { fillStyle: 'white' });
|
|
41545
|
-
if (colorOrUndefined) {
|
|
41546
|
-
IGVGraphics.fillRect(ctx, width - colorStripWidth - 2, 0, colorStripWidth, height, { fillStyle: colorOrUndefined });
|
|
41547
|
-
}
|
|
41548
|
-
|
|
41549
|
-
const flipAxis = (undefined === this.flipAxis) ? false : this.flipAxis;
|
|
41550
|
-
|
|
41551
|
-
const xTickStart = 0.95 * width - 8 - axesXOffset;
|
|
41552
|
-
const xTickEnd = 0.95 * width - axesXOffset;
|
|
41553
|
-
|
|
41554
|
-
const properties =
|
|
41555
|
-
{
|
|
41556
|
-
font: 'normal 10px Arial',
|
|
41557
|
-
textAlign: 'right',
|
|
41558
|
-
fillStyle: 'black',
|
|
41559
|
-
strokeStyle: 'black',
|
|
41560
|
-
};
|
|
41561
|
-
|
|
41562
|
-
// tick
|
|
41563
|
-
IGVGraphics.strokeLine(ctx, xTickStart, shim * height, xTickEnd, shim * height, properties);
|
|
41564
|
-
IGVGraphics.fillText(ctx, prettyPrint(flipAxis ? this.dataRange.min : this.dataRange.max), xTickStart + 4, shim * height + 12, properties);
|
|
41565
|
-
|
|
41566
|
-
const y = (1.0 - shim) * height;
|
|
41567
|
-
|
|
41568
|
-
// tick
|
|
41569
|
-
IGVGraphics.strokeLine(ctx, xTickStart, y, xTickEnd, y, properties);
|
|
41570
|
-
IGVGraphics.fillText(ctx, prettyPrint(flipAxis ? this.dataRange.max : this.dataRange.min), xTickStart + 4, y - 4, properties);
|
|
41571
|
-
|
|
41572
|
-
// vertical axis
|
|
41573
|
-
IGVGraphics.strokeLine(ctx, xTickEnd, shim * height, xTickEnd, y, properties);
|
|
41574
|
-
|
|
41575
|
-
function prettyPrint(number) {
|
|
41576
|
-
|
|
41577
|
-
if (number === 0) {
|
|
41578
|
-
return "0"
|
|
41579
|
-
} else if (Math.abs(number) >= 10) {
|
|
41580
|
-
return number.toFixed()
|
|
41581
|
-
} else if (Math.abs(number) >= 1) {
|
|
41582
|
-
return number.toFixed(1)
|
|
41583
|
-
} else if (Math.abs(number) >= 0.1) {
|
|
41584
|
-
return number.toFixed(2)
|
|
41585
|
-
} else {
|
|
41586
|
-
return number.toExponential(1)
|
|
41587
|
-
}
|
|
41588
|
-
}
|
|
41589
|
-
}
|
|
41590
|
-
|
|
41591
42055
|
/*
|
|
41592
42056
|
* The MIT License (MIT)
|
|
41593
42057
|
*
|
|
@@ -42588,7 +43052,7 @@ class TrackView {
|
|
|
42588
43052
|
const viewportsToRepaint = visibleViewports.filter(vp => vp.needsRepaint()).filter(viewport => viewport.checkZoomIn());
|
|
42589
43053
|
|
|
42590
43054
|
// Get viewports that require a data load
|
|
42591
|
-
const viewportsToReload =
|
|
43055
|
+
const viewportsToReload = visibleViewports.filter(viewport => viewport.needsReload());
|
|
42592
43056
|
|
|
42593
43057
|
// Trigger viewport to load features needed to cover current genomic range
|
|
42594
43058
|
// NOTE: these must be loaded synchronously, do not user Promise.all, not all file readers are thread safe
|
|
@@ -43183,462 +43647,6 @@ function renderSVGAxis(context, track, axisCanvas, deltaX, deltaY) {
|
|
|
43183
43647
|
|
|
43184
43648
|
}
|
|
43185
43649
|
|
|
43186
|
-
const DEFAULT_COLOR$1 = 'rgb(150, 150, 150)';
|
|
43187
|
-
|
|
43188
|
-
|
|
43189
|
-
class WigTrack extends TrackBase {
|
|
43190
|
-
|
|
43191
|
-
static defaults = {
|
|
43192
|
-
height: 50,
|
|
43193
|
-
flipAxis: false,
|
|
43194
|
-
logScale: false,
|
|
43195
|
-
windowFunction: 'mean',
|
|
43196
|
-
graphType: 'bar',
|
|
43197
|
-
normalize: undefined,
|
|
43198
|
-
scaleFactor: undefined,
|
|
43199
|
-
overflowColor: `rgb(255, 32, 255)`,
|
|
43200
|
-
baselineColor: 'lightGray',
|
|
43201
|
-
summarize: true
|
|
43202
|
-
}
|
|
43203
|
-
|
|
43204
|
-
constructor(config, browser) {
|
|
43205
|
-
super(config, browser);
|
|
43206
|
-
}
|
|
43207
|
-
|
|
43208
|
-
init(config) {
|
|
43209
|
-
|
|
43210
|
-
super.init(config);
|
|
43211
|
-
|
|
43212
|
-
this.type = "wig";
|
|
43213
|
-
this.featureType = 'numeric';
|
|
43214
|
-
this.resolutionAware = true;
|
|
43215
|
-
this.paintAxis = paintAxis;
|
|
43216
|
-
|
|
43217
|
-
const format = config.format ? config.format.toLowerCase() : config.format;
|
|
43218
|
-
if (config.featureSource) {
|
|
43219
|
-
this.featureSource = config.featureSource;
|
|
43220
|
-
delete config.featureSource;
|
|
43221
|
-
} else if ("bigwig" === format) {
|
|
43222
|
-
this.featureSource = new BWSource(config, this.browser.genome);
|
|
43223
|
-
} else if ("tdf" === format) {
|
|
43224
|
-
this.featureSource = new TDFSource(config, this.browser.genome);
|
|
43225
|
-
} else {
|
|
43226
|
-
this.featureSource = FeatureSource(config, this.browser.genome);
|
|
43227
|
-
}
|
|
43228
|
-
|
|
43229
|
-
|
|
43230
|
-
// Override autoscale default
|
|
43231
|
-
if (config.max === undefined || config.autoscale === true) {
|
|
43232
|
-
this.autoscale = true;
|
|
43233
|
-
} else {
|
|
43234
|
-
this.dataRange = {
|
|
43235
|
-
min: config.min || 0,
|
|
43236
|
-
max: config.max
|
|
43237
|
-
};
|
|
43238
|
-
}
|
|
43239
|
-
}
|
|
43240
|
-
|
|
43241
|
-
async postInit() {
|
|
43242
|
-
const header = await this.getHeader();
|
|
43243
|
-
if (this.disposed) return // This track was removed during async load
|
|
43244
|
-
if (header) this.setTrackProperties(header);
|
|
43245
|
-
}
|
|
43246
|
-
|
|
43247
|
-
async getFeatures(chr, start, end, bpPerPixel) {
|
|
43248
|
-
|
|
43249
|
-
const windowFunction = this.windowFunction;
|
|
43250
|
-
|
|
43251
|
-
const features = await this.featureSource.getFeatures({
|
|
43252
|
-
chr,
|
|
43253
|
-
start,
|
|
43254
|
-
end,
|
|
43255
|
-
bpPerPixel,
|
|
43256
|
-
visibilityWindow: this.visibilityWindow,
|
|
43257
|
-
windowFunction
|
|
43258
|
-
});
|
|
43259
|
-
if (this.normalize && this.featureSource.normalizationFactor) {
|
|
43260
|
-
const scaleFactor = this.featureSource.normalizationFactor;
|
|
43261
|
-
for (let f of features) {
|
|
43262
|
-
f.value *= scaleFactor;
|
|
43263
|
-
}
|
|
43264
|
-
}
|
|
43265
|
-
if (this.scaleFactor) {
|
|
43266
|
-
const scaleFactor = this.scaleFactor;
|
|
43267
|
-
for (let f of features) {
|
|
43268
|
-
f.value *= scaleFactor;
|
|
43269
|
-
}
|
|
43270
|
-
}
|
|
43271
|
-
|
|
43272
|
-
// Summarize features to current resolution. This needs to be done here, rather than in the "draw" function,
|
|
43273
|
-
// for group autoscale to work.
|
|
43274
|
-
if (this.summarize && ("mean" === windowFunction || "min" === windowFunction || "max" === windowFunction)) {
|
|
43275
|
-
return summarizeData(features, start, bpPerPixel, windowFunction)
|
|
43276
|
-
} else {
|
|
43277
|
-
return features
|
|
43278
|
-
}
|
|
43279
|
-
}
|
|
43280
|
-
|
|
43281
|
-
menuItemList() {
|
|
43282
|
-
const items = [];
|
|
43283
|
-
|
|
43284
|
-
if (this.flipAxis !== undefined) {
|
|
43285
|
-
items.push('<hr>');
|
|
43286
|
-
|
|
43287
|
-
function click() {
|
|
43288
|
-
this.flipAxis = !this.flipAxis;
|
|
43289
|
-
this.trackView.repaintViews();
|
|
43290
|
-
}
|
|
43291
|
-
|
|
43292
|
-
items.push({label: 'Flip y-axis', click});
|
|
43293
|
-
}
|
|
43294
|
-
|
|
43295
|
-
if(this.featureSource.windowFunctions) {
|
|
43296
|
-
items.push(...this.wigSummarizationItems());
|
|
43297
|
-
}
|
|
43298
|
-
|
|
43299
|
-
items.push(...this.numericDataMenuItems());
|
|
43300
|
-
|
|
43301
|
-
return items
|
|
43302
|
-
}
|
|
43303
|
-
|
|
43304
|
-
wigSummarizationItems() {
|
|
43305
|
-
|
|
43306
|
-
const windowFunctions = this.featureSource.windowFunctions;
|
|
43307
|
-
|
|
43308
|
-
const menuItems = [];
|
|
43309
|
-
menuItems.push('<hr/>');
|
|
43310
|
-
menuItems.push("<div>Windowing function</div>");
|
|
43311
|
-
for (const wf of windowFunctions) {
|
|
43312
|
-
const object = $$1(createCheckbox(wf, this.windowFunction === wf));
|
|
43313
|
-
|
|
43314
|
-
function clickHandler() {
|
|
43315
|
-
this.windowFunction = wf;
|
|
43316
|
-
this.trackView.updateViews();
|
|
43317
|
-
}
|
|
43318
|
-
|
|
43319
|
-
menuItems.push({object, click: clickHandler});
|
|
43320
|
-
}
|
|
43321
|
-
|
|
43322
|
-
return menuItems
|
|
43323
|
-
}
|
|
43324
|
-
|
|
43325
|
-
|
|
43326
|
-
async getHeader() {
|
|
43327
|
-
|
|
43328
|
-
if (typeof this.featureSource.getHeader === "function") {
|
|
43329
|
-
this.header = await this.featureSource.getHeader();
|
|
43330
|
-
}
|
|
43331
|
-
return this.header
|
|
43332
|
-
}
|
|
43333
|
-
|
|
43334
|
-
// TODO: refactor to igvUtils.js
|
|
43335
|
-
getScaleFactor(min, max, height, logScale) {
|
|
43336
|
-
const scale = logScale ? height / (Math.log10(max + 1) - (min <= 0 ? 0 : Math.log10(min + 1))) : height / (max - min);
|
|
43337
|
-
return scale
|
|
43338
|
-
}
|
|
43339
|
-
|
|
43340
|
-
computeYPixelValue(yValue, yScaleFactor) {
|
|
43341
|
-
return (this.flipAxis ? (yValue - this.dataRange.min) : (this.dataRange.max - yValue)) * yScaleFactor
|
|
43342
|
-
}
|
|
43343
|
-
|
|
43344
|
-
computeYPixelValueInLogScale(yValue, yScaleFactor) {
|
|
43345
|
-
let maxValue = this.dataRange.max;
|
|
43346
|
-
let minValue = this.dataRange.min;
|
|
43347
|
-
if (maxValue <= 0) return 0 // TODO:
|
|
43348
|
-
if (minValue <= -1) minValue = 0;
|
|
43349
|
-
minValue = (minValue <= 0) ? 0 : Math.log10(minValue + 1);
|
|
43350
|
-
maxValue = Math.log10(maxValue + 1);
|
|
43351
|
-
yValue = Math.log10(yValue + 1);
|
|
43352
|
-
return ((this.flipAxis ? (yValue - minValue) : (maxValue - yValue)) * yScaleFactor)
|
|
43353
|
-
}
|
|
43354
|
-
|
|
43355
|
-
draw(options) {
|
|
43356
|
-
|
|
43357
|
-
const features = options.features;
|
|
43358
|
-
const ctx = options.context;
|
|
43359
|
-
const bpPerPixel = options.bpPerPixel;
|
|
43360
|
-
const bpStart = options.bpStart;
|
|
43361
|
-
const pixelWidth = options.pixelWidth;
|
|
43362
|
-
const pixelHeight = options.pixelHeight;
|
|
43363
|
-
const bpEnd = bpStart + pixelWidth * bpPerPixel + 1;
|
|
43364
|
-
this.color || DEFAULT_COLOR$1;
|
|
43365
|
-
const scaleFactor = this.getScaleFactor(this.dataRange.min, this.dataRange.max, options.pixelHeight, this.logScale);
|
|
43366
|
-
const yScale = (yValue) => this.logScale
|
|
43367
|
-
? this.computeYPixelValueInLogScale(yValue, scaleFactor)
|
|
43368
|
-
: this.computeYPixelValue(yValue, scaleFactor);
|
|
43369
|
-
|
|
43370
|
-
if (features && features.length > 0) {
|
|
43371
|
-
|
|
43372
|
-
if (this.dataRange.min === undefined) this.dataRange.min = 0;
|
|
43373
|
-
|
|
43374
|
-
// Max can be less than min if config.min is set but max left to autoscale. If that's the case there is
|
|
43375
|
-
// nothing to paint.
|
|
43376
|
-
if (this.dataRange.max > this.dataRange.min) {
|
|
43377
|
-
|
|
43378
|
-
let lastPixelEnd = -1;
|
|
43379
|
-
let lastY;
|
|
43380
|
-
const y0 = yScale(0);
|
|
43381
|
-
|
|
43382
|
-
for (let f of features) {
|
|
43383
|
-
|
|
43384
|
-
if (f.end < bpStart) continue
|
|
43385
|
-
if (f.start > bpEnd) break
|
|
43386
|
-
|
|
43387
|
-
const x = (f.start - bpStart) / bpPerPixel;
|
|
43388
|
-
if (isNaN(x)) continue
|
|
43389
|
-
|
|
43390
|
-
let y = yScale(f.value);
|
|
43391
|
-
|
|
43392
|
-
const rectEnd = (f.end - bpStart) / bpPerPixel;
|
|
43393
|
-
const width = rectEnd - x;
|
|
43394
|
-
|
|
43395
|
-
const color = options.alpha ? IGVColor.addAlpha(this.getColorForFeature(f), options.alpha) : this.getColorForFeature(f);
|
|
43396
|
-
|
|
43397
|
-
if (this.graphType === "line") {
|
|
43398
|
-
if (lastY !== undefined) {
|
|
43399
|
-
IGVGraphics.strokeLine(ctx, lastPixelEnd, lastY, x, y, {
|
|
43400
|
-
"fillStyle": color,
|
|
43401
|
-
"strokeStyle": color
|
|
43402
|
-
});
|
|
43403
|
-
}
|
|
43404
|
-
IGVGraphics.strokeLine(ctx, x, y, x + width, y, {"fillStyle": color, "strokeStyle": color});
|
|
43405
|
-
} else if (this.graphType === "points") {
|
|
43406
|
-
const pointSize = this.config.pointSize || 3;
|
|
43407
|
-
const px = x + width / 2;
|
|
43408
|
-
IGVGraphics.fillCircle(ctx, px, y, pointSize / 2, {"fillStyle": color, "strokeStyle": color});
|
|
43409
|
-
|
|
43410
|
-
if (f.value > this.dataRange.max) {
|
|
43411
|
-
IGVGraphics.fillCircle(ctx, px, pointSize / 2, pointSize / 2, 3, {fillStyle: this.overflowColor});
|
|
43412
|
-
} else if (f.value < this.dataRange.min) {
|
|
43413
|
-
IGVGraphics.fillCircle(ctx, px, pixelHeight - pointSize / 2, pointSize / 2, 3, {fillStyle: this.overflowColor});
|
|
43414
|
-
}
|
|
43415
|
-
|
|
43416
|
-
} else {
|
|
43417
|
-
// Default graph type (bar)
|
|
43418
|
-
const height = Math.min(pixelHeight, y - y0);
|
|
43419
|
-
IGVGraphics.fillRect(ctx, x, y0, width, height, {fillStyle: color});
|
|
43420
|
-
if (f.value > this.dataRange.max) {
|
|
43421
|
-
IGVGraphics.fillRect(ctx, x, 0, width, 3, {fillStyle: this.overflowColor});
|
|
43422
|
-
} else if (f.value < this.dataRange.min) {
|
|
43423
|
-
IGVGraphics.fillRect(ctx, x, pixelHeight - 3, width, 3, {fillStyle: this.overflowColor});
|
|
43424
|
-
}
|
|
43425
|
-
|
|
43426
|
-
}
|
|
43427
|
-
lastPixelEnd = x + width;
|
|
43428
|
-
lastY = y;
|
|
43429
|
-
}
|
|
43430
|
-
|
|
43431
|
-
// If the track includes negative values draw a baseline
|
|
43432
|
-
if (this.dataRange.min < 0) {
|
|
43433
|
-
const ratio = this.dataRange.max / (this.dataRange.max - this.dataRange.min);
|
|
43434
|
-
const basepx = this.flipAxis ? (1 - ratio) * options.pixelHeight : ratio * options.pixelHeight;
|
|
43435
|
-
IGVGraphics.strokeLine(ctx, 0, basepx, options.pixelWidth, basepx, {strokeStyle: this.baselineColor});
|
|
43436
|
-
}
|
|
43437
|
-
}
|
|
43438
|
-
}
|
|
43439
|
-
|
|
43440
|
-
// Draw guidelines
|
|
43441
|
-
if (this.config.hasOwnProperty('guideLines')) {
|
|
43442
|
-
for (let line of this.config.guideLines) {
|
|
43443
|
-
if (line.hasOwnProperty('color') && line.hasOwnProperty('y') && line.hasOwnProperty('dotted')) {
|
|
43444
|
-
let y = yScale(line.y);
|
|
43445
|
-
let props = {
|
|
43446
|
-
'strokeStyle': line['color'],
|
|
43447
|
-
'strokeWidth': 2
|
|
43448
|
-
};
|
|
43449
|
-
if (line['dotted']) IGVGraphics.dashedLine(options.context, 0, y, options.pixelWidth, y, 5, props);
|
|
43450
|
-
else IGVGraphics.strokeLine(options.context, 0, y, options.pixelWidth, y, props);
|
|
43451
|
-
}
|
|
43452
|
-
}
|
|
43453
|
-
}
|
|
43454
|
-
}
|
|
43455
|
-
|
|
43456
|
-
popupData(clickState, features) {
|
|
43457
|
-
|
|
43458
|
-
if (features === undefined) features = this.clickedFeatures(clickState);
|
|
43459
|
-
|
|
43460
|
-
if (features && features.length > 0) {
|
|
43461
|
-
|
|
43462
|
-
const genomicLocation = clickState.genomicLocation;
|
|
43463
|
-
const popupData = [];
|
|
43464
|
-
|
|
43465
|
-
// Sort features based on distance from click
|
|
43466
|
-
features.sort(function (a, b) {
|
|
43467
|
-
const distA = Math.abs((a.start + a.end) / 2 - genomicLocation);
|
|
43468
|
-
const distB = Math.abs((b.start + b.end) / 2 - genomicLocation);
|
|
43469
|
-
return distA - distB
|
|
43470
|
-
});
|
|
43471
|
-
|
|
43472
|
-
// Display closest 10
|
|
43473
|
-
const displayFeatures = features.length > 10 ? features.slice(0, 10) : features;
|
|
43474
|
-
|
|
43475
|
-
// Resort in ascending order
|
|
43476
|
-
displayFeatures.sort(function (a, b) {
|
|
43477
|
-
return a.start - b.start
|
|
43478
|
-
});
|
|
43479
|
-
|
|
43480
|
-
for (let selectedFeature of displayFeatures) {
|
|
43481
|
-
if (selectedFeature) {
|
|
43482
|
-
if (popupData.length > 0) {
|
|
43483
|
-
popupData.push('<hr/>');
|
|
43484
|
-
}
|
|
43485
|
-
let posString = (selectedFeature.end - selectedFeature.start) === 1 ?
|
|
43486
|
-
numberFormatter$1(Math.floor(selectedFeature.start) + 1)
|
|
43487
|
-
: numberFormatter$1(Math.floor(selectedFeature.start) + 1) + "-" + numberFormatter$1(Math.floor(selectedFeature.end));
|
|
43488
|
-
popupData.push({name: "Position:", value: posString});
|
|
43489
|
-
popupData.push({
|
|
43490
|
-
name: "Value: ",
|
|
43491
|
-
value: numberFormatter$1(selectedFeature.value.toFixed(4))
|
|
43492
|
-
});
|
|
43493
|
-
}
|
|
43494
|
-
}
|
|
43495
|
-
if (displayFeatures.length < features.length) {
|
|
43496
|
-
popupData.push("<hr/>...");
|
|
43497
|
-
}
|
|
43498
|
-
|
|
43499
|
-
return popupData
|
|
43500
|
-
|
|
43501
|
-
} else {
|
|
43502
|
-
return []
|
|
43503
|
-
}
|
|
43504
|
-
}
|
|
43505
|
-
|
|
43506
|
-
get supportsWholeGenome() {
|
|
43507
|
-
return !this.config.indexURL && this.config.supportsWholeGenome !== false
|
|
43508
|
-
}
|
|
43509
|
-
|
|
43510
|
-
/**
|
|
43511
|
-
* Return color for feature.
|
|
43512
|
-
* @param feature
|
|
43513
|
-
* @returns {string}
|
|
43514
|
-
*/
|
|
43515
|
-
|
|
43516
|
-
getColorForFeature(f) {
|
|
43517
|
-
let c = (f.value < 0 && this.altColor) ? this.altColor : this.color || DEFAULT_COLOR$1;
|
|
43518
|
-
return (typeof c === "function") ? c(f.value) : c
|
|
43519
|
-
}
|
|
43520
|
-
|
|
43521
|
-
/**
|
|
43522
|
-
* Called when the track is removed. Do any needed cleanup here
|
|
43523
|
-
*/
|
|
43524
|
-
dispose() {
|
|
43525
|
-
this.trackView = undefined;
|
|
43526
|
-
}
|
|
43527
|
-
|
|
43528
|
-
}
|
|
43529
|
-
|
|
43530
|
-
/**
|
|
43531
|
-
* Summarize wig data in bins of size "bpPerPixel" with the given window function.
|
|
43532
|
-
*
|
|
43533
|
-
* @param features wig (numeric) data -- features cannot overlap, and are in ascending order by start position
|
|
43534
|
-
* @param startBP bp start position for computing binned data
|
|
43535
|
-
* @param bpPerPixel bp per pixel (bin)
|
|
43536
|
-
* @param windowFunction mean, min, or max
|
|
43537
|
-
* @returns {*|*[]}
|
|
43538
|
-
*/
|
|
43539
|
-
function summarizeData(features, startBP, bpPerPixel, windowFunction = "mean") {
|
|
43540
|
-
|
|
43541
|
-
if (bpPerPixel <= 1 || !features || features.length === 0) {
|
|
43542
|
-
return features
|
|
43543
|
-
}
|
|
43544
|
-
|
|
43545
|
-
// Assume features are sorted by position. Wig features cannot overlap. Note, UCSC "reductionLevel" == bpPerPixel
|
|
43546
|
-
const chr = features[0].chr;
|
|
43547
|
-
const binSize = bpPerPixel;
|
|
43548
|
-
const summaryFeatures = [];
|
|
43549
|
-
|
|
43550
|
-
const finishBin = (bin) => {
|
|
43551
|
-
const start = startBP + bin.bin * binSize;
|
|
43552
|
-
const end = start + binSize;
|
|
43553
|
-
let value;
|
|
43554
|
-
switch (windowFunction) {
|
|
43555
|
-
case "mean":
|
|
43556
|
-
value = bin.sumData / bin.count;
|
|
43557
|
-
break
|
|
43558
|
-
case "max":
|
|
43559
|
-
value = bin.max;
|
|
43560
|
-
break
|
|
43561
|
-
case "min":
|
|
43562
|
-
value = bin.min;
|
|
43563
|
-
break
|
|
43564
|
-
default:
|
|
43565
|
-
throw Error(`Unknown window function: ${windowFunction}`)
|
|
43566
|
-
}
|
|
43567
|
-
const description = `${windowFunction} of ${bin.count} values`;
|
|
43568
|
-
summaryFeatures.push({chr, start, end, value, description});
|
|
43569
|
-
};
|
|
43570
|
-
|
|
43571
|
-
let currentBinData;
|
|
43572
|
-
for (let f of features) {
|
|
43573
|
-
|
|
43574
|
-
// Loop through bins this feature overlaps, updating the weighted sum for each bin or min/max,
|
|
43575
|
-
// depending on window function
|
|
43576
|
-
let startBin = Math.floor((f.start - startBP) / binSize);
|
|
43577
|
-
const endBin = Math.floor((f.end - startBP) / binSize);
|
|
43578
|
-
|
|
43579
|
-
if (currentBinData && startBin === currentBinData.bin) {
|
|
43580
|
-
currentBinData.add(f);
|
|
43581
|
-
startBin++;
|
|
43582
|
-
}
|
|
43583
|
-
|
|
43584
|
-
if (!currentBinData || endBin > currentBinData.bin) {
|
|
43585
|
-
|
|
43586
|
-
if(currentBinData) {
|
|
43587
|
-
finishBin(currentBinData);
|
|
43588
|
-
}
|
|
43589
|
-
|
|
43590
|
-
// Feature stretches across multiple bins.
|
|
43591
|
-
if (endBin > startBin) {
|
|
43592
|
-
const end = startBP + endBin * binSize;
|
|
43593
|
-
summaryFeatures.push({chr, start: f.start, end, value: f.value});
|
|
43594
|
-
}
|
|
43595
|
-
|
|
43596
|
-
currentBinData = new SummaryBinData(endBin, f);
|
|
43597
|
-
}
|
|
43598
|
-
|
|
43599
|
-
}
|
|
43600
|
-
if(currentBinData) {
|
|
43601
|
-
finishBin(currentBinData);
|
|
43602
|
-
}
|
|
43603
|
-
|
|
43604
|
-
// Consolidate
|
|
43605
|
-
const c = [];
|
|
43606
|
-
let lastFeature = summaryFeatures[0];
|
|
43607
|
-
for (let f of summaryFeatures) {
|
|
43608
|
-
if (lastFeature.value === f.value && f.start <= lastFeature.end) {
|
|
43609
|
-
lastFeature.end = f.end;
|
|
43610
|
-
} else {
|
|
43611
|
-
c.push(lastFeature);
|
|
43612
|
-
lastFeature = f;
|
|
43613
|
-
}
|
|
43614
|
-
}
|
|
43615
|
-
c.push(lastFeature);
|
|
43616
|
-
|
|
43617
|
-
return c
|
|
43618
|
-
|
|
43619
|
-
}
|
|
43620
|
-
|
|
43621
|
-
class SummaryBinData {
|
|
43622
|
-
constructor(bin, feature) {
|
|
43623
|
-
this.bin = bin;
|
|
43624
|
-
this.sumData = feature.value;
|
|
43625
|
-
this.count = 1;
|
|
43626
|
-
this.min = feature.value;
|
|
43627
|
-
this.max = feature.value;
|
|
43628
|
-
}
|
|
43629
|
-
|
|
43630
|
-
add(feature) {
|
|
43631
|
-
this.sumData += feature.value;
|
|
43632
|
-
this.max = Math.max(feature.value, this.max);
|
|
43633
|
-
this.min = Math.min(feature.value, this.min);
|
|
43634
|
-
this.count++;
|
|
43635
|
-
}
|
|
43636
|
-
|
|
43637
|
-
get mean() {
|
|
43638
|
-
return this.sumData / this.count
|
|
43639
|
-
}
|
|
43640
|
-
}
|
|
43641
|
-
|
|
43642
43650
|
/**
|
|
43643
43651
|
*
|
|
43644
43652
|
* @param cs - object containing
|
|
@@ -48553,7 +48561,7 @@ class AlignmentContainer {
|
|
|
48553
48561
|
allAlignments() {
|
|
48554
48562
|
if (this.alignments) {
|
|
48555
48563
|
return this.alignments
|
|
48556
|
-
} else {
|
|
48564
|
+
} else if (this.packedGroups) {
|
|
48557
48565
|
const all = Array.from(this.packedGroups.values()).flatMap(group => group.rows.flatMap(row => row.alignments));
|
|
48558
48566
|
if (this.#unpacked && this.#unpacked.length > 0) {
|
|
48559
48567
|
for (let a of this.#unpacked) {
|
|
@@ -48561,6 +48569,8 @@ class AlignmentContainer {
|
|
|
48561
48569
|
}
|
|
48562
48570
|
}
|
|
48563
48571
|
return all
|
|
48572
|
+
} else {
|
|
48573
|
+
return []
|
|
48564
48574
|
}
|
|
48565
48575
|
}
|
|
48566
48576
|
|
|
@@ -48569,9 +48579,10 @@ class AlignmentContainer {
|
|
|
48569
48579
|
}
|
|
48570
48580
|
|
|
48571
48581
|
sortRows(options) {
|
|
48572
|
-
|
|
48573
|
-
|
|
48574
|
-
|
|
48582
|
+
if(this.packedGroups) {
|
|
48583
|
+
for (let group of this.packedGroups.values()) {
|
|
48584
|
+
group.sortRows(options, this);
|
|
48585
|
+
}
|
|
48575
48586
|
}
|
|
48576
48587
|
}
|
|
48577
48588
|
}
|
|
@@ -57361,23 +57372,24 @@ class AlignmentTrack extends TrackBase {
|
|
|
57361
57372
|
const y = clickState.y;
|
|
57362
57373
|
const offsetY = y - this.top;
|
|
57363
57374
|
const genomicLocation = clickState.genomicLocation;
|
|
57364
|
-
|
|
57365
|
-
|
|
57366
|
-
|
|
57367
|
-
|
|
57368
|
-
|
|
57369
|
-
|
|
57370
|
-
|
|
57371
|
-
|
|
57372
|
-
|
|
57373
|
-
|
|
57374
|
-
|
|
57375
|
-
|
|
57376
|
-
|
|
57377
|
-
|
|
57378
|
-
|
|
57379
|
-
|
|
57380
|
-
|
|
57375
|
+
|
|
57376
|
+
if(features.packedGroups) {
|
|
57377
|
+
let minGroupY = Number.MAX_VALUE;
|
|
57378
|
+
for (let group of features.packedGroups.values()) {
|
|
57379
|
+
minGroupY = Math.min(minGroupY, group.pixelTop);
|
|
57380
|
+
if (offsetY > group.pixelTop && offsetY <= group.pixelBottom) {
|
|
57381
|
+
|
|
57382
|
+
const alignmentRowHeight = this.displayMode === "SQUISHED" ?
|
|
57383
|
+
this.squishedRowHeight :
|
|
57384
|
+
this.alignmentRowHeight;
|
|
57385
|
+
|
|
57386
|
+
let packedAlignmentsIndex = Math.floor((offsetY - group.pixelTop) / alignmentRowHeight);
|
|
57387
|
+
|
|
57388
|
+
if (packedAlignmentsIndex >= 0 && packedAlignmentsIndex < group.length) {
|
|
57389
|
+
const alignmentRow = group.rows[packedAlignmentsIndex];
|
|
57390
|
+
const clicked = alignmentRow.alignments.filter(alignment => alignment.containsLocation(genomicLocation, this.showSoftClips));
|
|
57391
|
+
if (clicked.length > 0) return clicked[0]
|
|
57392
|
+
}
|
|
57381
57393
|
}
|
|
57382
57394
|
}
|
|
57383
57395
|
}
|
|
@@ -67605,8 +67617,6 @@ class VariantTrack extends TrackBase {
|
|
|
67605
67617
|
this.trackView.setTrackHeight(this.config.height || CNVPytorTrack.DEFAULT_TRACK_HEIGHT);
|
|
67606
67618
|
this.trackView.checkContentHeight();
|
|
67607
67619
|
this.trackView.updateViews();
|
|
67608
|
-
this.trackView.track.autoHeight = false;
|
|
67609
|
-
|
|
67610
67620
|
|
|
67611
67621
|
} finally {
|
|
67612
67622
|
this.trackView.stopSpinner();
|
|
@@ -68027,13 +68037,8 @@ class QTLTrack extends TrackBase {
|
|
|
68027
68037
|
this.divider = config.divider || "rgb(225,225,225)";
|
|
68028
68038
|
this.dotSize = config.dotSize || 2;
|
|
68029
68039
|
this.height = config.height || 100;
|
|
68030
|
-
this.autoHeight = false;
|
|
68031
68040
|
this.disableButtons = config.disableButtons;
|
|
68032
68041
|
|
|
68033
|
-
// Limit visibility window to 2 mb, gtex server gets flaky beyond that
|
|
68034
|
-
//this.visibilityWindow = config.visibilityWindow === undefined ?
|
|
68035
|
-
// 2000000 : config.visibilityWindow >= 0 ? Math.min(2000000, config.visibilityWindow) : 2000000
|
|
68036
|
-
|
|
68037
68042
|
this.featureSource = FeatureSource(config, this.browser.genome);
|
|
68038
68043
|
}
|
|
68039
68044
|
|
|
@@ -70512,7 +70517,7 @@ function createReferenceFrameList(loci, genome, browserFlanking, minimumBases, v
|
|
|
70512
70517
|
})
|
|
70513
70518
|
}
|
|
70514
70519
|
|
|
70515
|
-
const _version = "3.0.
|
|
70520
|
+
const _version = "3.0.4";
|
|
70516
70521
|
function version() {
|
|
70517
70522
|
return _version
|
|
70518
70523
|
}
|
|
@@ -71983,16 +71988,17 @@ class ROIMenu {
|
|
|
71983
71988
|
|
|
71984
71989
|
}
|
|
71985
71990
|
|
|
71986
|
-
async present(feature,
|
|
71987
|
-
const menuItems = this.menuItems(feature,
|
|
71991
|
+
async present(feature, roiSet, event, roiManager, columnContainer, regionElement) {
|
|
71992
|
+
const menuItems = this.menuItems(feature, roiSet, event, roiManager, columnContainer, regionElement);
|
|
71988
71993
|
this.browser.menuPopup.presentTrackContextMenu(event, menuItems);
|
|
71989
71994
|
}
|
|
71990
71995
|
|
|
71991
|
-
menuItems(feature,
|
|
71996
|
+
menuItems(feature, roiSet, event, roiManager, columnContainer, regionElement) {
|
|
71997
|
+
const items = feature.name ? [`<b>${feature.name}</b><br/>`] : [];
|
|
71998
|
+
if ('name' in roiSet) items.push(`<b>ROI Set: ${roiSet.name}</b>`);
|
|
71999
|
+
if (items.length > 0) items.push(`<hr/>`);
|
|
71992
72000
|
|
|
71993
|
-
|
|
71994
|
-
|
|
71995
|
-
if (isUserDefined) {
|
|
72001
|
+
if (roiSet.isUserDefined) {
|
|
71996
72002
|
items.push(
|
|
71997
72003
|
{
|
|
71998
72004
|
label: 'Set description ...',
|
|
@@ -72056,7 +72062,7 @@ class ROIMenu {
|
|
|
72056
72062
|
}
|
|
72057
72063
|
|
|
72058
72064
|
|
|
72059
|
-
if (isUserDefined) {
|
|
72065
|
+
if (roiSet.isUserDefined) {
|
|
72060
72066
|
items.push(
|
|
72061
72067
|
'<hr/>',
|
|
72062
72068
|
{
|
|
@@ -72341,13 +72347,17 @@ class ROIManager {
|
|
|
72341
72347
|
const [rectA, rectB] = tracks
|
|
72342
72348
|
.map(track => track.trackView.viewports[0].$viewport.get(0))
|
|
72343
72349
|
.map(element => getElementVerticalDimension(element));
|
|
72344
|
-
|
|
72350
|
+
|
|
72351
|
+
//Covers cases in which ruler and/or ideogram are hidden
|
|
72352
|
+
const heightA = rectA ? rectA.height : 0;
|
|
72353
|
+
const heightB = rectB ? rectB.height : 0;
|
|
72354
|
+
|
|
72345
72355
|
const elements = browser.columnContainer.querySelectorAll('.igv-roi-region');
|
|
72346
72356
|
|
|
72347
72357
|
const fudge = -0.5;
|
|
72348
72358
|
if (elements) {
|
|
72349
72359
|
for (const element of elements) {
|
|
72350
|
-
element.style.marginTop = `${
|
|
72360
|
+
element.style.marginTop = `${heightA + heightB + fudge}px`;
|
|
72351
72361
|
}
|
|
72352
72362
|
|
|
72353
72363
|
}
|
|
@@ -72515,7 +72525,6 @@ class ROIManager {
|
|
|
72515
72525
|
if (features) {
|
|
72516
72526
|
|
|
72517
72527
|
for (let feature of features) {
|
|
72518
|
-
|
|
72519
72528
|
const regionKey = createRegionKey(chr, feature.start, feature.end);
|
|
72520
72529
|
|
|
72521
72530
|
const {
|
|
@@ -72560,8 +72569,7 @@ class ROIManager {
|
|
|
72560
72569
|
event.stopPropagation();
|
|
72561
72570
|
|
|
72562
72571
|
translateMouseCoordinates(event, columnContainer);
|
|
72563
|
-
|
|
72564
|
-
this.roiMenu.present(feature, isUserDefined, event, this, columnContainer, regionElement);
|
|
72572
|
+
this.roiMenu.present(feature, roiSet, event, this, columnContainer, regionElement);
|
|
72565
72573
|
});
|
|
72566
72574
|
|
|
72567
72575
|
|
|
@@ -73730,7 +73738,7 @@ function generateGenomeID(config) {
|
|
|
73730
73738
|
}
|
|
73731
73739
|
}
|
|
73732
73740
|
|
|
73733
|
-
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';
|
|
73741
|
+
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';
|
|
73734
73742
|
|
|
73735
73743
|
/**
|
|
73736
73744
|
* Manages XQTL selections.
|
|
@@ -74613,6 +74621,10 @@ class Browser {
|
|
|
74613
74621
|
*/
|
|
74614
74622
|
async loadGenome(idOrConfig) {
|
|
74615
74623
|
|
|
74624
|
+
if (idOrConfig.genarkAccession) {
|
|
74625
|
+
idOrConfig.url = convertToHubURL(idOrConfig.genarkAccession);
|
|
74626
|
+
}
|
|
74627
|
+
|
|
74616
74628
|
// Translate the generic "url" field, used by clients such as igv-webapp
|
|
74617
74629
|
if (idOrConfig.url) {
|
|
74618
74630
|
if (isString$2(idOrConfig.url) && idOrConfig.url.endsWith("/hub.txt")) {
|
|
@@ -75905,7 +75917,7 @@ class Browser {
|
|
|
75905
75917
|
|
|
75906
75918
|
if (dragObject && dragObject.viewport.referenceFrame.start !== dragObject.start) {
|
|
75907
75919
|
this.updateViews();
|
|
75908
|
-
this.fireEvent('trackdragend');
|
|
75920
|
+
this.fireEvent('trackdragend', [dragObject.viewport]);
|
|
75909
75921
|
}
|
|
75910
75922
|
}
|
|
75911
75923
|
|