igv 3.2.6 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -10
- package/dist/igv.esm.js +1548 -893
- package/dist/igv.esm.min.js +9 -9
- package/dist/igv.esm.min.js.map +1 -1
- package/dist/igv.js +1548 -893
- package/dist/igv.min.js +9 -9
- package/dist/igv.min.js.map +1 -1
- package/package.json +3 -3
package/dist/igv.js
CHANGED
|
@@ -12880,6 +12880,20 @@
|
|
|
12880
12880
|
|
|
12881
12881
|
async loadAll() {
|
|
12882
12882
|
|
|
12883
|
+
|
|
12884
|
+
const pushChromosome = (current, order) => {
|
|
12885
|
+
const length = current.length || (current.offset + current.seq.length);
|
|
12886
|
+
if (!chrNameSet.has(current.chr)) {
|
|
12887
|
+
this.sequences.set(current.chr, []);
|
|
12888
|
+
this.chromosomes.set(current.chr, new Chromosome(current.chr, order, length));
|
|
12889
|
+
chrNameSet.add(current.chr);
|
|
12890
|
+
} else {
|
|
12891
|
+
const c = this.chromosomes.get(current.chr);
|
|
12892
|
+
c.bpLength = Math.max(c.bpLength, length);
|
|
12893
|
+
}
|
|
12894
|
+
this.sequences.get(current.chr).push(new SequenceSlice(current.offset, current.seq));
|
|
12895
|
+
};
|
|
12896
|
+
|
|
12883
12897
|
let data;
|
|
12884
12898
|
if (isDataURL(this.fastaURL)) {
|
|
12885
12899
|
let bytes = decodeDataURI$1(this.fastaURL);
|
|
@@ -12893,70 +12907,62 @@
|
|
|
12893
12907
|
|
|
12894
12908
|
const chrNameSet = new Set();
|
|
12895
12909
|
const lines = splitLines$2(data);
|
|
12896
|
-
const len = lines.length;
|
|
12897
|
-
let lineNo = 0;
|
|
12898
12910
|
let order = 0;
|
|
12899
|
-
let nextLine;
|
|
12900
12911
|
let current = {};
|
|
12901
|
-
|
|
12902
|
-
nextLine = lines[lineNo++].trim();
|
|
12912
|
+
for (let nextLine of lines) {
|
|
12903
12913
|
if (nextLine.startsWith("#") || nextLine.length === 0) ; else if (nextLine.startsWith(">")) {
|
|
12904
12914
|
// Start the next sequence
|
|
12905
|
-
if (current && current.seq) {
|
|
12906
|
-
pushChromosome
|
|
12915
|
+
if (current.seq && current.seq.length > 0) {
|
|
12916
|
+
pushChromosome(current, order++);
|
|
12907
12917
|
}
|
|
12918
|
+
current.seq = "";
|
|
12908
12919
|
|
|
12909
12920
|
const parts = nextLine.substr(1).split(/\s+/);
|
|
12910
12921
|
|
|
12911
|
-
|
|
12912
|
-
|
|
12913
|
-
|
|
12914
|
-
|
|
12915
|
-
|
|
12916
|
-
|
|
12917
|
-
|
|
12918
|
-
|
|
12919
|
-
|
|
12920
|
-
|
|
12921
|
-
|
|
12922
|
-
|
|
12923
|
-
|
|
12922
|
+
|
|
12923
|
+
// Check for @len= token, which is a non-standard extension supporting igv-reports.
|
|
12924
|
+
if (nextLine.includes("@len=")) {
|
|
12925
|
+
const nameParts = parts[0].split(':');
|
|
12926
|
+
current.chr = nameParts[0];
|
|
12927
|
+
if (nameParts.length > 1 && nameParts[1].indexOf('-') > 0) {
|
|
12928
|
+
|
|
12929
|
+
const locusParts = nameParts[1].split('-');
|
|
12930
|
+
if (locusParts.length === 2 &&
|
|
12931
|
+
/^[0-9]+$/.test(locusParts[0]) &&
|
|
12932
|
+
/^[0-9]+$/.test(locusParts[1])) ;
|
|
12933
|
+
const from = Number.parseInt(locusParts[0]);
|
|
12934
|
+
Number.parseInt(locusParts[1]);
|
|
12924
12935
|
current.offset = from - 1;
|
|
12925
|
-
}
|
|
12926
12936
|
|
|
12927
|
-
|
|
12928
|
-
|
|
12929
|
-
|
|
12930
|
-
|
|
12931
|
-
|
|
12937
|
+
// Check for chromosome length token
|
|
12938
|
+
if (parts.length > 1 && parts[1].startsWith("@len=")) {
|
|
12939
|
+
try {
|
|
12940
|
+
current.length = parseInt(parts[1].trim().substring(5));
|
|
12941
|
+
} catch (e) {
|
|
12942
|
+
current.length = undefined;
|
|
12943
|
+
console.error(`Error parsing sequence length for ${nextLine}`);
|
|
12944
|
+
}
|
|
12945
|
+
} else {
|
|
12932
12946
|
current.length = undefined;
|
|
12933
|
-
console.error(`Error parsing sequence length for ${nextLine}`);
|
|
12934
12947
|
}
|
|
12935
|
-
} else {
|
|
12936
|
-
current.length = undefined;
|
|
12937
12948
|
}
|
|
12949
|
+
} else {
|
|
12950
|
+
// No special tokens, a standard FASTA header
|
|
12951
|
+
current.chr = parts[0];
|
|
12952
|
+
current.offset = 0;
|
|
12938
12953
|
}
|
|
12954
|
+
|
|
12939
12955
|
} else {
|
|
12956
|
+
// Not a header or comment, so it must be sequence data
|
|
12940
12957
|
current.seq += nextLine;
|
|
12941
12958
|
}
|
|
12942
|
-
// add last seq
|
|
12943
|
-
if (current && current.seq) {
|
|
12944
|
-
pushChromosome.call(this, current, order);
|
|
12945
|
-
}
|
|
12946
12959
|
}
|
|
12947
12960
|
|
|
12948
|
-
|
|
12949
|
-
|
|
12950
|
-
|
|
12951
|
-
this.sequences.set(current.chr, []);
|
|
12952
|
-
this.chromosomes.set(current.chr, new Chromosome(current.chr, order, length));
|
|
12953
|
-
chrNameSet.add(current.chr);
|
|
12954
|
-
} else {
|
|
12955
|
-
const c = this.chromosomes.get(current.chr);
|
|
12956
|
-
c.bpLength = Math.max(c.bpLength, length);
|
|
12957
|
-
}
|
|
12958
|
-
this.sequences.get(current.chr).push(new SequenceSlice(current.offset, current.seq));
|
|
12961
|
+
// Handle the last sequence
|
|
12962
|
+
if (current.seq && current.seq.length > 0) {
|
|
12963
|
+
pushChromosome(current, order);
|
|
12959
12964
|
}
|
|
12965
|
+
|
|
12960
12966
|
}
|
|
12961
12967
|
|
|
12962
12968
|
/**
|
|
@@ -13721,17 +13727,22 @@
|
|
|
13721
13727
|
|
|
13722
13728
|
static magic = 2026540177
|
|
13723
13729
|
littleEndian = true
|
|
13730
|
+
type = 'BPTree' // Either BPTree or BPChromTree
|
|
13724
13731
|
nodeCache = new Map()
|
|
13725
13732
|
|
|
13726
|
-
static async loadBpTree(path, config, startOffset) {
|
|
13727
|
-
const bpTree = new BPTree(path, config, startOffset);
|
|
13733
|
+
static async loadBpTree(path, config, startOffset, type, loader) {
|
|
13734
|
+
const bpTree = new BPTree(path, config, startOffset, type, loader);
|
|
13728
13735
|
return bpTree.init()
|
|
13729
13736
|
}
|
|
13730
13737
|
|
|
13731
|
-
constructor(path, config, startOffset) {
|
|
13738
|
+
constructor(path, config, startOffset, type, loader) {
|
|
13732
13739
|
this.path = path;
|
|
13733
13740
|
this.config = config;
|
|
13734
13741
|
this.startOffset = startOffset;
|
|
13742
|
+
if(type) {
|
|
13743
|
+
this.type = type;
|
|
13744
|
+
}
|
|
13745
|
+
this.loader = loader || igvxhr;
|
|
13735
13746
|
}
|
|
13736
13747
|
|
|
13737
13748
|
async init() {
|
|
@@ -13757,69 +13768,22 @@
|
|
|
13757
13768
|
return this
|
|
13758
13769
|
}
|
|
13759
13770
|
|
|
13760
|
-
|
|
13761
|
-
|
|
13771
|
+
getItemCount() {
|
|
13762
13772
|
if(!this.header) {
|
|
13763
|
-
|
|
13773
|
+
throw Error("Header not initialized")
|
|
13764
13774
|
}
|
|
13775
|
+
return this.header.itemCount
|
|
13776
|
+
}
|
|
13765
13777
|
|
|
13766
|
-
|
|
13778
|
+
async search(term) {
|
|
13767
13779
|
|
|
13768
|
-
if
|
|
13769
|
-
|
|
13780
|
+
if(!this.header) {
|
|
13781
|
+
await this.init();
|
|
13770
13782
|
}
|
|
13771
13783
|
|
|
13772
|
-
const readTreeNode = async (offset) => {
|
|
13773
|
-
|
|
13774
|
-
if (this.nodeCache.has(offset)) {
|
|
13775
|
-
return this.nodeCache.get(offset)
|
|
13776
|
-
} else {
|
|
13777
|
-
|
|
13778
|
-
let binaryParser = await this.#getParserFor(offset, 4);
|
|
13779
|
-
const type = binaryParser.getByte();
|
|
13780
|
-
binaryParser.getByte();
|
|
13781
|
-
const count = binaryParser.getUShort();
|
|
13782
|
-
const items = [];
|
|
13783
|
-
|
|
13784
|
-
if (type === 1) {
|
|
13785
|
-
// Leaf node
|
|
13786
|
-
const size = count * (keySize + valSize);
|
|
13787
|
-
binaryParser = await this.#getParserFor(offset + 4, size);
|
|
13788
|
-
for (let i = 0; i < count; i++) {
|
|
13789
|
-
const key = binaryParser.getFixedLengthString(keySize);
|
|
13790
|
-
const offset = binaryParser.getLong();
|
|
13791
|
-
|
|
13792
|
-
let value;
|
|
13793
|
-
if (valSize === 16) {
|
|
13794
|
-
const length = binaryParser.getInt();
|
|
13795
|
-
binaryParser.getInt();
|
|
13796
|
-
value = {offset, length};
|
|
13797
|
-
} else {
|
|
13798
|
-
value = {offset};
|
|
13799
|
-
}
|
|
13800
|
-
items.push({key, value});
|
|
13801
|
-
}
|
|
13802
|
-
} else {
|
|
13803
|
-
// Non leaf node
|
|
13804
|
-
const size = count * (keySize + 8);
|
|
13805
|
-
binaryParser = await this.#getParserFor(offset + 4, size);
|
|
13806
|
-
|
|
13807
|
-
for (let i = 0; i < count; i++) {
|
|
13808
|
-
const key = binaryParser.getFixedLengthString(keySize);
|
|
13809
|
-
const offset = binaryParser.getLong();
|
|
13810
|
-
items.push({key, offset});
|
|
13811
|
-
}
|
|
13812
|
-
}
|
|
13813
|
-
|
|
13814
|
-
const node = {type, count, items};
|
|
13815
|
-
this.nodeCache.set(offset, node);
|
|
13816
|
-
return node
|
|
13817
|
-
}
|
|
13818
|
-
};
|
|
13819
|
-
|
|
13820
13784
|
const walkTreeNode = async (offset) => {
|
|
13821
13785
|
|
|
13822
|
-
const node = await readTreeNode(offset);
|
|
13786
|
+
const node = await this.readTreeNode(offset);
|
|
13823
13787
|
|
|
13824
13788
|
if (node.type === 1) {
|
|
13825
13789
|
// Leaf node
|
|
@@ -13850,9 +13814,66 @@
|
|
|
13850
13814
|
return walkTreeNode(this.header.nodeOffset)
|
|
13851
13815
|
}
|
|
13852
13816
|
|
|
13817
|
+
async readTreeNode (offset) {
|
|
13818
|
+
|
|
13819
|
+
if (this.nodeCache.has(offset)) {
|
|
13820
|
+
return this.nodeCache.get(offset)
|
|
13821
|
+
} else {
|
|
13822
|
+
let binaryParser = await this.#getParserFor(offset, 4);
|
|
13823
|
+
const type = binaryParser.getByte();
|
|
13824
|
+
binaryParser.getByte();
|
|
13825
|
+
const count = binaryParser.getUShort();
|
|
13826
|
+
const items = [];
|
|
13827
|
+
|
|
13828
|
+
const {keySize, valSize} = this.header;
|
|
13829
|
+
|
|
13830
|
+
if (type === 1) {
|
|
13831
|
+
// Leaf node
|
|
13832
|
+
const size = count * (keySize + valSize);
|
|
13833
|
+
binaryParser = await this.#getParserFor(offset + 4, size);
|
|
13834
|
+
for (let i = 0; i < count; i++) {
|
|
13835
|
+
const key = binaryParser.getFixedLengthString(keySize);
|
|
13836
|
+
let value;
|
|
13837
|
+
if(this.type === 'BPChromTree') {
|
|
13838
|
+
const id = binaryParser.getInt();
|
|
13839
|
+
const size = binaryParser.getInt();
|
|
13840
|
+
value = {id, size};
|
|
13841
|
+
} else {
|
|
13842
|
+
const offset = binaryParser.getLong();
|
|
13843
|
+
if (valSize === 16) {
|
|
13844
|
+
const length = binaryParser.getLong();
|
|
13845
|
+
value = {offset, length};
|
|
13846
|
+
} else {
|
|
13847
|
+
value = {offset};
|
|
13848
|
+
}
|
|
13849
|
+
}
|
|
13850
|
+
items.push({key, value});
|
|
13851
|
+
}
|
|
13852
|
+
} else {
|
|
13853
|
+
// Non leaf node
|
|
13854
|
+
const size = count * (keySize + 8);
|
|
13855
|
+
binaryParser = await this.#getParserFor(offset + 4, size);
|
|
13856
|
+
|
|
13857
|
+
for (let i = 0; i < count; i++) {
|
|
13858
|
+
const key = binaryParser.getFixedLengthString(keySize);
|
|
13859
|
+
const offset = binaryParser.getLong();
|
|
13860
|
+
items.push({key, offset});
|
|
13861
|
+
}
|
|
13862
|
+
}
|
|
13863
|
+
|
|
13864
|
+
const node = {type, count, items};
|
|
13865
|
+
this.nodeCache.set(offset, node);
|
|
13866
|
+
return node
|
|
13867
|
+
}
|
|
13868
|
+
}
|
|
13869
|
+
|
|
13853
13870
|
async #getParserFor(start, size) {
|
|
13854
|
-
|
|
13855
|
-
|
|
13871
|
+
try {
|
|
13872
|
+
const data = await this.loader.loadArrayBuffer(this.path, buildOptions(this.config, {range: {start, size}}));
|
|
13873
|
+
return new BinaryParser$1(new DataView(data), this.littleEndian)
|
|
13874
|
+
} catch (e) {
|
|
13875
|
+
console.error(e);
|
|
13876
|
+
}
|
|
13856
13877
|
}
|
|
13857
13878
|
|
|
13858
13879
|
}
|
|
@@ -13878,6 +13899,7 @@
|
|
|
13878
13899
|
|
|
13879
13900
|
littleEndian
|
|
13880
13901
|
metaIndex = new Map()
|
|
13902
|
+
chromosomeNames
|
|
13881
13903
|
|
|
13882
13904
|
constructor(config) {
|
|
13883
13905
|
this.url = config.twoBitURL || config.fastaURL;
|
|
@@ -13970,9 +13992,16 @@
|
|
|
13970
13992
|
return sequenceBases
|
|
13971
13993
|
}
|
|
13972
13994
|
|
|
13995
|
+
/**
|
|
13996
|
+
* Read the internal index of the 2bit file. This is a list of sequence names and their offsets in the file.
|
|
13997
|
+
*
|
|
13998
|
+
* @returns {Promise<Map<any, any>>}
|
|
13999
|
+
* @private
|
|
14000
|
+
*/
|
|
13973
14001
|
async _readIndex() {
|
|
13974
14002
|
|
|
13975
14003
|
const index = new Map();
|
|
14004
|
+
this.chromosomeNames = [];
|
|
13976
14005
|
|
|
13977
14006
|
const loadRange = {start: 0, size: 64};
|
|
13978
14007
|
let arrayBuffer = await igvxhr.loadArrayBuffer(this.url, {range: loadRange});
|
|
@@ -14025,6 +14054,8 @@
|
|
|
14025
14054
|
index.set(name, offset);
|
|
14026
14055
|
|
|
14027
14056
|
estNameLength = Math.floor(estNameLength * (i / (i + 1)) + name.length / (i + 1));
|
|
14057
|
+
|
|
14058
|
+
this.chromosomeNames.push(name);
|
|
14028
14059
|
}
|
|
14029
14060
|
return index
|
|
14030
14061
|
}
|
|
@@ -22170,84 +22201,6 @@
|
|
|
22170
22201
|
return Math.ceil(Math.log(Math.max(0, (chrSize / (bpPerPixel * 700)))) / log2)
|
|
22171
22202
|
}
|
|
22172
22203
|
|
|
22173
|
-
/**
|
|
22174
|
-
* A ChromTree parses a UCSC bigbed/bigwig "chromosomeTree" section of the header to produce 2 maps,
|
|
22175
|
-
* (1) ID -> chromosome names, and its
|
|
22176
|
-
* (2) chromsome name -> ID
|
|
22177
|
-
*
|
|
22178
|
-
* Both maps are needed by IGV
|
|
22179
|
-
*
|
|
22180
|
-
* The chromosome tree is a B+ index, but is located continguously in memory in the header section of the file. This
|
|
22181
|
-
* makes it feasible to parse the whole tree with data from a single fetch. In the end the tree is discarded
|
|
22182
|
-
* leaving only the mapps.
|
|
22183
|
-
*/
|
|
22184
|
-
class ChromTree {
|
|
22185
|
-
|
|
22186
|
-
constructor(header, nameToID, valueToKey, sumLengths) {
|
|
22187
|
-
this.header = header;
|
|
22188
|
-
this.nameToId = nameToID;
|
|
22189
|
-
this.idToName = valueToKey;
|
|
22190
|
-
this.sumLengths = sumLengths;
|
|
22191
|
-
}
|
|
22192
|
-
|
|
22193
|
-
static parseTree(binaryParser, startOffset, genome = false) {
|
|
22194
|
-
{
|
|
22195
|
-
const magic = binaryParser.getInt();
|
|
22196
|
-
const blockSize = binaryParser.getInt();
|
|
22197
|
-
const keySize = binaryParser.getInt();
|
|
22198
|
-
const valSize = binaryParser.getInt();
|
|
22199
|
-
const itemCount = binaryParser.getLong();
|
|
22200
|
-
const reserved = binaryParser.getLong();
|
|
22201
|
-
|
|
22202
|
-
const header = {magic, blockSize, keySize, valSize, itemCount, reserved};
|
|
22203
|
-
const nameToId = new Map();
|
|
22204
|
-
const idToName = [];
|
|
22205
|
-
let sumLengths = 0;
|
|
22206
|
-
const readTreeNode = (offset) => {
|
|
22207
|
-
if (offset >= 0) binaryParser.position = offset;
|
|
22208
|
-
const type = binaryParser.getByte();
|
|
22209
|
-
binaryParser.getByte();
|
|
22210
|
-
const count = binaryParser.getUShort();
|
|
22211
|
-
|
|
22212
|
-
if (type === 1) {
|
|
22213
|
-
// Leaf node
|
|
22214
|
-
for (let i = 0; i < count; i++) {
|
|
22215
|
-
let key = binaryParser.getFixedLengthString(keySize);
|
|
22216
|
-
let value;
|
|
22217
|
-
if (valSize === 8) {
|
|
22218
|
-
value = binaryParser.getInt();
|
|
22219
|
-
const chromSize = binaryParser.getInt();
|
|
22220
|
-
sumLengths += chromSize;
|
|
22221
|
-
if (genome) key = genome.getChromosomeName(key); // Translate to canonical chr name
|
|
22222
|
-
nameToId.set(key, value);
|
|
22223
|
-
idToName[value] = key;
|
|
22224
|
-
|
|
22225
|
-
} else {
|
|
22226
|
-
throw Error(`Unexpected "valSize" value in chromosome tree. Expected 8, actual value is ${valSize}`)
|
|
22227
|
-
}
|
|
22228
|
-
}
|
|
22229
|
-
} else {
|
|
22230
|
-
// non-leaf
|
|
22231
|
-
for (let i = 0; i < count; i++) {
|
|
22232
|
-
binaryParser.getFixedLengthString(keySize);
|
|
22233
|
-
const childOffset = binaryParser.getLong();
|
|
22234
|
-
const bufferOffset = childOffset - startOffset;
|
|
22235
|
-
const currOffset = binaryParser.position;
|
|
22236
|
-
readTreeNode(bufferOffset);
|
|
22237
|
-
binaryParser.position = currOffset;
|
|
22238
|
-
}
|
|
22239
|
-
}
|
|
22240
|
-
};
|
|
22241
|
-
|
|
22242
|
-
// Recursively walk tree to populate dictionary
|
|
22243
|
-
readTreeNode( -1);
|
|
22244
|
-
|
|
22245
|
-
return new ChromTree(header, nameToId, idToName, sumLengths)
|
|
22246
|
-
}
|
|
22247
|
-
}
|
|
22248
|
-
|
|
22249
|
-
}
|
|
22250
|
-
|
|
22251
22204
|
const RPTREE_HEADER_SIZE = 48;
|
|
22252
22205
|
const RPTREE_NODE_LEAF_ITEM_SIZE = 32; // leaf item size
|
|
22253
22206
|
const RPTREE_NODE_CHILD_ITEM_SIZE = 24; // child item size
|
|
@@ -22258,11 +22211,12 @@
|
|
|
22258
22211
|
littleEndian = true
|
|
22259
22212
|
nodeCache = new Map()
|
|
22260
22213
|
|
|
22261
|
-
constructor(path, config, startOffset) {
|
|
22214
|
+
constructor(path, config, startOffset, loader) {
|
|
22262
22215
|
|
|
22263
22216
|
this.path = path;
|
|
22264
22217
|
this.config = config;
|
|
22265
22218
|
this.startOffset = startOffset;
|
|
22219
|
+
this.loader = loader || igvxhr;
|
|
22266
22220
|
}
|
|
22267
22221
|
|
|
22268
22222
|
|
|
@@ -22306,7 +22260,7 @@
|
|
|
22306
22260
|
}
|
|
22307
22261
|
|
|
22308
22262
|
async #getParserFor(start, size) {
|
|
22309
|
-
const data = await
|
|
22263
|
+
const data = await this.loader.loadArrayBuffer(this.path, buildOptions(this.config, {range: {start, size}}));
|
|
22310
22264
|
return new BinaryParser$1(new DataView(data), this.littleEndian)
|
|
22311
22265
|
}
|
|
22312
22266
|
|
|
@@ -22663,6 +22617,165 @@
|
|
|
22663
22617
|
}
|
|
22664
22618
|
}
|
|
22665
22619
|
|
|
22620
|
+
class ChromTree {
|
|
22621
|
+
|
|
22622
|
+
nameToId = new Map()
|
|
22623
|
+
idToName = new Map()
|
|
22624
|
+
|
|
22625
|
+
constructor(path, config, startOffset, loader) {
|
|
22626
|
+
this.path = path;
|
|
22627
|
+
this.config = config;
|
|
22628
|
+
this.startOffset = startOffset;
|
|
22629
|
+
|
|
22630
|
+
this.bpTree = new BPTree(path, config, startOffset, 'BPChromTree', loader);
|
|
22631
|
+
}
|
|
22632
|
+
|
|
22633
|
+
async init() {
|
|
22634
|
+
return this.bpTree.init()
|
|
22635
|
+
}
|
|
22636
|
+
|
|
22637
|
+
getItemCount() {
|
|
22638
|
+
return this.bpTree.getItemCount()
|
|
22639
|
+
}
|
|
22640
|
+
|
|
22641
|
+
/**
|
|
22642
|
+
* Return the chromosome ID for the given name. This is the internal chromosome ID for the parent BB file only.
|
|
22643
|
+
* @param {string} chr - The chromosome name.
|
|
22644
|
+
* @returns {number|null} - The chromosome ID or null if not found.
|
|
22645
|
+
*/
|
|
22646
|
+
async getIdForName(chr) {
|
|
22647
|
+
if (this.nameToId.has(chr)) {
|
|
22648
|
+
return this.nameToId.get(chr)
|
|
22649
|
+
} else {
|
|
22650
|
+
try {
|
|
22651
|
+
const result = await this.bpTree.search(chr);
|
|
22652
|
+
if (result) {
|
|
22653
|
+
const id = result.id;
|
|
22654
|
+
this.nameToId.set(chr, id);
|
|
22655
|
+
return id
|
|
22656
|
+
} else {
|
|
22657
|
+
return
|
|
22658
|
+
}
|
|
22659
|
+
} catch (error) {
|
|
22660
|
+
throw new Error(error)
|
|
22661
|
+
}
|
|
22662
|
+
}
|
|
22663
|
+
}
|
|
22664
|
+
|
|
22665
|
+
/**
|
|
22666
|
+
* Return the chromosome name for the given ID. This is a potentially expensive operation as it involves
|
|
22667
|
+
* walking the tree until the leaf item for the given name is found. Currently it is used in only 2
|
|
22668
|
+
* situations:
|
|
22669
|
+
* (1) decoding features from a bigbed search-by-name query
|
|
22670
|
+
* (2) decoding bigwig data from the whole genome view
|
|
22671
|
+
* @param {number} id
|
|
22672
|
+
* @return {string|null}
|
|
22673
|
+
*/
|
|
22674
|
+
async getNameForId(id) {
|
|
22675
|
+
if (this.idToName.has(id)) {
|
|
22676
|
+
return this.idToName.get(id)
|
|
22677
|
+
} else {
|
|
22678
|
+
const name = await this.searchForName(id);
|
|
22679
|
+
if (name !== null) {
|
|
22680
|
+
this.idToName.set(id, name);
|
|
22681
|
+
return name
|
|
22682
|
+
}
|
|
22683
|
+
}
|
|
22684
|
+
return null
|
|
22685
|
+
}
|
|
22686
|
+
|
|
22687
|
+
/**
|
|
22688
|
+
* Perform a reverse search by traversing the tree starting at the given offset. This is potentially expensive
|
|
22689
|
+
* as it traverses the tree to find the name corresponding to the given ID. It shoud not be used for large trees.
|
|
22690
|
+
*
|
|
22691
|
+
* @param {number} id - The ID to search for.
|
|
22692
|
+
* @returns {string|null} - The name corresponding to the ID, or null if not found.
|
|
22693
|
+
*/
|
|
22694
|
+
async searchForName(id) {
|
|
22695
|
+
|
|
22696
|
+
const reverseSearch = async (offset, id) => {
|
|
22697
|
+
|
|
22698
|
+
const node = await this.bpTree.readTreeNode(offset);
|
|
22699
|
+
|
|
22700
|
+
let found = null;
|
|
22701
|
+
|
|
22702
|
+
if (node.type === 1) {
|
|
22703
|
+
// Leaf node
|
|
22704
|
+
for (const item of node.items) {
|
|
22705
|
+
const key = item.key;
|
|
22706
|
+
const itemId = item.value.id;
|
|
22707
|
+
if (itemId === id) {
|
|
22708
|
+
found = key;
|
|
22709
|
+
}
|
|
22710
|
+
// Cache the name and ID for future lookups
|
|
22711
|
+
this.nameToId.set(key, itemId);
|
|
22712
|
+
this.idToName.set(id, itemId);
|
|
22713
|
+
}
|
|
22714
|
+
return found
|
|
22715
|
+
} else {
|
|
22716
|
+
// Non-leaf node
|
|
22717
|
+
for (const item of node.items) {
|
|
22718
|
+
found = await reverseSearch.call(this, item.offset, id);
|
|
22719
|
+
if (found !== null) {
|
|
22720
|
+
break
|
|
22721
|
+
}
|
|
22722
|
+
}
|
|
22723
|
+
}
|
|
22724
|
+
return found
|
|
22725
|
+
};
|
|
22726
|
+
|
|
22727
|
+
try {
|
|
22728
|
+
return reverseSearch.call(this, this.startOffset + 32, id)
|
|
22729
|
+
} catch (error) {
|
|
22730
|
+
throw new Error(error)
|
|
22731
|
+
}
|
|
22732
|
+
}
|
|
22733
|
+
|
|
22734
|
+
/**
|
|
22735
|
+
* Return an estimated length of the genome, which might be the actual length if the number of contigs is small.
|
|
22736
|
+
* This is only used for calculating a default feature visibility window.
|
|
22737
|
+
*
|
|
22738
|
+
* @return {number}
|
|
22739
|
+
*/
|
|
22740
|
+
async estimateGenomeSize() {
|
|
22741
|
+
try {
|
|
22742
|
+
const runningTotal = {total: 0, count: 0};
|
|
22743
|
+
await this.accumulateSize(this.startOffset + 32, runningTotal, 10000);
|
|
22744
|
+
const itemCount = this.getItemCount();
|
|
22745
|
+
return (itemCount / runningTotal.count) * runningTotal.total
|
|
22746
|
+
|
|
22747
|
+
} catch (error) {
|
|
22748
|
+
console.error("Error estimating genome size", error);
|
|
22749
|
+
return -1
|
|
22750
|
+
}
|
|
22751
|
+
}
|
|
22752
|
+
|
|
22753
|
+
async accumulateSize(offset, runningTotal, maxCount) {
|
|
22754
|
+
|
|
22755
|
+
const node = await this.bpTree.readTreeNode(offset);
|
|
22756
|
+
|
|
22757
|
+
if (node.type === 1) {
|
|
22758
|
+
// Leaf node
|
|
22759
|
+
for (const item of node.items) {
|
|
22760
|
+
const value = item.value;
|
|
22761
|
+
runningTotal.total += value.size;
|
|
22762
|
+
runningTotal.count += 1;
|
|
22763
|
+
}
|
|
22764
|
+
} else {
|
|
22765
|
+
// Non-leaf node. Items are visited in random order to avoid biasing the estimate
|
|
22766
|
+
const shuffledItems = node.items.slice().sort(() => Math.random() - 0.5);
|
|
22767
|
+
for (const item of shuffledItems) {
|
|
22768
|
+
await this.accumulateSize(item.offset, runningTotal, maxCount);
|
|
22769
|
+
if (runningTotal.count > maxCount) {
|
|
22770
|
+
break
|
|
22771
|
+
}
|
|
22772
|
+
}
|
|
22773
|
+
}
|
|
22774
|
+
return runningTotal
|
|
22775
|
+
}
|
|
22776
|
+
|
|
22777
|
+
}
|
|
22778
|
+
|
|
22666
22779
|
/*
|
|
22667
22780
|
* The MIT License (MIT)
|
|
22668
22781
|
*
|
|
@@ -22726,14 +22839,40 @@
|
|
|
22726
22839
|
async preload() {
|
|
22727
22840
|
const data = await igvxhr.loadArrayBuffer(this.path);
|
|
22728
22841
|
this.loader = new DataBuffer(data);
|
|
22842
|
+
for (let rpTree of this.rpTreeCache.values()) {
|
|
22843
|
+
rpTree.loader = this.loader;
|
|
22844
|
+
}
|
|
22845
|
+
if (this._searchTrees) {
|
|
22846
|
+
for (let bpTree of this._searchTrees) {
|
|
22847
|
+
bpTree.loader = this.loader;
|
|
22848
|
+
}
|
|
22849
|
+
}
|
|
22729
22850
|
}
|
|
22730
22851
|
|
|
22731
|
-
async readWGFeatures(bpPerPixel, windowFunction) {
|
|
22852
|
+
async readWGFeatures(wgChromosomeNames, bpPerPixel, windowFunction) {
|
|
22853
|
+
|
|
22732
22854
|
await this.loadHeader();
|
|
22733
|
-
|
|
22734
|
-
|
|
22735
|
-
|
|
22736
|
-
|
|
22855
|
+
// Convert the logic to JavaScript
|
|
22856
|
+
let minID = Number.MAX_SAFE_INTEGER;
|
|
22857
|
+
let maxID = -1;
|
|
22858
|
+
let chr1;
|
|
22859
|
+
let chr2;
|
|
22860
|
+
|
|
22861
|
+
for (const chr of wgChromosomeNames) {
|
|
22862
|
+
const id = await this.getIdForChr(chr);
|
|
22863
|
+
if (id === null || id === undefined) {
|
|
22864
|
+
continue
|
|
22865
|
+
}
|
|
22866
|
+
if (id < minID) {
|
|
22867
|
+
minID = id;
|
|
22868
|
+
chr1 = chr;
|
|
22869
|
+
}
|
|
22870
|
+
if (id > maxID) {
|
|
22871
|
+
maxID = id;
|
|
22872
|
+
chr2 = chr;
|
|
22873
|
+
}
|
|
22874
|
+
}
|
|
22875
|
+
|
|
22737
22876
|
return this.readFeatures(chr1, 0, chr2, Number.MAX_VALUE, bpPerPixel, windowFunction)
|
|
22738
22877
|
}
|
|
22739
22878
|
|
|
@@ -22744,8 +22883,8 @@
|
|
|
22744
22883
|
|
|
22745
22884
|
await this.loadHeader();
|
|
22746
22885
|
|
|
22747
|
-
|
|
22748
|
-
|
|
22886
|
+
const chrIdx1 = await this.getIdForChr(chr1);
|
|
22887
|
+
const chrIdx2 = await this.getIdForChr(chr2);
|
|
22749
22888
|
|
|
22750
22889
|
if (chrIdx1 === undefined || chrIdx2 === undefined) {
|
|
22751
22890
|
return []
|
|
@@ -22804,7 +22943,7 @@
|
|
|
22804
22943
|
} else {
|
|
22805
22944
|
plain = uint8Array;
|
|
22806
22945
|
}
|
|
22807
|
-
decodeFunction.call(this, new DataView(plain.buffer), chrIdx1, bpStart, chrIdx2, bpEnd, features,
|
|
22946
|
+
await decodeFunction.call(this, new DataView(plain.buffer), chrIdx1, bpStart, chrIdx2, bpEnd, features, windowFunction);
|
|
22808
22947
|
}
|
|
22809
22948
|
|
|
22810
22949
|
features.sort(function (a, b) {
|
|
@@ -22821,29 +22960,30 @@
|
|
|
22821
22960
|
* @param chr
|
|
22822
22961
|
* @returns {Promise<*>}
|
|
22823
22962
|
*/
|
|
22824
|
-
async
|
|
22963
|
+
async getIdForChr(chr) {
|
|
22825
22964
|
|
|
22826
22965
|
if (this.chrAliasTable.has(chr)) {
|
|
22827
22966
|
chr = this.chrAliasTable.get(chr);
|
|
22828
|
-
if (chr
|
|
22967
|
+
if (!chr) {
|
|
22829
22968
|
return undefined
|
|
22830
22969
|
}
|
|
22831
22970
|
}
|
|
22832
22971
|
|
|
22833
|
-
let chrIdx = this.chromTree.
|
|
22972
|
+
let chrIdx = await this.chromTree.getIdForName(chr);
|
|
22834
22973
|
|
|
22835
22974
|
// Try alias
|
|
22836
22975
|
if (chrIdx === undefined && this.genome) {
|
|
22837
22976
|
const aliasRecord = await this.genome.getAliasRecord(chr);
|
|
22838
22977
|
let alias;
|
|
22839
22978
|
if (aliasRecord) {
|
|
22840
|
-
|
|
22841
|
-
|
|
22842
|
-
|
|
22843
|
-
|
|
22844
|
-
|
|
22845
|
-
|
|
22846
|
-
|
|
22979
|
+
for (let k of Object.keys(aliasRecord)) {
|
|
22980
|
+
if (k === "start" || k === "end") continue
|
|
22981
|
+
alias = aliasRecord[k];
|
|
22982
|
+
if (alias === chr) continue // Already tried this
|
|
22983
|
+
chrIdx = await this.chromTree.getIdForName(alias);
|
|
22984
|
+
if (chrIdx !== undefined) {
|
|
22985
|
+
break
|
|
22986
|
+
}
|
|
22847
22987
|
}
|
|
22848
22988
|
}
|
|
22849
22989
|
this.chrAliasTable.set(chr, alias); // alias may be undefined => no alias exists. Setting prevents repeated attempts
|
|
@@ -22930,7 +23070,8 @@
|
|
|
22930
23070
|
this.header.extraIndexOffsets.length > 0) {
|
|
22931
23071
|
this._searchTrees = [];
|
|
22932
23072
|
for (let offset of this.header.extraIndexOffsets) {
|
|
22933
|
-
const
|
|
23073
|
+
const type = undefined;
|
|
23074
|
+
const bpTree = await BPTree.loadBpTree(this.path, this.config, offset, type, this.loader);
|
|
22934
23075
|
this._searchTrees.push(bpTree);
|
|
22935
23076
|
}
|
|
22936
23077
|
}
|
|
@@ -23047,15 +23188,13 @@
|
|
|
23047
23188
|
this.totalSummary = new BWTotalSummary(extHeaderParser);
|
|
23048
23189
|
}
|
|
23049
23190
|
|
|
23050
|
-
|
|
23051
|
-
|
|
23052
|
-
this.chromTree = await this.#readChromTree(header.chromTreeOffset, bufferSize);
|
|
23053
|
-
this.chrNames = new Set(this.chromTree.idToName);
|
|
23191
|
+
this.chromTree = new ChromTree(this.path, this.config, header.chromTreeOffset, this.loader);
|
|
23192
|
+
await this.chromTree.init();
|
|
23054
23193
|
|
|
23055
23194
|
// Estimate feature density from dataCount (bigbed only)
|
|
23056
|
-
if("bigbed" === this.type) {
|
|
23195
|
+
if ("bigbed" === this.type) {
|
|
23057
23196
|
const dataCount = await this.#readDataCount(header.fullDataOffset);
|
|
23058
|
-
this.featureDensity = dataCount / this.chromTree.
|
|
23197
|
+
this.featureDensity = dataCount / await this.chromTree.estimateGenomeSize();
|
|
23059
23198
|
}
|
|
23060
23199
|
|
|
23061
23200
|
this.header = header;
|
|
@@ -23079,38 +23218,6 @@
|
|
|
23079
23218
|
return binaryParser.getInt()
|
|
23080
23219
|
}
|
|
23081
23220
|
|
|
23082
|
-
/**
|
|
23083
|
-
* Used when the chromTreeOffset is > fullDataOffset, that is when the chrom tree is not in the initial chunk
|
|
23084
|
-
* read for parsing the header. We know the start position, but not the total size of the chrom tree
|
|
23085
|
-
*
|
|
23086
|
-
* @returns {Promise<void>}
|
|
23087
|
-
*/
|
|
23088
|
-
async #readChromTree(chromTreeOffset, bufferSize) {
|
|
23089
|
-
|
|
23090
|
-
let size = bufferSize;
|
|
23091
|
-
const load = async () => {
|
|
23092
|
-
const data = await this.loader.loadArrayBuffer(this.path, buildOptions(this.config, {
|
|
23093
|
-
range: {
|
|
23094
|
-
start: chromTreeOffset,
|
|
23095
|
-
size: size
|
|
23096
|
-
}
|
|
23097
|
-
}));
|
|
23098
|
-
const binaryParser = new BinaryParser$1(new DataView(data), this.littleEndian);
|
|
23099
|
-
return ChromTree.parseTree(binaryParser, chromTreeOffset, this.genome)
|
|
23100
|
-
};
|
|
23101
|
-
|
|
23102
|
-
let error;
|
|
23103
|
-
while (size < 1000000) {
|
|
23104
|
-
try {
|
|
23105
|
-
const chromTree = await load();
|
|
23106
|
-
return chromTree
|
|
23107
|
-
} catch (e) {
|
|
23108
|
-
error = e;
|
|
23109
|
-
size *= 2;
|
|
23110
|
-
}
|
|
23111
|
-
}
|
|
23112
|
-
throw (error)
|
|
23113
|
-
}
|
|
23114
23221
|
|
|
23115
23222
|
async loadExtendedHeader(offset) {
|
|
23116
23223
|
|
|
@@ -23166,7 +23273,7 @@
|
|
|
23166
23273
|
if (rpTree) {
|
|
23167
23274
|
return rpTree
|
|
23168
23275
|
} else {
|
|
23169
|
-
rpTree = new RPTree(this.path, this.config, offset);
|
|
23276
|
+
rpTree = new RPTree(this.path, this.config, offset, this.loader);
|
|
23170
23277
|
await rpTree.init();
|
|
23171
23278
|
this.rpTreeCache.set(offset, rpTree);
|
|
23172
23279
|
return rpTree
|
|
@@ -23206,9 +23313,7 @@
|
|
|
23206
23313
|
const plain = (this.header.uncompressBuffSize > 0) ? inflate_1$3(uint8Array) : uint8Array;
|
|
23207
23314
|
const decodeFunction = getBedDataDecoder.call(this);
|
|
23208
23315
|
const features = [];
|
|
23209
|
-
decodeFunction.call(this, new DataView(plain.buffer), 0, 0, Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER,
|
|
23210
|
-
features, this.chromTree.idToName);
|
|
23211
|
-
|
|
23316
|
+
await decodeFunction.call(this, new DataView(plain.buffer), 0, 0, Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, features);
|
|
23212
23317
|
return features
|
|
23213
23318
|
|
|
23214
23319
|
}
|
|
@@ -23276,7 +23381,7 @@
|
|
|
23276
23381
|
}
|
|
23277
23382
|
|
|
23278
23383
|
|
|
23279
|
-
function decodeWigData(data, chrIdx1, bpStart, chrIdx2, bpEnd, featureArray,
|
|
23384
|
+
async function decodeWigData(data, chrIdx1, bpStart, chrIdx2, bpEnd, featureArray, windowFunction, littleEndian) {
|
|
23280
23385
|
|
|
23281
23386
|
const binaryParser = new BinaryParser$1(data, littleEndian);
|
|
23282
23387
|
const chromId = binaryParser.getInt();
|
|
@@ -23317,7 +23422,7 @@
|
|
|
23317
23422
|
else if (chromId > chrIdx2 || (chromId === chrIdx2 && chromStart >= bpEnd)) break
|
|
23318
23423
|
|
|
23319
23424
|
if (Number.isFinite(value)) {
|
|
23320
|
-
const chr =
|
|
23425
|
+
const chr = await this.chromTree.getNameForId(chromId);
|
|
23321
23426
|
featureArray.push({chr: chr, start: chromStart, end: chromEnd, value: value});
|
|
23322
23427
|
}
|
|
23323
23428
|
}
|
|
@@ -23328,12 +23433,13 @@
|
|
|
23328
23433
|
|
|
23329
23434
|
const minSize = 3 * 4 + 1; // Minimum # of bytes required for a bed record
|
|
23330
23435
|
const decoder = getDecoder(this.header.definedFieldCount, this.header.fieldCount, this.autoSql, this.format);
|
|
23331
|
-
return function (data, chrIdx1, bpStart, chrIdx2, bpEnd, featureArray
|
|
23332
|
-
|
|
23436
|
+
return async function (data, chrIdx1, bpStart, chrIdx2, bpEnd, featureArray) {
|
|
23437
|
+
|
|
23438
|
+
const binaryParser = new BinaryParser$1(data, this.littleEndian);
|
|
23333
23439
|
while (binaryParser.remLength() >= minSize) {
|
|
23334
23440
|
|
|
23335
23441
|
const chromId = binaryParser.getInt();
|
|
23336
|
-
const chr =
|
|
23442
|
+
const chr = await this.chromTree.getNameForId(chromId);
|
|
23337
23443
|
const chromStart = binaryParser.getInt();
|
|
23338
23444
|
const chromEnd = binaryParser.getInt();
|
|
23339
23445
|
const rest = binaryParser.getString();
|
|
@@ -23350,8 +23456,7 @@
|
|
|
23350
23456
|
}
|
|
23351
23457
|
}
|
|
23352
23458
|
|
|
23353
|
-
|
|
23354
|
-
function decodeZoomData(data, chrIdx1, bpStart, chrIdx2, bpEnd, featureArray, chrDict, windowFunction, littleEndian) {
|
|
23459
|
+
async function decodeZoomData(data, chrIdx1, bpStart, chrIdx2, bpEnd, featureArray, windowFunction, littleEndian) {
|
|
23355
23460
|
|
|
23356
23461
|
const binaryParser = new BinaryParser$1(data, littleEndian);
|
|
23357
23462
|
const minSize = 8 * 4; // Minimum # of bytes required for a zoom record
|
|
@@ -23383,7 +23488,7 @@
|
|
|
23383
23488
|
|
|
23384
23489
|
|
|
23385
23490
|
if (Number.isFinite(value)) {
|
|
23386
|
-
const chr =
|
|
23491
|
+
const chr = await this.chromTree.getNameForId(chromId);
|
|
23387
23492
|
featureArray.push({chr: chr, start: chromStart, end: chromEnd, value: value});
|
|
23388
23493
|
|
|
23389
23494
|
|
|
@@ -23468,7 +23573,8 @@
|
|
|
23468
23573
|
|
|
23469
23574
|
let features;
|
|
23470
23575
|
if ("all" === chr.toLowerCase()) {
|
|
23471
|
-
|
|
23576
|
+
const wgChromosomeNames = this.genome.wgChromosomeNames;
|
|
23577
|
+
features = isBigWig && wgChromosomeNames? await this.getWGValues(wgChromosomeNames, windowFunction, bpPerPixel) : [];
|
|
23472
23578
|
} else {
|
|
23473
23579
|
features = await this.reader.readFeatures(chr, start, chr, end, bpPerPixel, windowFunction);
|
|
23474
23580
|
}
|
|
@@ -23492,15 +23598,14 @@
|
|
|
23492
23598
|
|
|
23493
23599
|
}
|
|
23494
23600
|
|
|
23495
|
-
async getWGValues(windowFunction, bpPerPixel) {
|
|
23601
|
+
async getWGValues(wgChromosomeNames, windowFunction, bpPerPixel) {
|
|
23496
23602
|
|
|
23497
23603
|
const genome = this.genome;
|
|
23498
23604
|
const cached = this.#wgValues[windowFunction];
|
|
23499
23605
|
if (cached && cached.bpPerPixel > 0.8 * bpPerPixel && cached.bpPerPixel < 1.2 * bpPerPixel) {
|
|
23500
23606
|
return cached.values
|
|
23501
23607
|
} else {
|
|
23502
|
-
|
|
23503
|
-
const features = await this.reader.readWGFeatures(bpPerPixel, windowFunction);
|
|
23608
|
+
const features = await this.reader.readWGFeatures(wgChromosomeNames, bpPerPixel, windowFunction);
|
|
23504
23609
|
let wgValues = [];
|
|
23505
23610
|
for (let f of features) {
|
|
23506
23611
|
const chr = f.chr;
|
|
@@ -28295,7 +28400,7 @@
|
|
|
28295
28400
|
features
|
|
28296
28401
|
};
|
|
28297
28402
|
|
|
28298
|
-
const track = await browser.
|
|
28403
|
+
const track = (await browser.loadTrackList([trackConfig]))[0];
|
|
28299
28404
|
track.openTableView();
|
|
28300
28405
|
|
|
28301
28406
|
} catch (e) {
|
|
@@ -30347,109 +30452,578 @@
|
|
|
30347
30452
|
}
|
|
30348
30453
|
}
|
|
30349
30454
|
|
|
30350
|
-
|
|
30351
|
-
https://genomewiki.ucsc.edu/index.php/Assembly_Hubs
|
|
30352
|
-
https://genome.ucsc.edu/goldenpath/help/hgTrackHubHelp.html
|
|
30353
|
-
https://genome.ucsc.edu/goldenPath/help/hgTrackHubHelp
|
|
30354
|
-
https://genome.ucsc.edu/goldenpath/help/trackDb/trackDbHub.html
|
|
30355
|
-
*/
|
|
30455
|
+
const parentOverrideProperties = new Set(["visibility", "priority", "group"]);
|
|
30356
30456
|
|
|
30457
|
+
const nonInheritableProperties = new Set([
|
|
30458
|
+
"track", "type", "shortLabel", "longLabel", "bigDataUrl",
|
|
30459
|
+
"parent", "superTrack", "priority", "view", "compositeContainer", "compositeTrack"
|
|
30460
|
+
]);
|
|
30357
30461
|
|
|
30358
|
-
class Hub {
|
|
30359
30462
|
|
|
30360
|
-
|
|
30361
|
-
|
|
30362
|
-
|
|
30463
|
+
class Stanza {
|
|
30464
|
+
|
|
30465
|
+
properties = new Map()
|
|
30466
|
+
|
|
30467
|
+
constructor(type, name) {
|
|
30468
|
+
this.type = type;
|
|
30469
|
+
this.name = name;
|
|
30470
|
+
}
|
|
30471
|
+
|
|
30472
|
+
setProperty(key, value) {
|
|
30473
|
+
this.properties.set(key, value);
|
|
30474
|
+
}
|
|
30475
|
+
|
|
30476
|
+
getProperty(key) {
|
|
30477
|
+
if (this.properties.has("noInherit")) {
|
|
30478
|
+
return this.properties.get(key)
|
|
30479
|
+
} else if (this.parent && parentOverrideProperties.has(key) && this.parent.hasProperty(key)) {
|
|
30480
|
+
return this.parent.getProperty(key)
|
|
30481
|
+
} else if (this.properties.has(key)) {
|
|
30482
|
+
return this.properties.get(key)
|
|
30483
|
+
} else if (this.parent && !nonInheritableProperties.has(key)) {
|
|
30484
|
+
return this.parent.getProperty(key)
|
|
30485
|
+
} else {
|
|
30486
|
+
return undefined
|
|
30487
|
+
}
|
|
30488
|
+
}
|
|
30489
|
+
|
|
30490
|
+
hasProperty(key) {
|
|
30491
|
+
return this.getProperty(key) !== null && this.getProperty(key) !== undefined
|
|
30492
|
+
}
|
|
30363
30493
|
|
|
30364
|
-
|
|
30494
|
+
hasOwnProperty(key) {
|
|
30495
|
+
return this.properties.has(key)
|
|
30496
|
+
}
|
|
30365
30497
|
|
|
30366
|
-
|
|
30367
|
-
|
|
30368
|
-
|
|
30369
|
-
|
|
30370
|
-
|
|
30371
|
-
|
|
30372
|
-
|
|
30373
|
-
|
|
30374
|
-
|
|
30498
|
+
getOwnProperty(key) {
|
|
30499
|
+
return this.properties.get(key)
|
|
30500
|
+
}
|
|
30501
|
+
|
|
30502
|
+
removeProperty(key) {
|
|
30503
|
+
this.properties.delete(key);
|
|
30504
|
+
}
|
|
30505
|
+
|
|
30506
|
+
get format() {
|
|
30507
|
+
const type = this.getProperty("type");
|
|
30508
|
+
if (type) {
|
|
30509
|
+
// Trim extra bed qualifiers (e.g. bigBed + 4)
|
|
30510
|
+
return firstWord$1(type)
|
|
30511
|
+
}
|
|
30512
|
+
return undefined // unknown type
|
|
30513
|
+
}
|
|
30514
|
+
|
|
30515
|
+
/**
|
|
30516
|
+
* IGV display mode
|
|
30517
|
+
*/
|
|
30518
|
+
get displayMode() {
|
|
30519
|
+
let viz = this.getProperty("visibility");
|
|
30520
|
+
if (!viz) {
|
|
30521
|
+
return "COLLAPSED"
|
|
30522
|
+
} else {
|
|
30523
|
+
viz = viz.toLowerCase();
|
|
30524
|
+
switch (viz) {
|
|
30525
|
+
case "dense":
|
|
30526
|
+
return "COLLAPSED"
|
|
30527
|
+
case "pack":
|
|
30528
|
+
return "EXPANDED"
|
|
30529
|
+
case "squish":
|
|
30530
|
+
return "SQUISHED"
|
|
30531
|
+
default:
|
|
30532
|
+
return "COLLAPSED"
|
|
30375
30533
|
}
|
|
30534
|
+
}
|
|
30535
|
+
}
|
|
30536
|
+
}
|
|
30537
|
+
|
|
30538
|
+
|
|
30539
|
+
function firstWord$1(str) {
|
|
30540
|
+
const idx = str.indexOf(' ');
|
|
30541
|
+
return idx > 0 ? str.substring(0, idx) : str
|
|
30542
|
+
}
|
|
30376
30543
|
|
|
30377
|
-
|
|
30378
|
-
|
|
30379
|
-
|
|
30380
|
-
|
|
30381
|
-
|
|
30382
|
-
|
|
30383
|
-
|
|
30544
|
+
class TrackConfigContainer {
|
|
30545
|
+
constructor(name, label, priority, defaultOpen) {
|
|
30546
|
+
this.name = name;
|
|
30547
|
+
this.priority = priority;
|
|
30548
|
+
this.label = label;
|
|
30549
|
+
this.defaultOpen = defaultOpen;
|
|
30550
|
+
this.tracks = [];
|
|
30551
|
+
this.children = [];
|
|
30552
|
+
}
|
|
30553
|
+
|
|
30554
|
+
isEmpty() {
|
|
30555
|
+
return this.tracks.length === 0 &&
|
|
30556
|
+
(!this.children || this.children.length === 0 || this.children.every(child => child.isEmpty()));
|
|
30557
|
+
}
|
|
30558
|
+
|
|
30559
|
+
map(callback) {
|
|
30560
|
+
this.tracks.forEach(callback);
|
|
30561
|
+
this.children.forEach(child => child.map(callback));
|
|
30562
|
+
}
|
|
30563
|
+
|
|
30564
|
+
findTracks(filter) {
|
|
30565
|
+
const found = [];
|
|
30566
|
+
this._find(found, filter);
|
|
30567
|
+
return found;
|
|
30568
|
+
}
|
|
30569
|
+
|
|
30570
|
+
_find(found, filter) {
|
|
30571
|
+
this.tracks.forEach(track => {
|
|
30572
|
+
if (filter(track)) {
|
|
30573
|
+
found.push(track);
|
|
30384
30574
|
}
|
|
30575
|
+
});
|
|
30576
|
+
this.children.forEach(child => child._find(found, filter));
|
|
30577
|
+
}
|
|
30578
|
+
|
|
30579
|
+
countTracks() {
|
|
30580
|
+
return this.tracks.length + this.children.reduce((count, child) => count + child.countTracks(), 0);
|
|
30581
|
+
}
|
|
30582
|
+
|
|
30583
|
+
countSelectedTracks() {
|
|
30584
|
+
const selectedCount = this.tracks.filter(track => track.visible).length;
|
|
30585
|
+
return selectedCount + this.children.reduce((count, child) => count + child.countSelectedTracks(), 0);
|
|
30586
|
+
}
|
|
30587
|
+
|
|
30588
|
+
trim() {
|
|
30589
|
+
this.children = this.children.filter(child => !child.isEmpty());
|
|
30590
|
+
this.children.forEach(child => child.trim());
|
|
30591
|
+
}
|
|
30592
|
+
|
|
30593
|
+
setTrackVisibility(loadedTrackPaths) {
|
|
30594
|
+
this.tracks.forEach(track => {
|
|
30595
|
+
track.visible = loadedTrackPaths.has(track.url);
|
|
30596
|
+
});
|
|
30597
|
+
this.children.forEach(child => child.setTrackVisibility(loadedTrackPaths));
|
|
30598
|
+
}
|
|
30599
|
+
}
|
|
30600
|
+
|
|
30601
|
+
const supportedTypes = new Set([
|
|
30602
|
+
"bigbed", "bigwig", "biggenepred", "vcftabix", "refgene",
|
|
30603
|
+
"bam", "sampleinfo", "vcf.list", "ucscsnp", "bed", "tdf", "gff", "gff3", "gtf", "vcf", "vcfphasedtrio",
|
|
30604
|
+
"bigdbsnp", "rmask", "genepred", "wig", "bedgraph", "interact", "broadpeak", "narrowpeak", "gappedpeak",
|
|
30605
|
+
"gistic", "seg", "mut, bigrmsk"
|
|
30606
|
+
]);
|
|
30607
|
+
|
|
30608
|
+
const filterTracks = new Set(["cytoBandIdeo", "assembly", "gap", "gapOverlap", "allGaps",
|
|
30609
|
+
"cpgIslandExtUnmasked", "windowMasker"]);
|
|
30610
|
+
|
|
30611
|
+
class TrackDbHub {
|
|
30612
|
+
|
|
30613
|
+
constructor(trackStanzas, groupStanzas) {
|
|
30614
|
+
this.groupStanzas = groupStanzas;
|
|
30615
|
+
this.trackStanzas = trackStanzas;
|
|
30616
|
+
}
|
|
30617
|
+
|
|
30618
|
+
findCytobandURL() {
|
|
30619
|
+
for (const t of this.trackStanzas) {
|
|
30620
|
+
if (t.name === "cytoBandIdeo" && t.hasProperty("bigDataUrl")) {
|
|
30621
|
+
return t.getProperty("bigDataUrl")
|
|
30385
30622
|
}
|
|
30386
30623
|
}
|
|
30624
|
+
}
|
|
30387
30625
|
|
|
30388
|
-
|
|
30389
|
-
|
|
30390
|
-
|
|
30391
|
-
|
|
30392
|
-
|
|
30393
|
-
|
|
30394
|
-
|
|
30395
|
-
|
|
30396
|
-
|
|
30397
|
-
|
|
30398
|
-
|
|
30626
|
+
getSupportedTrackCount() {
|
|
30627
|
+
let count = 0;
|
|
30628
|
+
for (const t of this.trackStanzas) {
|
|
30629
|
+
if (!filterTracks.has(t.name) &&
|
|
30630
|
+
t.hasProperty("bigDataUrl") &&
|
|
30631
|
+
t.format &&
|
|
30632
|
+
supportedTypes.has(t.format.toLowerCase())) {
|
|
30633
|
+
count++;
|
|
30634
|
+
}
|
|
30635
|
+
}
|
|
30636
|
+
return count
|
|
30637
|
+
}
|
|
30638
|
+
|
|
30639
|
+
getGroupedTrackConfigurations() {
|
|
30640
|
+
|
|
30641
|
+
if (!this.groupTrackConfigs) {
|
|
30642
|
+
this.groupTrackConfigs = [];
|
|
30643
|
+
const trackContainers = new Map();
|
|
30399
30644
|
|
|
30400
|
-
|
|
30645
|
+
// create a container for tracks with no parent
|
|
30646
|
+
const nullContainer = new TrackConfigContainer('', '', 0, true);
|
|
30647
|
+
this.groupTrackConfigs.push(nullContainer);
|
|
30648
|
+
|
|
30649
|
+
const hasGroups = this.groupStanzas && this.groupStanzas.length > 0;
|
|
30650
|
+
if (hasGroups) {
|
|
30651
|
+
for (const groupStanza of this.groupStanzas) {
|
|
30652
|
+
const name = groupStanza.getProperty("name");
|
|
30653
|
+
const defaultOpen = groupStanza.getProperty("defaultIsClosed") === "0";
|
|
30654
|
+
const priority = groupStanza.hasProperty("priority") ? getPriority(groupStanza.getProperty("priority")) : Number.MAX_SAFE_INTEGER - 1;
|
|
30655
|
+
const container = new TrackConfigContainer(name, groupStanza.getProperty("label"), priority, defaultOpen);
|
|
30656
|
+
trackContainers.set(name, container);
|
|
30657
|
+
this.groupTrackConfigs.push(container);
|
|
30658
|
+
}
|
|
30659
|
+
}
|
|
30660
|
+
|
|
30661
|
+
for (let s of this.trackStanzas) {
|
|
30662
|
+
|
|
30663
|
+
const isContainer = (s.hasOwnProperty("superTrack") && !s.hasOwnProperty("bigDataUrl")) ||
|
|
30664
|
+
s.hasOwnProperty("compositeTrack") || s.hasOwnProperty("view") ||
|
|
30665
|
+
(s.hasOwnProperty("container") && s.getOwnProperty("container").equals("multiWig"));
|
|
30666
|
+
|
|
30667
|
+
// Find parent, if any. "group" containers can be implicit, all other types should be explicitly
|
|
30668
|
+
// defined before their children
|
|
30669
|
+
let parent;
|
|
30670
|
+
|
|
30671
|
+
if (s.hasOwnProperty("parent")) {
|
|
30672
|
+
parent = trackContainers.get(s.getOwnProperty("parent"));
|
|
30673
|
+
}
|
|
30674
|
+
|
|
30675
|
+
if (!parent && hasGroups && s.hasProperty("group")) {
|
|
30676
|
+
const groupName = s.getProperty("group");
|
|
30677
|
+
if (trackContainers.has(groupName)) {
|
|
30678
|
+
parent = trackContainers.get(groupName);
|
|
30679
|
+
} else {
|
|
30680
|
+
const container = new TrackConfigContainer(groupName, groupName, 1000, true);
|
|
30681
|
+
trackContainers.set(groupName, container);
|
|
30682
|
+
this.groupTrackConfigs.push(container);
|
|
30683
|
+
parent = container;
|
|
30684
|
+
}
|
|
30685
|
+
}
|
|
30686
|
+
|
|
30687
|
+
if (isContainer) {
|
|
30688
|
+
|
|
30689
|
+
const name = s.getProperty("track");
|
|
30690
|
+
const priority = s.hasProperty("priority") ? getPriority(s.getProperty("priority")) : Number.MAX_SAFE_INTEGER - 1;
|
|
30691
|
+
const defaultOpen = s.getProperty("defaultIsClosed") === "0";
|
|
30692
|
+
const longLabel = s.getOwnProperty("longLabel");
|
|
30693
|
+
const label = longLabel && longLabel.length < 50 ? longLabel : s.getOwnProperty("shortLabel");
|
|
30694
|
+
const container = new TrackConfigContainer(name, label, priority, defaultOpen);
|
|
30695
|
+
|
|
30696
|
+
if (trackContainers.has(name)) {
|
|
30697
|
+
throw new Error(`Duplicate track container: ${name}`)
|
|
30698
|
+
}
|
|
30699
|
+
trackContainers.set(name, container);
|
|
30700
|
+
|
|
30701
|
+
if (parent) {
|
|
30702
|
+
parent.children.push(container);
|
|
30703
|
+
} else {
|
|
30704
|
+
// No parent or a superTrack => promote to top level
|
|
30705
|
+
this.groupTrackConfigs.push(container);
|
|
30706
|
+
}
|
|
30707
|
+
} else if (!filterTracks.has(s.name) &&
|
|
30708
|
+
s.hasProperty("bigDataUrl") &&
|
|
30709
|
+
s.format &&
|
|
30710
|
+
supportedTypes.has(s.format.toLowerCase())) {
|
|
30711
|
+
|
|
30712
|
+
const trackConfig = this.#getTrackConfig(s);
|
|
30713
|
+
if (parent) {
|
|
30714
|
+
parent.tracks.push(trackConfig);
|
|
30715
|
+
} else {
|
|
30716
|
+
nullContainer.tracks.push(trackConfig);
|
|
30717
|
+
}
|
|
30718
|
+
}
|
|
30719
|
+
}
|
|
30720
|
+
|
|
30721
|
+
}
|
|
30722
|
+
|
|
30723
|
+
// Filter empty groups and sort
|
|
30724
|
+
this.groupTrackConfigs.forEach(c => c.trim());
|
|
30725
|
+
this.groupTrackConfigs = this.groupTrackConfigs.filter(t => !t.isEmpty());
|
|
30726
|
+
|
|
30727
|
+
this.groupTrackConfigs.sort((a, b) => a.priority - b.priority);
|
|
30728
|
+
return this.groupTrackConfigs
|
|
30401
30729
|
}
|
|
30402
30730
|
|
|
30403
|
-
|
|
30731
|
+
/**
|
|
30732
|
+
* Return an array of igv track config objects that satisfy the filter
|
|
30733
|
+
*/
|
|
30734
|
+
#getTracksConfigs(filter) {
|
|
30735
|
+
return this.trackStanzas.filter(t => {
|
|
30736
|
+
return supportedTypes.has(t.format) && t.hasProperty("bigDataUrl") && (!filter || filter(t))
|
|
30737
|
+
})
|
|
30738
|
+
.map(t => this.#getTrackConfig(t))
|
|
30739
|
+
}
|
|
30404
30740
|
|
|
30405
|
-
this.url = url;
|
|
30406
30741
|
|
|
30407
|
-
|
|
30408
|
-
|
|
30742
|
+
/** example
|
|
30743
|
+
* track gc5Base
|
|
30744
|
+
* shortLabel GC Percent
|
|
30745
|
+
* longLabel GC Percent in 5-Base Windows
|
|
30746
|
+
* group map
|
|
30747
|
+
* visibility full
|
|
30748
|
+
* autoScale Off
|
|
30749
|
+
* maxHeightPixels 128:36:16
|
|
30750
|
+
* graphTypeDefault Bar
|
|
30751
|
+
* gridDefault OFF
|
|
30752
|
+
* windowingFunction Mean
|
|
30753
|
+
* color 0,0,0
|
|
30754
|
+
* altColor 128,128,128
|
|
30755
|
+
* viewLimits 30:70
|
|
30756
|
+
* type bigWig 0 100
|
|
30757
|
+
* bigDataUrl bbi/GCA_011100615.1_Macaca_fascicularis_6.0.gc5Base.bw
|
|
30758
|
+
* html html/GCA_011100615.1_Macaca_fascicularis_6.0.gc5Base
|
|
30759
|
+
* @param t
|
|
30760
|
+
*/
|
|
30761
|
+
#getTrackConfig(t) {
|
|
30409
30762
|
|
|
30410
|
-
|
|
30411
|
-
|
|
30412
|
-
|
|
30413
|
-
|
|
30414
|
-
|
|
30763
|
+
const format = t.format;
|
|
30764
|
+
|
|
30765
|
+
const config = {
|
|
30766
|
+
"id": t.getProperty("track"),
|
|
30767
|
+
"name": t.getProperty("shortLabel"),
|
|
30768
|
+
"format": format,
|
|
30769
|
+
"url": t.getProperty("bigDataUrl"),
|
|
30770
|
+
"displayMode": t.displayMode,
|
|
30771
|
+
};
|
|
30772
|
+
|
|
30773
|
+
if ("vcfTabix" === format) {
|
|
30774
|
+
config.indexURL = config.url + ".tbi";
|
|
30775
|
+
}
|
|
30776
|
+
|
|
30777
|
+
if (t.hasProperty("longLabel") && t.hasProperty("html")) {
|
|
30778
|
+
if (config.description) config.description += "<br/>";
|
|
30779
|
+
config.description =
|
|
30780
|
+
`<a target="_blank" href="${t.getProperty("html")}">${t.getProperty("longLabel")}</a>`;
|
|
30781
|
+
} else if (t.hasProperty("longLabel")) {
|
|
30782
|
+
config.description = t.getProperty("longLabel");
|
|
30783
|
+
}
|
|
30784
|
+
|
|
30785
|
+
if (t.hasProperty("autoScale")) {
|
|
30786
|
+
config.autoscale = t.getProperty("autoScale").toLowerCase() === "on";
|
|
30787
|
+
}
|
|
30788
|
+
if (t.hasProperty("maxHeightPixels")) {
|
|
30789
|
+
const tokens = t.getProperty("maxHeightPixels").split(":");
|
|
30790
|
+
config.maxHeight = Number.parseInt(tokens[0]);
|
|
30791
|
+
config.height = Number.parseInt(tokens[1]);
|
|
30792
|
+
config.minHeight = Number.parseInt(tokens[2]);
|
|
30793
|
+
}
|
|
30794
|
+
// TODO -- graphTypeDefault
|
|
30795
|
+
// TODO -- windowingFunction
|
|
30796
|
+
if (t.hasProperty("color")) {
|
|
30797
|
+
const c = t.getProperty("color");
|
|
30798
|
+
config.color = c.indexOf(",") > 0 ? `rgb(${c})` : c;
|
|
30799
|
+
}
|
|
30800
|
+
if (t.hasProperty("altColor")) {
|
|
30801
|
+
const c = t.getProperty("altColor");
|
|
30802
|
+
config.altColor = c.indexOf(",") > 0 ? `rgb(${c})` : c;
|
|
30803
|
+
}
|
|
30804
|
+
if (t.hasProperty("viewLimits")) {
|
|
30805
|
+
const tokens = t.getProperty("viewLimits").split(":");
|
|
30806
|
+
let min, max;
|
|
30807
|
+
if (tokens.length > 1) {
|
|
30808
|
+
min = Number.parseInt(tokens[0]);
|
|
30809
|
+
max = Number.parseInt(tokens[1]);
|
|
30810
|
+
}
|
|
30811
|
+
if (Number.isNaN(max) || Number.isNaN(min)) {
|
|
30812
|
+
console.warn(`Unexpected viewLimits value in track line: ${properties["viewLimits"]}`);
|
|
30813
|
+
} else {
|
|
30814
|
+
config.min = min;
|
|
30815
|
+
config.max = max;
|
|
30816
|
+
}
|
|
30817
|
+
|
|
30818
|
+
}
|
|
30819
|
+
if (t.hasProperty("itemRgb")) ;
|
|
30820
|
+
if ("hide" === t.getProperty("visibility")) {
|
|
30821
|
+
// TODO -- this not supported yet
|
|
30822
|
+
config.visible = false;
|
|
30415
30823
|
}
|
|
30416
|
-
if (
|
|
30417
|
-
|
|
30824
|
+
if (t.hasProperty("url")) {
|
|
30825
|
+
config.infoURL = t.getProperty("url");
|
|
30418
30826
|
}
|
|
30419
|
-
if (
|
|
30420
|
-
|
|
30827
|
+
if (t.hasProperty("searchIndex")) {
|
|
30828
|
+
config.searchIndex = t.getProperty("searchIndex");
|
|
30829
|
+
}
|
|
30830
|
+
if (t.hasProperty("searchTrix")) {
|
|
30831
|
+
config.trixURL = t.getProperty("searchTrix");
|
|
30832
|
+
}
|
|
30833
|
+
if (t.hasProperty("html")) {
|
|
30834
|
+
config.html = t.getProperty("html");
|
|
30421
30835
|
}
|
|
30422
30836
|
|
|
30423
|
-
|
|
30424
|
-
|
|
30425
|
-
this.
|
|
30426
|
-
|
|
30427
|
-
|
|
30837
|
+
if (t.hasProperty("group")) {
|
|
30838
|
+
config._group = t.getProperty("group");
|
|
30839
|
+
if (this.groupPriorityMap && this.groupPriorityMap.has(config._group)) {
|
|
30840
|
+
const nextPriority = this.groupPriorityMap.get(config._group) + 1;
|
|
30841
|
+
config.order = nextPriority;
|
|
30842
|
+
this.groupPriorityMap.set(config._group, nextPriority);
|
|
30843
|
+
}
|
|
30844
|
+
}
|
|
30845
|
+
|
|
30846
|
+
if (t.hasProperty("metadata")) {
|
|
30847
|
+
config.attributes = parseMetadata(t.getProperty("metadata"));
|
|
30848
|
+
}
|
|
30849
|
+
|
|
30850
|
+
if (t.hasProperty("maxWindowToDraw")) {
|
|
30851
|
+
let maxWindowToDraw = parseInt(t.getProperty("maxWindowToDraw"), 10);
|
|
30852
|
+
if (maxWindowToDraw > Number.MAX_SAFE_INTEGER) {
|
|
30853
|
+
maxWindowToDraw = Number.MAX_SAFE_INTEGER;
|
|
30854
|
+
}
|
|
30855
|
+
config.visibilityWindow = maxWindowToDraw;
|
|
30428
30856
|
}
|
|
30429
30857
|
|
|
30430
|
-
//
|
|
30431
|
-
|
|
30432
|
-
|
|
30433
|
-
if (
|
|
30434
|
-
|
|
30858
|
+
// IGV does not support "maxWindowCoverage" in the same way as UCSC. Use to limit visibility window
|
|
30859
|
+
if (t.hasProperty("maxWindowCoverage")) {
|
|
30860
|
+
let maxWindowToDraw = parseInt(t.getProperty("maxWindowCoverage"), 10);
|
|
30861
|
+
if (maxWindowToDraw > Number.MAX_SAFE_INTEGER) {
|
|
30862
|
+
maxWindowToDraw = Number.MAX_SAFE_INTEGER;
|
|
30435
30863
|
}
|
|
30864
|
+
config.visibilityWindow = maxWindowToDraw;
|
|
30436
30865
|
}
|
|
30437
30866
|
|
|
30438
|
-
|
|
30439
|
-
|
|
30440
|
-
|
|
30441
|
-
|
|
30442
|
-
|
|
30443
|
-
|
|
30867
|
+
return config
|
|
30868
|
+
}
|
|
30869
|
+
}
|
|
30870
|
+
|
|
30871
|
+
function htmlText(html) {
|
|
30872
|
+
// Assumes a pattern like <span style="color:#C58DAA">Digestive</span>
|
|
30873
|
+
const idx1 = html.indexOf('>');
|
|
30874
|
+
const idx2 = html.indexOf('<', idx1);
|
|
30875
|
+
if (idx1 > 0 && idx2 > idx1) {
|
|
30876
|
+
return html.substring(idx1 + 1, idx2)
|
|
30877
|
+
} else {
|
|
30878
|
+
return html
|
|
30879
|
+
}
|
|
30880
|
+
}
|
|
30881
|
+
|
|
30882
|
+
/**
|
|
30883
|
+
* Return the priority for the group. The priority format is uncertain, but extends to at least 2 levels (e.g. 3.4).
|
|
30884
|
+
* Ignore levels > 3
|
|
30885
|
+
*
|
|
30886
|
+
* @param {string} priorityString Priority as a string (e.g. 3.4)
|
|
30887
|
+
* @return {number} A priority as an integer
|
|
30888
|
+
*/
|
|
30889
|
+
function getPriority(priorityString) {
|
|
30890
|
+
try {
|
|
30891
|
+
const tokens = priorityString.trim().split(".");
|
|
30892
|
+
let p = parseInt(tokens[0], 10) * 100;
|
|
30893
|
+
if (tokens.length > 1) {
|
|
30894
|
+
p += parseInt(tokens[1], 10) * 10;
|
|
30895
|
+
}
|
|
30896
|
+
if (tokens.length > 2) {
|
|
30897
|
+
p += parseInt(tokens[2], 10);
|
|
30898
|
+
}
|
|
30899
|
+
return p
|
|
30900
|
+
} catch (e) {
|
|
30901
|
+
console.error(`Error parsing priority string: ${priorityString}`, e);
|
|
30902
|
+
return Number.MAX_SAFE_INTEGER
|
|
30903
|
+
}
|
|
30904
|
+
}
|
|
30905
|
+
|
|
30906
|
+
function parseMetadata(metadata) {
|
|
30907
|
+
const attrs = new Map();
|
|
30908
|
+
let lastMetdataLengh = -1;
|
|
30909
|
+
while (metadata && metadata.length > 0) {
|
|
30910
|
+
try {
|
|
30911
|
+
if (metadata.length === lastMetdataLengh) {
|
|
30912
|
+
break
|
|
30913
|
+
}
|
|
30914
|
+
lastMetdataLengh = metadata.length;
|
|
30915
|
+
let idx = metadata.indexOf("=");
|
|
30916
|
+
if (idx === -1 || idx === metadata.length - 1) {
|
|
30917
|
+
break
|
|
30918
|
+
}
|
|
30919
|
+
let idx2;
|
|
30920
|
+
const key = capitalize(stripQuotes$2(metadata.substring(0, idx)));
|
|
30921
|
+
let value;
|
|
30922
|
+
|
|
30923
|
+
if (metadata.charAt(idx + 1) === '"') {
|
|
30924
|
+
idx++;
|
|
30925
|
+
idx2 = metadata.indexOf('" ', idx + 1);
|
|
30926
|
+
value = idx2 > 0 ? metadata.substring(idx + 1, idx2) : metadata.substring(idx + 1);
|
|
30927
|
+
idx2++;
|
|
30928
|
+
} else {
|
|
30929
|
+
idx2 = metadata.indexOf(" ", idx + 1);
|
|
30930
|
+
if (idx2 === -1) {
|
|
30931
|
+
idx2 = metadata.length;
|
|
30444
30932
|
}
|
|
30933
|
+
value = metadata.substring(idx + 1, idx2);
|
|
30934
|
+
}
|
|
30935
|
+
value = stripQuotes$2(value);
|
|
30936
|
+
if (value.endsWith('"')) {
|
|
30937
|
+
value = value.substring(0, value.length - 1);
|
|
30938
|
+
}
|
|
30939
|
+
if (value.startsWith("<") && value.endsWith(">")) {
|
|
30940
|
+
value = htmlText(value);
|
|
30445
30941
|
}
|
|
30942
|
+
attrs.set(key, value);
|
|
30943
|
+
if (idx2 === metadata.length) {
|
|
30944
|
+
break
|
|
30945
|
+
}
|
|
30946
|
+
metadata = idx2 > 0 ? metadata.substring(idx2 + 1).trim() : "";
|
|
30947
|
+
} catch (e) {
|
|
30948
|
+
// We don't want to fail parsing the hub due to a failure parsing metadata. Also, we don't want to
|
|
30949
|
+
// overwhelm the log. Metadata is of marginal importance in IGV.
|
|
30446
30950
|
}
|
|
30447
30951
|
}
|
|
30952
|
+
return attrs
|
|
30953
|
+
}
|
|
30954
|
+
|
|
30955
|
+
/*
|
|
30956
|
+
https://genomewiki.ucsc.edu/index.php/Assembly_Hubs
|
|
30957
|
+
https://genome.ucsc.edu/goldenpath/help/hgTrackHubHelp.html
|
|
30958
|
+
https://genome.ucsc.edu/goldenPath/help/hgTrackHubHelp
|
|
30959
|
+
https://genome.ucsc.edu/goldenpath/help/trackDb/trackDbHub.html
|
|
30960
|
+
*/
|
|
30961
|
+
|
|
30962
|
+
const idMappings = new Map([
|
|
30963
|
+
["hg38", "GCF_000001405.40"],
|
|
30964
|
+
["mm39", "GCF_000001635.27"],
|
|
30965
|
+
["mm10", "GCF_000001635.26"],
|
|
30966
|
+
["bosTau9", "GCF_002263795.1"],
|
|
30967
|
+
["canFam4", "GCF_011100685.1"],
|
|
30968
|
+
["canFam6", "GCF_000002285.5"],
|
|
30969
|
+
["ce11", "GCF_000002985.6"],
|
|
30970
|
+
["dm6", "GCF_000001215.4"],
|
|
30971
|
+
["galGal6", "GCF_000002315.6"],
|
|
30972
|
+
["gorGor6", "GCF_008122165.1"],
|
|
30973
|
+
["macFas5", "GCA_000364345.1"],
|
|
30974
|
+
["panTro6", "GCA_002880755.3"],
|
|
30975
|
+
["rn6", "GCF_000001895.5"],
|
|
30976
|
+
["rn7", "GCF_015227675.2"],
|
|
30977
|
+
["sacCer3", "GCF_000146045.2"],
|
|
30978
|
+
["sacCer2", "GCF_000146045.2"],
|
|
30979
|
+
["susScr11", "GCF_000003025.6"],
|
|
30980
|
+
["taeGut1", "GCF_000002275.3"],
|
|
30981
|
+
["tetNig2", "GCF_000002275.3"],
|
|
30982
|
+
["xenTro10", "GCF_000002035.6"],
|
|
30983
|
+
["xenTro9", "GCF_000002035.6"],
|
|
30984
|
+
["tair10", "GCF_000001735.4"],
|
|
30985
|
+
]);
|
|
30448
30986
|
|
|
30449
|
-
|
|
30450
|
-
|
|
30987
|
+
class Hub {
|
|
30988
|
+
|
|
30989
|
+
static supportedTypes = new Set(["bigBed", "bigWig", "bigGenePred", "vcfTabix"])
|
|
30990
|
+
static filterTracks = new Set(["cytoBandIdeo", "assembly", "gap", "gapOverlap", "allGaps",
|
|
30991
|
+
"cpgIslandExtUnmasked", "windowMasker"])
|
|
30992
|
+
|
|
30993
|
+
constructor(url, hubStanza, genomeStanzas, trackStanzas, groupStanzas) {
|
|
30994
|
+
|
|
30995
|
+
this.url = url;
|
|
30996
|
+
this.hubStanza = hubStanza;
|
|
30997
|
+
this.genomeStanzas = genomeStanzas;
|
|
30998
|
+
this.trackStanzas = trackStanzas;
|
|
30999
|
+
this.groupStanzas = groupStanzas;
|
|
31000
|
+
this.trackHubMap = new Map();
|
|
31001
|
+
|
|
31002
|
+
// trackStanzas will not be null if this is a "onefile" hub
|
|
31003
|
+
if (trackStanzas) {
|
|
31004
|
+
const genomeId = genomeStanzas[0].getProperty("genome"); // Assumption here this is a single genome hub
|
|
31005
|
+
this.trackHubMap.set(genomeId, new TrackDbHub(trackStanzas, groupStanzas));
|
|
31006
|
+
}
|
|
30451
31007
|
}
|
|
30452
31008
|
|
|
31009
|
+
|
|
31010
|
+
getName() {
|
|
31011
|
+
return this.hubStanza.getProperty("hub")
|
|
31012
|
+
}
|
|
31013
|
+
|
|
31014
|
+
getShortLabel() {
|
|
31015
|
+
return this.hubStanza.getProperty("shortLabel")
|
|
31016
|
+
}
|
|
31017
|
+
|
|
31018
|
+
getLongLabel() {
|
|
31019
|
+
return this.hubStanza.getProperty("longLabel")
|
|
31020
|
+
}
|
|
31021
|
+
|
|
31022
|
+
getDescriptionUrl() {
|
|
31023
|
+
return this.hubStanza.getProperty("descriptionUrl")
|
|
31024
|
+
}
|
|
31025
|
+
|
|
31026
|
+
|
|
30453
31027
|
/* Example genome stanza
|
|
30454
31028
|
genome GCF_000186305.1
|
|
30455
31029
|
taxId 176946
|
|
@@ -30468,133 +31042,126 @@
|
|
|
30468
31042
|
isPcr dynablat-01.soe.ucsc.edu 4040 dynamic GCF/000/186/305/GCF_000186305.1
|
|
30469
31043
|
*/
|
|
30470
31044
|
|
|
30471
|
-
getGenomeConfig(
|
|
30472
|
-
|
|
31045
|
+
getGenomeConfig(genomeId) {
|
|
31046
|
+
|
|
31047
|
+
const genomeStanza = genomeId ? this.genomeStanzas.find(s => s.getProperty("genome") === genomeId) : this.genomeStanzas[0];
|
|
31048
|
+
if (!genomeStanza) {
|
|
31049
|
+
throw new Error(`Genome not found in hub: ${genomeId}`)
|
|
31050
|
+
}
|
|
31051
|
+
return this.#getGenomeConfig(genomeStanza)
|
|
31052
|
+
}
|
|
31053
|
+
|
|
31054
|
+
#getGenomeConfig(genomeStanza) {
|
|
30473
31055
|
|
|
30474
|
-
const id =
|
|
31056
|
+
const id = genomeStanza.getProperty("genome");
|
|
30475
31057
|
const gsName =
|
|
30476
31058
|
this.hubStanza.getProperty("shortLabel") ||
|
|
30477
|
-
|
|
30478
|
-
|
|
30479
|
-
|
|
31059
|
+
genomeStanza.getProperty("scientificName") ||
|
|
31060
|
+
genomeStanza.getProperty("organism") ||
|
|
31061
|
+
genomeStanza.getProperty("description");
|
|
30480
31062
|
const name = gsName + (gsName ? ` (${id})` : ` ${id}`);
|
|
30481
31063
|
|
|
30482
31064
|
const config = {
|
|
30483
|
-
|
|
31065
|
+
|
|
30484
31066
|
id: id,
|
|
30485
31067
|
name: name,
|
|
30486
|
-
twoBitURL:
|
|
31068
|
+
twoBitURL: genomeStanza.getProperty("twoBitPath"),
|
|
30487
31069
|
nameSet: "ucsc",
|
|
31070
|
+
hubs: [this.url]
|
|
30488
31071
|
};
|
|
30489
31072
|
|
|
30490
|
-
if (
|
|
30491
|
-
config.chromSizesURL =
|
|
31073
|
+
if (genomeStanza.hasProperty("chromSizes")) {
|
|
31074
|
+
config.chromSizesURL = genomeStanza.getProperty("chromSizes");
|
|
30492
31075
|
} else {
|
|
30493
31076
|
config.wholeGenomeView = false;
|
|
30494
31077
|
config.showChromosomeWidget = false;
|
|
30495
31078
|
}
|
|
30496
31079
|
|
|
30497
|
-
if (
|
|
30498
|
-
const hubLocus =
|
|
31080
|
+
if (genomeStanza.hasProperty("defaultPos")) {
|
|
31081
|
+
const hubLocus = genomeStanza.getProperty("defaultPos");
|
|
30499
31082
|
// Strip out coordinates => whole chromosome view
|
|
30500
|
-
if (hubLocus) {
|
|
30501
|
-
|
|
30502
|
-
|
|
30503
|
-
}
|
|
31083
|
+
// if (hubLocus) {
|
|
31084
|
+
// const idx = hubLocus.lastIndexOf(":")
|
|
31085
|
+
// config.locus = idx > 0 ? hubLocus.substring(0, idx) : hubLocus
|
|
31086
|
+
// }
|
|
31087
|
+
config.locus = hubLocus;
|
|
30504
31088
|
}
|
|
30505
31089
|
|
|
30506
|
-
if (
|
|
30507
|
-
config.blat =
|
|
30508
|
-
}
|
|
30509
|
-
if (this.genomeStanza.hasProperty("chromAliasBb")) {
|
|
30510
|
-
config.chromAliasBbURL = this.baseURL + this.genomeStanza.getProperty("chromAliasBb");
|
|
31090
|
+
if (genomeStanza.hasProperty("blat")) {
|
|
31091
|
+
config.blat = genomeStanza.getProperty("blat");
|
|
30511
31092
|
}
|
|
30512
|
-
if (
|
|
30513
|
-
config.
|
|
31093
|
+
if (genomeStanza.hasProperty("chromAliasBb")) {
|
|
31094
|
+
config.chromAliasBbURL = genomeStanza.getProperty("chromAliasBb");
|
|
30514
31095
|
}
|
|
30515
|
-
if (
|
|
30516
|
-
config.
|
|
31096
|
+
if (genomeStanza.hasProperty("chromAlias")) {
|
|
31097
|
+
config.aliasURL = genomeStanza.getProperty("chromAlias");
|
|
30517
31098
|
}
|
|
30518
|
-
|
|
30519
|
-
|
|
30520
|
-
config.twoBitBptURL = this.baseURL + this.genomeStanza.getProperty("twoBitBptUrl");
|
|
31099
|
+
if (genomeStanza.hasProperty("twoBitBptURL")) {
|
|
31100
|
+
config.twoBitBptURL = genomeStanza.getProperty("twoBitBptURL");
|
|
30521
31101
|
}
|
|
30522
31102
|
|
|
30523
|
-
|
|
30524
|
-
|
|
30525
|
-
config.chromSizesURL = this.baseURL + this.genomeStanza.getProperty("chromSizes");
|
|
31103
|
+
if (genomeStanza.hasProperty("twoBitBptUrl")) {
|
|
31104
|
+
config.twoBitBptURL = genomeStanza.getProperty("twoBitBptUrl");
|
|
30526
31105
|
}
|
|
30527
31106
|
|
|
30528
31107
|
if (this.hubStanza.hasProperty("longLabel")) {
|
|
30529
31108
|
config.description = this.hubStanza.getProperty("longLabel").replace("/", "\n");
|
|
30530
31109
|
} else {
|
|
30531
31110
|
config.description = config.id;
|
|
30532
|
-
if (
|
|
30533
|
-
config.description += `\n${
|
|
31111
|
+
if (genomeStanza.hasProperty("description")) {
|
|
31112
|
+
config.description += `\n${genomeStanza.getProperty("description")}`;
|
|
30534
31113
|
}
|
|
30535
|
-
if (
|
|
30536
|
-
config.description += `\n${
|
|
31114
|
+
if (genomeStanza.hasProperty("organism")) {
|
|
31115
|
+
config.description += `\n${genomeStanza.getProperty("organism")}`;
|
|
30537
31116
|
}
|
|
30538
|
-
if (
|
|
30539
|
-
config.description += `\n${
|
|
31117
|
+
if (genomeStanza.hasProperty("scientificName")) {
|
|
31118
|
+
config.description += `\n${genomeStanza.getProperty("scientificName")}`;
|
|
30540
31119
|
}
|
|
30541
31120
|
|
|
30542
|
-
if (
|
|
30543
|
-
config.infoURL =
|
|
31121
|
+
if (genomeStanza.hasProperty("htmlPath")) {
|
|
31122
|
+
config.infoURL = genomeStanza.getProperty("htmlPath");
|
|
30544
31123
|
}
|
|
30545
31124
|
}
|
|
30546
31125
|
|
|
30547
|
-
// Search for cytoband
|
|
30548
|
-
/*
|
|
30549
|
-
track cytoBandIdeo
|
|
30550
|
-
shortLabel Chromosome Band (Ideogram)
|
|
30551
|
-
longLabel Ideogram for Orientation
|
|
30552
|
-
group map
|
|
30553
|
-
visibility dense
|
|
30554
|
-
type bigBed 4 +
|
|
30555
|
-
bigDataUrl bbi/GCA_004027145.1_DauMad_v1_BIUU.cytoBand.bb
|
|
30556
|
-
*/
|
|
30557
|
-
const cytoStanza = this.trackStanzas.filter(t => "cytoBandIdeo" === t.name && t.hasProperty("bigDataUrl"));
|
|
30558
|
-
if (cytoStanza.length > 0) {
|
|
30559
|
-
config.cytobandBbURL = this.baseURL + cytoStanza[0].getProperty("bigDataUrl");
|
|
30560
|
-
}
|
|
30561
|
-
|
|
30562
31126
|
// Tracks.
|
|
30563
31127
|
const filter = (t) => !Hub.filterTracks.has(t.name) && "hide" !== t.getProperty("visibility");
|
|
30564
31128
|
config.tracks = this.#getTracksConfigs(filter);
|
|
30565
31129
|
|
|
30566
|
-
|
|
30567
31130
|
return config
|
|
30568
31131
|
}
|
|
30569
31132
|
|
|
30570
|
-
getGroupedTrackConfigurations() {
|
|
30571
|
-
|
|
30572
|
-
|
|
30573
|
-
|
|
30574
|
-
for (let c of this.#getTracksConfigs()) {
|
|
30575
|
-
if (c.name === "cytoBandIdeo") continue
|
|
30576
|
-
const groupName = c.group || "other";
|
|
30577
|
-
if (trackConfigMap.has(groupName)) {
|
|
30578
|
-
trackConfigMap.get(groupName).push(c);
|
|
30579
|
-
} else {
|
|
30580
|
-
trackConfigMap.set(groupName, [c]);
|
|
30581
|
-
}
|
|
31133
|
+
async getGroupedTrackConfigurations(genomeId) {
|
|
31134
|
+
let trackHub = await this.#getTrackDbHub(genomeId);
|
|
31135
|
+
if (!trackHub && idMappings.has(genomeId)) {
|
|
31136
|
+
trackHub = await this.#getTrackDbHub(idMappings.get(genomeId));
|
|
30582
31137
|
}
|
|
31138
|
+
if (!trackHub) {
|
|
31139
|
+
console.log(`Warning: no trackDB found for genomeId ${genomeId}.`);
|
|
31140
|
+
}
|
|
31141
|
+
return trackHub ? trackHub.getGroupedTrackConfigurations() : []
|
|
31142
|
+
}
|
|
30583
31143
|
|
|
30584
|
-
|
|
30585
|
-
|
|
30586
|
-
|
|
30587
|
-
|
|
30588
|
-
|
|
30589
|
-
|
|
30590
|
-
|
|
30591
|
-
|
|
30592
|
-
|
|
31144
|
+
async #getTrackDbHub(genomeId) {
|
|
31145
|
+
let trackHub = this.trackHubMap.get(genomeId);
|
|
31146
|
+
if (!trackHub) {
|
|
31147
|
+
for (let stanza of this.genomeStanzas) {
|
|
31148
|
+
if (genomeId === stanza.getProperty("genome")) {
|
|
31149
|
+
try {
|
|
31150
|
+
const trackDbURL = stanza.getProperty("trackDb");
|
|
31151
|
+
const trackStanzas = await loadStanzas(trackDbURL);
|
|
31152
|
+
trackHub = new TrackDbHub(trackStanzas, this.groupStanzas);
|
|
31153
|
+
this.trackHubMap.set(genomeId, trackHub);
|
|
31154
|
+
} catch (error) {
|
|
31155
|
+
console.error(`Error loading trackDb file: ${stanza.getProperty("trackDb")}`, error);
|
|
31156
|
+
}
|
|
31157
|
+
break
|
|
31158
|
+
}
|
|
30593
31159
|
}
|
|
30594
|
-
}
|
|
30595
|
-
|
|
31160
|
+
}
|
|
31161
|
+
return trackHub
|
|
30596
31162
|
}
|
|
30597
31163
|
|
|
31164
|
+
|
|
30598
31165
|
/**
|
|
30599
31166
|
* Return an array of igv track config objects that satisfy the filter
|
|
30600
31167
|
*/
|
|
@@ -30632,7 +31199,7 @@
|
|
|
30632
31199
|
"id": t.getProperty("track"),
|
|
30633
31200
|
"name": t.getProperty("shortLabel"),
|
|
30634
31201
|
"format": format,
|
|
30635
|
-
"url":
|
|
31202
|
+
"url": t.getProperty("bigDataUrl"),
|
|
30636
31203
|
"displayMode": t.displayMode,
|
|
30637
31204
|
};
|
|
30638
31205
|
|
|
@@ -30643,7 +31210,7 @@
|
|
|
30643
31210
|
if (t.hasProperty("longLabel") && t.hasProperty("html")) {
|
|
30644
31211
|
if (config.description) config.description += "<br/>";
|
|
30645
31212
|
config.description =
|
|
30646
|
-
`<a target="_blank" href="${
|
|
31213
|
+
`<a target="_blank" href="${t.getProperty("html")}">${t.getProperty("longLabel")}</a>`;
|
|
30647
31214
|
} else if (t.hasProperty("longLabel")) {
|
|
30648
31215
|
config.description = t.getProperty("longLabel");
|
|
30649
31216
|
}
|
|
@@ -30675,7 +31242,7 @@
|
|
|
30675
31242
|
max = Number.parseInt(tokens[1]);
|
|
30676
31243
|
}
|
|
30677
31244
|
if (Number.isNaN(max) || Number.isNaN(min)) {
|
|
30678
|
-
console.warn(`Unexpected viewLimits value in track line: ${
|
|
31245
|
+
console.warn(`Unexpected viewLimits value in track line: ${t.getProperty("viewLimits")}`);
|
|
30679
31246
|
} else {
|
|
30680
31247
|
config.min = min;
|
|
30681
31248
|
config.max = max;
|
|
@@ -30694,15 +31261,15 @@
|
|
|
30694
31261
|
config.searchIndex = t.getProperty("searchIndex");
|
|
30695
31262
|
}
|
|
30696
31263
|
if (t.hasProperty("searchTrix")) {
|
|
30697
|
-
config.trixURL =
|
|
31264
|
+
config.trixURL = t.getProperty("searchTrix");
|
|
30698
31265
|
}
|
|
30699
31266
|
|
|
30700
31267
|
if (t.hasProperty("group")) {
|
|
30701
|
-
config.
|
|
30702
|
-
if (this.groupPriorityMap && this.groupPriorityMap.has(config.
|
|
30703
|
-
const nextPriority = this.groupPriorityMap.get(config.
|
|
31268
|
+
config._group = t.getProperty("group");
|
|
31269
|
+
if (this.groupPriorityMap && this.groupPriorityMap.has(config._group)) {
|
|
31270
|
+
const nextPriority = this.groupPriorityMap.get(config._group) + 1;
|
|
30704
31271
|
config.order = nextPriority;
|
|
30705
|
-
this.groupPriorityMap.set(config.
|
|
31272
|
+
this.groupPriorityMap.set(config._group, nextPriority);
|
|
30706
31273
|
}
|
|
30707
31274
|
}
|
|
30708
31275
|
|
|
@@ -30711,96 +31278,92 @@
|
|
|
30711
31278
|
|
|
30712
31279
|
}
|
|
30713
31280
|
|
|
30714
|
-
|
|
30715
|
-
|
|
30716
|
-
|
|
30717
|
-
|
|
31281
|
+
/*
|
|
31282
|
+
https://genomewiki.ucsc.edu/index.php/Assembly_Hubs
|
|
31283
|
+
https://genome.ucsc.edu/goldenpath/help/hgTrackHubHelp.html
|
|
31284
|
+
https://genome.ucsc.edu/goldenPath/help/hgTrackHubHelp
|
|
31285
|
+
https://genome.ucsc.edu/goldenpath/help/trackDb/trackDbHub.html
|
|
31286
|
+
*/
|
|
30718
31287
|
|
|
30719
|
-
|
|
31288
|
+
const urlProperties = new Set(["descriptionUrl", "desriptionUrl",
|
|
31289
|
+
"twoBitPath", "blat", "chromAliasBb", "twoBitBptURL", "twoBitBptUrl", "htmlPath", "bigDataUrl",
|
|
31290
|
+
"genomesFile", "trackDb", "groups", "include", "html", "searchTrix", "groups",
|
|
31291
|
+
"chromSizes"]);
|
|
30720
31292
|
|
|
30721
|
-
properties = new Map()
|
|
30722
31293
|
|
|
30723
|
-
|
|
30724
|
-
this.type = type;
|
|
30725
|
-
this.name = name;
|
|
30726
|
-
}
|
|
31294
|
+
const hubCache = new Map();
|
|
30727
31295
|
|
|
30728
|
-
|
|
30729
|
-
|
|
31296
|
+
async function loadHub(url) {
|
|
31297
|
+
if (hubCache.has(url)) {
|
|
31298
|
+
return hubCache.get(url)
|
|
30730
31299
|
}
|
|
30731
31300
|
|
|
30732
|
-
|
|
30733
|
-
|
|
30734
|
-
|
|
30735
|
-
} else if (this.parent) {
|
|
30736
|
-
return this.parent.getProperty(key)
|
|
30737
|
-
} else {
|
|
30738
|
-
return undefined
|
|
30739
|
-
}
|
|
31301
|
+
const stanzas = await loadStanzas(url);
|
|
31302
|
+
if (stanzas.length < 1) {
|
|
31303
|
+
throw new Error("Empty hub file")
|
|
30740
31304
|
}
|
|
30741
31305
|
|
|
30742
|
-
|
|
30743
|
-
|
|
30744
|
-
|
|
30745
|
-
} else if (this.parent) {
|
|
30746
|
-
return this.parent.hasProperty(key)
|
|
30747
|
-
} else {
|
|
30748
|
-
return false
|
|
30749
|
-
}
|
|
31306
|
+
const hubStanza = stanzas[0];
|
|
31307
|
+
if (hubStanza.type !== "hub") {
|
|
31308
|
+
throw new Error("First stanza must be a hub stanza")
|
|
30750
31309
|
}
|
|
30751
31310
|
|
|
30752
|
-
|
|
30753
|
-
|
|
30754
|
-
|
|
30755
|
-
|
|
30756
|
-
|
|
31311
|
+
let genomeStanzas;
|
|
31312
|
+
let trackStanzas;
|
|
31313
|
+
if (hubStanza.getProperty("useOneFile") === "on") {
|
|
31314
|
+
// This is a "onefile" hub, all stanzas are in the same file
|
|
31315
|
+
if (stanzas[1].type !== "genome") {
|
|
31316
|
+
throw new Error("Unexpected hub file -- expected 'genome' stanza but found " + stanzas[1].type)
|
|
30757
31317
|
}
|
|
30758
|
-
|
|
30759
|
-
|
|
31318
|
+
const genomeStanza = stanzas[1];
|
|
31319
|
+
genomeStanzas = [genomeStanza];
|
|
31320
|
+
trackStanzas = stanzas.slice(2);
|
|
30760
31321
|
|
|
30761
|
-
|
|
30762
|
-
|
|
30763
|
-
|
|
30764
|
-
|
|
30765
|
-
|
|
30766
|
-
|
|
30767
|
-
|
|
30768
|
-
|
|
30769
|
-
|
|
30770
|
-
|
|
30771
|
-
|
|
30772
|
-
return "COLLAPSED"
|
|
30773
|
-
case "pack":
|
|
30774
|
-
return "EXPANDED"
|
|
30775
|
-
case "squish":
|
|
30776
|
-
return "SQUISHED"
|
|
30777
|
-
default:
|
|
30778
|
-
return "COLLAPSED"
|
|
31322
|
+
// If this is an assembly check chromSizes. This file can be very large, and not needed if whole genome view
|
|
31323
|
+
// is not enabled. Remove it if > 100 kb
|
|
31324
|
+
if (genomeStanza.hasOwnProperty("chromSizes")) {
|
|
31325
|
+
const chromSizes = genomeStanza.getProperty("chromSizes");
|
|
31326
|
+
try {
|
|
31327
|
+
const contentLength = await igvxhr.getContentLength(chromSizes);
|
|
31328
|
+
if (contentLength > 100000) {
|
|
31329
|
+
genomeStanza.removeProperty("chromSizes");
|
|
31330
|
+
}
|
|
31331
|
+
} catch (e) {
|
|
31332
|
+
console.error(`Error getting content length for chromSizes ${chromSizes}`, e);
|
|
30779
31333
|
}
|
|
30780
|
-
}
|
|
30781
|
-
}
|
|
30782
|
-
}
|
|
30783
31334
|
|
|
31335
|
+
}
|
|
30784
31336
|
|
|
30785
|
-
|
|
30786
|
-
|
|
30787
|
-
|
|
30788
|
-
* @returns {Promise<number|string>}
|
|
30789
|
-
*/
|
|
30790
|
-
async function getContentLength(url) {
|
|
30791
|
-
try {
|
|
30792
|
-
const response = await fetch(url, {method: 'HEAD'});
|
|
30793
|
-
const headers = response.headers;
|
|
30794
|
-
if (headers.has("content-length")) {
|
|
30795
|
-
return headers.get("content-length")
|
|
30796
|
-
} else {
|
|
30797
|
-
return null
|
|
31337
|
+
} else {
|
|
31338
|
+
if (!hubStanza.hasProperty("genomesFile")) {
|
|
31339
|
+
throw new Error("hub.txt must specify 'genomesFile'")
|
|
30798
31340
|
}
|
|
30799
|
-
|
|
30800
|
-
return null
|
|
31341
|
+
genomeStanzas = await loadStanzas(hubStanza.getProperty("genomesFile"));
|
|
30801
31342
|
}
|
|
31343
|
+
|
|
31344
|
+
// Load group files for all genomes, if any.
|
|
31345
|
+
const uniqGroupURLs = new Set();
|
|
31346
|
+
genomeStanzas.forEach(s => {
|
|
31347
|
+
const groupURL = s.getProperty("groups");
|
|
31348
|
+
if (groupURL) uniqGroupURLs.add(groupURL);
|
|
31349
|
+
|
|
31350
|
+
});
|
|
31351
|
+
const groupStanzas = [];
|
|
31352
|
+
const groupPromises = Array.from(uniqGroupURLs).map(async url => {
|
|
31353
|
+
const stanza = await loadStanzas(url);
|
|
31354
|
+
return stanza
|
|
31355
|
+
});
|
|
31356
|
+
const groupResults = await Promise.all(groupPromises);
|
|
31357
|
+
groupResults.forEach(stanza => groupStanzas.push(...stanza));
|
|
31358
|
+
|
|
31359
|
+
const hub = new Hub(url, hubStanza, genomeStanzas, trackStanzas, groupStanzas);
|
|
31360
|
+
|
|
31361
|
+
hubCache.set(url, hub);
|
|
31362
|
+
|
|
31363
|
+
return hub
|
|
30802
31364
|
}
|
|
30803
31365
|
|
|
31366
|
+
|
|
30804
31367
|
/**
|
|
30805
31368
|
* Parse a UCSC file
|
|
30806
31369
|
* @param url
|
|
@@ -30808,38 +31371,92 @@
|
|
|
30808
31371
|
*/
|
|
30809
31372
|
async function loadStanzas(url) {
|
|
30810
31373
|
|
|
30811
|
-
const
|
|
30812
|
-
const
|
|
31374
|
+
const idx = url.lastIndexOf("/");
|
|
31375
|
+
const baseURL = url.substring(0, idx + 1);
|
|
31376
|
+
const host = getHost(url);
|
|
31377
|
+
|
|
31378
|
+
//const response = await fetch(url)
|
|
31379
|
+
const data = await igvxhr.loadString(url, {}); //await response.text()
|
|
30813
31380
|
const lines = data.split(/\n|\r\n|\r/g);
|
|
30814
31381
|
|
|
30815
31382
|
const nodes = [];
|
|
30816
31383
|
let currentNode;
|
|
30817
31384
|
let startNewNode = true;
|
|
30818
|
-
for (let
|
|
30819
|
-
|
|
30820
|
-
|
|
30821
|
-
|
|
31385
|
+
for (let i = 0; i < lines.length; i++) {
|
|
31386
|
+
|
|
31387
|
+
let line = lines[i].trim();
|
|
31388
|
+
|
|
31389
|
+
if (line.length == 0) {
|
|
30822
31390
|
// Break - start a new node
|
|
30823
31391
|
startNewNode = true;
|
|
30824
31392
|
} else {
|
|
30825
|
-
|
|
30826
|
-
|
|
30827
|
-
|
|
31393
|
+
if (line.startsWith("#")) {
|
|
31394
|
+
continue
|
|
31395
|
+
}
|
|
31396
|
+
|
|
31397
|
+
while (line.endsWith('\\')) {
|
|
31398
|
+
i++;
|
|
31399
|
+
if (i >= lines.length) {
|
|
31400
|
+
break
|
|
31401
|
+
}
|
|
31402
|
+
line = line.substring(0, line.length - 1) + lines[i].trim();
|
|
31403
|
+
}
|
|
31404
|
+
|
|
31405
|
+
if (line.startsWith("include")) {
|
|
31406
|
+
const relativeURL = line.substring(8).trim();
|
|
31407
|
+
const includeURL = getDataURL(relativeURL, host, baseURL);
|
|
31408
|
+
const includeStanzas = await loadStanzas(includeURL);
|
|
31409
|
+
for (let s of includeStanzas) {
|
|
31410
|
+
nodes.push(s);
|
|
31411
|
+
}
|
|
31412
|
+
}
|
|
31413
|
+
|
|
31414
|
+
|
|
31415
|
+
const i = line.indexOf(' ');
|
|
31416
|
+
const key = line.substring(0, i).trim();
|
|
31417
|
+
let value = line.substring(i + 1).trim();
|
|
31418
|
+
|
|
31419
|
+
if (key === "type") {
|
|
31420
|
+
// The "type" property contains format and sometimes other information. For example, data range
|
|
31421
|
+
// on a bigwig "type bigWig 0 .5"
|
|
31422
|
+
const tokens = value.split(/\s+/);
|
|
31423
|
+
value = tokens[0];
|
|
31424
|
+
if (value === "bigWig" && tokens.length === 3) {
|
|
31425
|
+
// This is a bigWig with a range
|
|
31426
|
+
const min = tokens[1];
|
|
31427
|
+
const max = tokens[2];
|
|
31428
|
+
if (currentNode) {
|
|
31429
|
+
currentNode.setProperty("min", min);
|
|
31430
|
+
currentNode.setProperty("max", max);
|
|
31431
|
+
}
|
|
31432
|
+
}
|
|
31433
|
+
|
|
31434
|
+
} else if (!["shortLabel", "longLabel", "metadata", "label"].includes(key)) {
|
|
31435
|
+
const tokens = value.split(/\s+/);
|
|
31436
|
+
value = tokens[0];
|
|
31437
|
+
}
|
|
31438
|
+
|
|
31439
|
+
if (urlProperties.has(key) || value.endsWith("URL") || value.endsWith("Url")) {
|
|
31440
|
+
value = getDataURL(value, host, baseURL);
|
|
31441
|
+
}
|
|
31442
|
+
|
|
30828
31443
|
if (startNewNode) {
|
|
30829
|
-
|
|
30830
|
-
|
|
30831
|
-
const newNode = new Stanza(key, value);
|
|
30832
|
-
nodes.push(newNode);
|
|
30833
|
-
currentNode = newNode;
|
|
31444
|
+
currentNode = new Stanza(key, value);
|
|
31445
|
+
nodes.push(currentNode);
|
|
30834
31446
|
startNewNode = false;
|
|
30835
31447
|
}
|
|
31448
|
+
|
|
30836
31449
|
currentNode.setProperty(key, value);
|
|
30837
31450
|
}
|
|
30838
31451
|
}
|
|
30839
|
-
|
|
30840
31452
|
return resolveParents(nodes)
|
|
30841
31453
|
}
|
|
30842
31454
|
|
|
31455
|
+
function firstWord(str) {
|
|
31456
|
+
const idx = str.indexOf(' ');
|
|
31457
|
+
return idx > 0 ? str.substring(0, idx) : str
|
|
31458
|
+
}
|
|
31459
|
+
|
|
30843
31460
|
function resolveParents(nodes) {
|
|
30844
31461
|
const nodeMap = new Map();
|
|
30845
31462
|
for (let n of nodes) {
|
|
@@ -30854,13 +31471,30 @@
|
|
|
30854
31471
|
return nodes
|
|
30855
31472
|
}
|
|
30856
31473
|
|
|
30857
|
-
function
|
|
30858
|
-
|
|
30859
|
-
|
|
30860
|
-
|
|
30861
|
-
|
|
31474
|
+
function getDataURL(url, host, baseURL) {
|
|
31475
|
+
if (url.startsWith("http://") || url.startsWith("https://") || url.startsWith("gs://") || url.startsWith("s3://")) {
|
|
31476
|
+
return url
|
|
31477
|
+
} else if (url.startsWith("/")) {
|
|
31478
|
+
return host + url
|
|
31479
|
+
} else {
|
|
31480
|
+
return baseURL + url
|
|
30862
31481
|
}
|
|
30863
|
-
|
|
31482
|
+
}
|
|
31483
|
+
|
|
31484
|
+
function getHost(url) {
|
|
31485
|
+
let host;
|
|
31486
|
+
if (url.startsWith("https://") || url.startsWith("http://")) {
|
|
31487
|
+
try {
|
|
31488
|
+
const tmp = new URL(url);
|
|
31489
|
+
host = `${tmp.protocol}//${tmp.host}`;
|
|
31490
|
+
} catch (e) {
|
|
31491
|
+
console.error("Error parsing base URL host", e);
|
|
31492
|
+
throw e
|
|
31493
|
+
}
|
|
31494
|
+
} else {
|
|
31495
|
+
host = '';
|
|
31496
|
+
}
|
|
31497
|
+
return host
|
|
30864
31498
|
}
|
|
30865
31499
|
|
|
30866
31500
|
const DEFAULT_GENOMES_URL = "https://igv.org/genomes/genomes.json";
|
|
@@ -30944,8 +31578,8 @@
|
|
|
30944
31578
|
if ((genomeID.startsWith("GCA_") || genomeID.startsWith("GCF_")) && genomeID.length >= 13) {
|
|
30945
31579
|
try {
|
|
30946
31580
|
const hubURL = convertToHubURL(genomeID);
|
|
30947
|
-
const hub = await
|
|
30948
|
-
reference = hub.getGenomeConfig();
|
|
31581
|
+
const hub = await loadHub(hubURL);
|
|
31582
|
+
reference = hub.getGenomeConfig(genomeID);
|
|
30949
31583
|
} catch (e) {
|
|
30950
31584
|
console.error(e);
|
|
30951
31585
|
}
|
|
@@ -32907,27 +33541,18 @@
|
|
|
32907
33541
|
return [ r, g, b ]
|
|
32908
33542
|
});
|
|
32909
33543
|
|
|
32910
|
-
const colorForNA = appleCrayonRGB('magnesium');
|
|
32911
|
-
const sampleInfoFileHeaders = ['#sampleTable', '#sampleMapping', '#colors'];
|
|
32912
|
-
|
|
32913
33544
|
class SampleInfo {
|
|
32914
33545
|
|
|
32915
33546
|
static emptySpaceReplacement = '|'
|
|
32916
|
-
|
|
32917
|
-
|
|
32918
|
-
attributeNames = []
|
|
32919
|
-
sampleMappingDictionary = {}
|
|
32920
|
-
colorDictionary = {}
|
|
32921
|
-
attributeRangeLUT = {}
|
|
33547
|
+
static colorForNA = appleCrayonRGB('magnesium')
|
|
33548
|
+
static sampleInfoFileHeaders = ['#sampleTable', '#sampleMapping', '#colors']
|
|
32922
33549
|
|
|
32923
33550
|
constructor(browser) {
|
|
32924
|
-
|
|
32925
33551
|
const found = browser.tracks.some(t => typeof t.getSamples === 'function');
|
|
32926
33552
|
if (found.length > 0) {
|
|
32927
33553
|
browser.sampleInfoControl.setButtonVisibility(true);
|
|
32928
33554
|
}
|
|
32929
33555
|
this.initialize();
|
|
32930
|
-
|
|
32931
33556
|
}
|
|
32932
33557
|
|
|
32933
33558
|
initialize() {
|
|
@@ -32954,47 +33579,60 @@
|
|
|
32954
33579
|
|
|
32955
33580
|
getAttributes(sampleName) {
|
|
32956
33581
|
|
|
32957
|
-
const key =
|
|
33582
|
+
const key = this.sampleMappingDictionary[sampleName] || sampleName;
|
|
32958
33583
|
return this.sampleDictionary[key]
|
|
32959
33584
|
}
|
|
32960
33585
|
|
|
32961
|
-
async
|
|
32962
|
-
|
|
32963
|
-
|
|
32964
|
-
this
|
|
32965
|
-
|
|
32966
|
-
|
|
32967
|
-
|
|
33586
|
+
async loadSampleInfo(config) {
|
|
33587
|
+
|
|
33588
|
+
if (config.url) {
|
|
33589
|
+
await this.loadSampleInfoFile(config.url);
|
|
33590
|
+
} else {
|
|
33591
|
+
|
|
33592
|
+
const samples = { ...config };
|
|
33593
|
+
for (const [key, record] of Object.entries(samples)) {
|
|
33594
|
+
samples[key] = SampleInfo.toNumericalRepresentation(record);
|
|
33595
|
+
}
|
|
33596
|
+
|
|
33597
|
+
const [ value ] = Object.values(samples);
|
|
33598
|
+
const attributes = Object.keys(value);
|
|
33599
|
+
|
|
33600
|
+
this.loadSampleInfoHelper(attributes, samples);
|
|
33601
|
+
|
|
32968
33602
|
}
|
|
32969
|
-
}
|
|
32970
33603
|
|
|
32971
|
-
|
|
33604
|
+
this.initialized = true;
|
|
33605
|
+
}
|
|
32972
33606
|
|
|
32973
|
-
|
|
33607
|
+
loadSampleInfoHelper(attributes, samples){
|
|
32974
33608
|
|
|
32975
|
-
|
|
32976
|
-
|
|
32977
|
-
|
|
32978
|
-
this.#accumulateSampleTableDictionary(value);
|
|
32979
|
-
break
|
|
32980
|
-
case '#sampleMapping':
|
|
32981
|
-
this.#accumulateSampleMappingDictionary(value);
|
|
32982
|
-
break
|
|
32983
|
-
case '#colors':
|
|
32984
|
-
this.#accumulateColorScheme(value);
|
|
32985
|
-
break
|
|
33609
|
+
// Establish the range of values for each attribute
|
|
33610
|
+
const lut = createAttributeRangeLUT(attributes, samples);
|
|
33611
|
+
accumulateDictionary(this.attributeRangeLUT, lut);
|
|
32986
33612
|
|
|
33613
|
+
// Ensure unique attribute names list
|
|
33614
|
+
const currentAttributeNameSet = new Set(this.attributeNames);
|
|
33615
|
+
for (const name of attributes) {
|
|
33616
|
+
if (!currentAttributeNameSet.has(name)) {
|
|
33617
|
+
this.attributeNames.push(name);
|
|
32987
33618
|
}
|
|
32988
33619
|
}
|
|
32989
33620
|
|
|
32990
|
-
this.
|
|
33621
|
+
accumulateDictionary(this.sampleDictionary, samples);
|
|
32991
33622
|
|
|
32992
33623
|
}
|
|
32993
33624
|
|
|
33625
|
+
async loadSampleInfoFile(path) {
|
|
33626
|
+
try {
|
|
33627
|
+
const string = await igvxhr.loadString(path);
|
|
33628
|
+
this.#processSampleInfoFileAsString(string);
|
|
33629
|
+
this.sampleInfoFiles.push(path);
|
|
33630
|
+
} catch (e) {
|
|
33631
|
+
console.error(e.message);
|
|
33632
|
+
}
|
|
33633
|
+
}
|
|
33634
|
+
|
|
32994
33635
|
getAttributeColor(attribute, value) {
|
|
32995
|
-
// if (value === 'NA') {
|
|
32996
|
-
// console.log(`${ attribute } : ${ value }`)
|
|
32997
|
-
// }
|
|
32998
33636
|
|
|
32999
33637
|
let color;
|
|
33000
33638
|
|
|
@@ -33012,7 +33650,7 @@
|
|
|
33012
33650
|
|
|
33013
33651
|
} else if (typeof value === "string") {
|
|
33014
33652
|
|
|
33015
|
-
color = 'NA' === value ? colorForNA : stringToRGBString(value);
|
|
33653
|
+
color = 'NA' === value ? SampleInfo.colorForNA : SampleInfo.stringToRGBString(value);
|
|
33016
33654
|
|
|
33017
33655
|
} else {
|
|
33018
33656
|
|
|
@@ -33080,6 +33718,27 @@
|
|
|
33080
33718
|
return json
|
|
33081
33719
|
}
|
|
33082
33720
|
|
|
33721
|
+
#processSampleInfoFileAsString(string) {
|
|
33722
|
+
|
|
33723
|
+
const sectionDictionary = createSectionDictionary(string);
|
|
33724
|
+
|
|
33725
|
+
for (const [header, value] of Object.entries(sectionDictionary)) {
|
|
33726
|
+
switch (header) {
|
|
33727
|
+
case '#sampleTable':
|
|
33728
|
+
this.#accumulateSampleTableDictionary(value);
|
|
33729
|
+
break
|
|
33730
|
+
case '#sampleMapping':
|
|
33731
|
+
this.#accumulateSampleMappingDictionary(value);
|
|
33732
|
+
break
|
|
33733
|
+
case '#colors':
|
|
33734
|
+
this.#accumulateColorScheme(value);
|
|
33735
|
+
break
|
|
33736
|
+
|
|
33737
|
+
}
|
|
33738
|
+
}
|
|
33739
|
+
|
|
33740
|
+
}
|
|
33741
|
+
|
|
33083
33742
|
#accumulateSampleTableDictionary(lines) {
|
|
33084
33743
|
|
|
33085
33744
|
// shift array with first item that is 'sample' or 'Linking_id'. Remaining items are attribute names
|
|
@@ -33119,22 +33778,11 @@
|
|
|
33119
33778
|
} // for (lines)
|
|
33120
33779
|
|
|
33121
33780
|
for (const [key, record] of Object.entries(samples)) {
|
|
33122
|
-
samples[key] = toNumericalRepresentation(record);
|
|
33781
|
+
samples[key] = SampleInfo.toNumericalRepresentation(record);
|
|
33123
33782
|
}
|
|
33124
33783
|
|
|
33125
|
-
|
|
33126
|
-
const lut = createAttributeRangeLUT(attributes, samples);
|
|
33127
|
-
accumulateDictionary(this.attributeRangeLUT, lut);
|
|
33128
|
-
|
|
33129
|
-
// Ensure unique attribute names list
|
|
33130
|
-
const currentAttributeNameSet = new Set(this.attributeNames);
|
|
33131
|
-
for (const name of attributes) {
|
|
33132
|
-
if (!currentAttributeNameSet.has(name)) {
|
|
33133
|
-
this.attributeNames.push(name);
|
|
33134
|
-
}
|
|
33135
|
-
}
|
|
33784
|
+
this.loadSampleInfoHelper(attributes, samples);
|
|
33136
33785
|
|
|
33137
|
-
accumulateDictionary(this.sampleDictionary, samples);
|
|
33138
33786
|
}
|
|
33139
33787
|
|
|
33140
33788
|
#accumulateSampleMappingDictionary(lines) {
|
|
@@ -33235,7 +33883,7 @@
|
|
|
33235
33883
|
this.colorDictionary[attribute] = attributeValue => {
|
|
33236
33884
|
|
|
33237
33885
|
if ('NA' === attributeValue) {
|
|
33238
|
-
return colorForNA
|
|
33886
|
+
return SampleInfo.colorForNA
|
|
33239
33887
|
} else {
|
|
33240
33888
|
const [min, max] = this.attributeRangeLUT[attribute];
|
|
33241
33889
|
const interpolant = (attributeValue - min) / (max - min);
|
|
@@ -33255,8 +33903,34 @@
|
|
|
33255
33903
|
|
|
33256
33904
|
}
|
|
33257
33905
|
|
|
33258
|
-
|
|
33906
|
+
static toNumericalRepresentation(obj) {
|
|
33907
|
+
const result = Object.assign({}, obj);
|
|
33259
33908
|
|
|
33909
|
+
for (const [key, value] of Object.entries(result)) {
|
|
33910
|
+
if (typeof value === 'string' && !isNaN(value)) {
|
|
33911
|
+
result[key] = Number(value);
|
|
33912
|
+
}
|
|
33913
|
+
}
|
|
33914
|
+
|
|
33915
|
+
return result
|
|
33916
|
+
}
|
|
33917
|
+
|
|
33918
|
+
static stringToRGBString(str) {
|
|
33919
|
+
let hash = 0;
|
|
33920
|
+
for (let i = 0; i < str.length; i++) {
|
|
33921
|
+
hash = str.charCodeAt(i) + ((hash << 5) - hash);
|
|
33922
|
+
}
|
|
33923
|
+
|
|
33924
|
+
let color = [];
|
|
33925
|
+
for (let i = 0; i < 3; i++) {
|
|
33926
|
+
const value = (hash >> (i * 8)) & 0xff;
|
|
33927
|
+
color.push(value);
|
|
33928
|
+
}
|
|
33929
|
+
|
|
33930
|
+
return `rgb(${color.join(', ')})`
|
|
33931
|
+
}
|
|
33932
|
+
|
|
33933
|
+
}
|
|
33260
33934
|
|
|
33261
33935
|
function createSectionDictionary(string) {
|
|
33262
33936
|
|
|
@@ -33267,14 +33941,14 @@
|
|
|
33267
33941
|
let currentHeader;
|
|
33268
33942
|
|
|
33269
33943
|
// If the first line does not start with a section header an initial #sampleTable is implied
|
|
33270
|
-
if (!sampleInfoFileHeaders.includes(lines[0])) {
|
|
33944
|
+
if (!SampleInfo.sampleInfoFileHeaders.includes(lines[0])) {
|
|
33271
33945
|
currentHeader = '#sampleTable';
|
|
33272
33946
|
dictionary[currentHeader] = [];
|
|
33273
33947
|
}
|
|
33274
33948
|
|
|
33275
33949
|
for (const line of lines) {
|
|
33276
33950
|
|
|
33277
|
-
if (sampleInfoFileHeaders.includes(line)) {
|
|
33951
|
+
if (SampleInfo.sampleInfoFileHeaders.includes(line)) {
|
|
33278
33952
|
currentHeader = line;
|
|
33279
33953
|
dictionary[currentHeader] = [];
|
|
33280
33954
|
} else if (currentHeader && false === line.startsWith('#')) {
|
|
@@ -33293,7 +33967,6 @@
|
|
|
33293
33967
|
}
|
|
33294
33968
|
}
|
|
33295
33969
|
|
|
33296
|
-
|
|
33297
33970
|
function createAttributeRangeLUT(names, dictionary) {
|
|
33298
33971
|
|
|
33299
33972
|
const lut = {};
|
|
@@ -33339,35 +34012,6 @@
|
|
|
33339
34012
|
return lut
|
|
33340
34013
|
}
|
|
33341
34014
|
|
|
33342
|
-
function toNumericalRepresentation(obj) {
|
|
33343
|
-
|
|
33344
|
-
const result = Object.assign({}, obj);
|
|
33345
|
-
|
|
33346
|
-
for (const [key, value] of Object.entries(result)) {
|
|
33347
|
-
if (typeof value === 'string' && !isNaN(value)) {
|
|
33348
|
-
result[key] = Number(value);
|
|
33349
|
-
}
|
|
33350
|
-
}
|
|
33351
|
-
|
|
33352
|
-
return result
|
|
33353
|
-
}
|
|
33354
|
-
|
|
33355
|
-
function stringToRGBString(str) {
|
|
33356
|
-
|
|
33357
|
-
let hash = 0;
|
|
33358
|
-
for (let i = 0; i < str.length; i++) {
|
|
33359
|
-
hash = str.charCodeAt(i) + ((hash << 5) - hash);
|
|
33360
|
-
}
|
|
33361
|
-
|
|
33362
|
-
let color = [];
|
|
33363
|
-
for (let i = 0; i < 3; i++) {
|
|
33364
|
-
const value = (hash >> (i * 8)) & 0xff;
|
|
33365
|
-
color.push(value);
|
|
33366
|
-
}
|
|
33367
|
-
|
|
33368
|
-
return `rgb(${color.join(', ')})`
|
|
33369
|
-
}
|
|
33370
|
-
|
|
33371
34015
|
const sampleInfoTileXShim = 8;
|
|
33372
34016
|
const sampleInfoTileWidth = 16;
|
|
33373
34017
|
|
|
@@ -33976,7 +34620,11 @@
|
|
|
33976
34620
|
|
|
33977
34621
|
checkCanvas() {
|
|
33978
34622
|
|
|
33979
|
-
|
|
34623
|
+
let width = 0;
|
|
34624
|
+
if (true === this.browser.showSampleNames) {
|
|
34625
|
+
width = undefined === this.browser.sampleNameViewportWidth ? 0 : this.browser.sampleNameViewportWidth;
|
|
34626
|
+
}
|
|
34627
|
+
|
|
33980
34628
|
this.ctx.canvas.width = width * window.devicePixelRatio;
|
|
33981
34629
|
this.ctx.canvas.style.width = `${width}px`;
|
|
33982
34630
|
|
|
@@ -35088,7 +35736,7 @@
|
|
|
35088
35736
|
let element = document.createElement('div');
|
|
35089
35737
|
element.textContent = 'Separate tracks';
|
|
35090
35738
|
|
|
35091
|
-
function click(e) {
|
|
35739
|
+
async function click(e) {
|
|
35092
35740
|
|
|
35093
35741
|
// Capture state which will be nulled when track is removed
|
|
35094
35742
|
const groupAutoscale = this.autoscale;
|
|
@@ -35104,9 +35752,10 @@
|
|
|
35104
35752
|
track.autoscaleGroup = name;
|
|
35105
35753
|
}
|
|
35106
35754
|
track.isMergedTrack = false;
|
|
35107
|
-
browser.addTrack(track
|
|
35755
|
+
browser.addTrack(track);
|
|
35108
35756
|
}
|
|
35109
|
-
browser.updateViews();
|
|
35757
|
+
await browser.updateViews();
|
|
35758
|
+
browser.reorderTracks();
|
|
35110
35759
|
}
|
|
35111
35760
|
|
|
35112
35761
|
return {element, click}
|
|
@@ -35201,7 +35850,7 @@
|
|
|
35201
35850
|
}
|
|
35202
35851
|
}
|
|
35203
35852
|
|
|
35204
|
-
function trackOverlayClickHandler(e) {
|
|
35853
|
+
async function trackOverlayClickHandler(e) {
|
|
35205
35854
|
|
|
35206
35855
|
if (true === isOverlayTrackCriteriaMet(this.browser)) {
|
|
35207
35856
|
|
|
@@ -35238,9 +35887,9 @@
|
|
|
35238
35887
|
track.trackView.dispose();
|
|
35239
35888
|
}
|
|
35240
35889
|
|
|
35241
|
-
this.browser.addTrack(
|
|
35242
|
-
mergedTrack.trackView.updateViews();
|
|
35243
|
-
|
|
35890
|
+
this.browser.addTrack(mergedTrack);
|
|
35891
|
+
await mergedTrack.trackView.updateViews();
|
|
35892
|
+
this.browser.reorderTracks();
|
|
35244
35893
|
}
|
|
35245
35894
|
|
|
35246
35895
|
}
|
|
@@ -36847,14 +37496,45 @@
|
|
|
36847
37496
|
|
|
36848
37497
|
|
|
36849
37498
|
present(options, e) {
|
|
36850
|
-
|
|
36851
37499
|
this.label.textContent = options.label;
|
|
36852
37500
|
this._input.value = options.value;
|
|
36853
37501
|
this.callback = options.callback || options.click;
|
|
36854
37502
|
|
|
36855
|
-
|
|
36856
|
-
|
|
36857
|
-
|
|
37503
|
+
this.container.style.display = '';
|
|
37504
|
+
|
|
37505
|
+
// Get click coordinates
|
|
37506
|
+
const clickX = e.clientX;
|
|
37507
|
+
const clickY = e.clientY;
|
|
37508
|
+
|
|
37509
|
+
// Get dialog dimensions
|
|
37510
|
+
const dialogWidth = this.container.offsetWidth;
|
|
37511
|
+
const dialogHeight = this.container.offsetHeight;
|
|
37512
|
+
|
|
37513
|
+
// Calculate available space
|
|
37514
|
+
const windowWidth = window.innerWidth;
|
|
37515
|
+
const windowHeight = window.innerHeight;
|
|
37516
|
+
|
|
37517
|
+
// Calculate position to keep dialog on screen
|
|
37518
|
+
let left = clickX;
|
|
37519
|
+
let top = clickY;
|
|
37520
|
+
|
|
37521
|
+
// Adjust horizontal position if dialog would go off screen
|
|
37522
|
+
if (left + dialogWidth > windowWidth) {
|
|
37523
|
+
left = windowWidth - dialogWidth - 10; // 10px padding from edge
|
|
37524
|
+
}
|
|
37525
|
+
|
|
37526
|
+
// Adjust vertical position if dialog would go off screen
|
|
37527
|
+
if (top + dialogHeight > windowHeight) {
|
|
37528
|
+
top = windowHeight - dialogHeight - 10; // 10px padding from edge
|
|
37529
|
+
}
|
|
37530
|
+
|
|
37531
|
+
// Ensure minimum distance from edges
|
|
37532
|
+
left = Math.max(10, left);
|
|
37533
|
+
top = Math.max(10, top);
|
|
37534
|
+
|
|
37535
|
+
// Apply positions
|
|
37536
|
+
this.container.style.left = `${left}px`;
|
|
37537
|
+
this.container.style.top = `${top}px`;
|
|
36858
37538
|
}
|
|
36859
37539
|
}
|
|
36860
37540
|
|
|
@@ -50502,7 +51182,7 @@
|
|
|
50502
51182
|
|
|
50503
51183
|
let url = this.url.slice(); // slice => copy
|
|
50504
51184
|
if (this.config.oauthToken) {
|
|
50505
|
-
const token = resolveToken(this.config.oauthToken);
|
|
51185
|
+
const token = await resolveToken(this.config.oauthToken);
|
|
50506
51186
|
headers['Authorization'] = `Bearer ${token}`;
|
|
50507
51187
|
}
|
|
50508
51188
|
|
|
@@ -55267,7 +55947,7 @@
|
|
|
55267
55947
|
async function openH5File(options) {
|
|
55268
55948
|
|
|
55269
55949
|
// Some clients (notably igv-webapp) pass a File reference in the url field. Fix this
|
|
55270
|
-
if(options.url && isBlobLike(options.url)) {
|
|
55950
|
+
if (options.url && isBlobLike(options.url)) {
|
|
55271
55951
|
options.file = options.url;
|
|
55272
55952
|
options.url = undefined;
|
|
55273
55953
|
}
|
|
@@ -55297,26 +55977,30 @@
|
|
|
55297
55977
|
|
|
55298
55978
|
async function readExternalIndex(options) {
|
|
55299
55979
|
|
|
55300
|
-
|
|
55301
|
-
if(options.indexReader) {
|
|
55302
|
-
indexReader = options.indexReader;
|
|
55303
|
-
}
|
|
55304
|
-
else if(options.index) {
|
|
55980
|
+
if (options.index) {
|
|
55305
55981
|
return options.index
|
|
55306
|
-
} else
|
|
55307
|
-
indexReader
|
|
55308
|
-
|
|
55309
|
-
|
|
55310
|
-
|
|
55311
|
-
|
|
55312
|
-
|
|
55313
|
-
|
|
55982
|
+
} else {
|
|
55983
|
+
let indexReader;
|
|
55984
|
+
if (options.indexReader) {
|
|
55985
|
+
indexReader = options.indexReader;
|
|
55986
|
+
} else {
|
|
55987
|
+
let indexOptions;
|
|
55988
|
+
if (options.indexURL) {
|
|
55989
|
+
indexOptions = Object.assign({url: options.indexURL}, options);
|
|
55990
|
+
} else if (options.indexPath) {
|
|
55991
|
+
indexOptions = Object.assign({path: options.indexPath}, options);
|
|
55992
|
+
} else if (options.indexFile) {
|
|
55993
|
+
indexOptions = Object.assign({file: options.indexFile}, options);
|
|
55994
|
+
} else {
|
|
55995
|
+
return undefined
|
|
55996
|
+
}
|
|
55997
|
+
indexReader = getReaderFor(indexOptions);
|
|
55998
|
+
}
|
|
55314
55999
|
const indexFileContents = await indexReader.read();
|
|
55315
56000
|
const indexFileJson = new TextDecoder().decode(indexFileContents);
|
|
55316
56001
|
return JSON.parse(indexFileJson)
|
|
55317
|
-
} else {
|
|
55318
|
-
return undefined
|
|
55319
56002
|
}
|
|
56003
|
+
|
|
55320
56004
|
}
|
|
55321
56005
|
|
|
55322
56006
|
|
|
@@ -55394,9 +56078,9 @@
|
|
|
55394
56078
|
* @param {string} h5_file - path for the pytor file
|
|
55395
56079
|
* @param {integer} bin_size - bin size
|
|
55396
56080
|
*/
|
|
55397
|
-
constructor(
|
|
56081
|
+
constructor(config, bin_size=100000){
|
|
55398
56082
|
|
|
55399
|
-
this.
|
|
56083
|
+
this.config = config;
|
|
55400
56084
|
this.bin_size = bin_size;
|
|
55401
56085
|
this.h5_obj = undefined;
|
|
55402
56086
|
this.pytorKeys = [];
|
|
@@ -55406,11 +56090,8 @@
|
|
|
55406
56090
|
async fetch(){
|
|
55407
56091
|
|
|
55408
56092
|
if(!this.h5_obj) {
|
|
55409
|
-
this.
|
|
55410
|
-
|
|
55411
|
-
fetchSize: 1000000,
|
|
55412
|
-
maxSize: 200000000
|
|
55413
|
-
});
|
|
56093
|
+
const options = Object.assign(this.config, {fetchSize: 1000000, maxSize: 200000000});
|
|
56094
|
+
this.h5_obj = await openH5File(options);
|
|
55414
56095
|
}
|
|
55415
56096
|
return this.h5_obj
|
|
55416
56097
|
}
|
|
@@ -64446,7 +65127,7 @@ ${indent}columns: ${matrix.columns}
|
|
|
64446
65127
|
this.set_available_callers();
|
|
64447
65128
|
|
|
64448
65129
|
} else {
|
|
64449
|
-
this.cnvpytor_obj = new HDF5Reader(this.config
|
|
65130
|
+
this.cnvpytor_obj = new HDF5Reader(this.config, this.bin_size);
|
|
64450
65131
|
// get chrom list that currently user viewing
|
|
64451
65132
|
let chroms = [ ...new Set(this.browser.referenceFrameList.map(val => val.chr))];
|
|
64452
65133
|
|
|
@@ -69434,6 +70115,23 @@ ${indent}columns: ${matrix.columns}
|
|
|
69434
70115
|
return this.genome.getChromosome(this.chr)
|
|
69435
70116
|
}
|
|
69436
70117
|
|
|
70118
|
+
/**
|
|
70119
|
+
* Update reference frame based on new viewport width
|
|
70120
|
+
* @param {number} viewportWidth - The calculated viewport width
|
|
70121
|
+
*/
|
|
70122
|
+
updateForViewportWidth(viewportWidth) {
|
|
70123
|
+
const {chr} = this;
|
|
70124
|
+
const {bpLength} = this.getChromosome();
|
|
70125
|
+
const viewportWidthBP = this.toBP(viewportWidth);
|
|
70126
|
+
|
|
70127
|
+
// viewportWidthBP > bpLength occurs when locus is full chromosome and user widens browser
|
|
70128
|
+
if (GenomeUtils.isWholeGenomeView(chr) || viewportWidthBP > bpLength) {
|
|
70129
|
+
this.bpPerPixel = bpLength / viewportWidth;
|
|
70130
|
+
} else {
|
|
70131
|
+
this.end = this.start + this.toBP(viewportWidth);
|
|
70132
|
+
}
|
|
70133
|
+
}
|
|
70134
|
+
|
|
69437
70135
|
getMultiLocusLabelBPLengthOnly(pixels) {
|
|
69438
70136
|
const margin = ' ';
|
|
69439
70137
|
const ss = Math.floor(this.start) + 1;
|
|
@@ -69519,7 +70217,7 @@ ${indent}columns: ${matrix.columns}
|
|
|
69519
70217
|
})
|
|
69520
70218
|
}
|
|
69521
70219
|
|
|
69522
|
-
const _version = "3.
|
|
70220
|
+
const _version = "3.3.0";
|
|
69523
70221
|
function version() {
|
|
69524
70222
|
return _version
|
|
69525
70223
|
}
|
|
@@ -69563,10 +70261,24 @@ ${indent}columns: ${matrix.columns}
|
|
|
69563
70261
|
this.select.setAttribute('name', 'chromosome-select-widget');
|
|
69564
70262
|
this.container.appendChild(this.select);
|
|
69565
70263
|
|
|
69566
|
-
this.select.addEventListener('change', () => {
|
|
70264
|
+
this.select.addEventListener('change', async () => {
|
|
69567
70265
|
this.select.blur();
|
|
69568
70266
|
if (this.select.value !== '' && maximumSequenceCountExceeded !== this.select.value) {
|
|
69569
|
-
|
|
70267
|
+
|
|
70268
|
+
if (this.select.value.trim().toLowerCase() === "all" || this.select.value === "*") {
|
|
70269
|
+
if (browser.genome.wholeGenomeView) {
|
|
70270
|
+
const wgChr = browser.genome.getChromosome("all");
|
|
70271
|
+
return {chr: "all", start: 0, end: wgChr.bpLength}
|
|
70272
|
+
}
|
|
70273
|
+
} else {
|
|
70274
|
+
const chromosome = await browser.genome.loadChromosome(this.select.value);
|
|
70275
|
+
const locusObject = {chr: chromosome.name};
|
|
70276
|
+
if (locusObject.start === undefined && locusObject.end === undefined) {
|
|
70277
|
+
locusObject.start = 0;
|
|
70278
|
+
locusObject.end = chromosome.bpLength;
|
|
70279
|
+
}
|
|
70280
|
+
browser.updateLoci([locusObject]);
|
|
70281
|
+
}
|
|
69570
70282
|
}
|
|
69571
70283
|
});
|
|
69572
70284
|
|
|
@@ -69591,7 +70303,9 @@ ${indent}columns: ${matrix.columns}
|
|
|
69591
70303
|
this.genome = genome;
|
|
69592
70304
|
|
|
69593
70305
|
// Start with explicit chromosome name list
|
|
69594
|
-
const list = genome.wgChromosomeNames
|
|
70306
|
+
const list = (genome.wgChromosomeNames) ?
|
|
70307
|
+
genome.wgChromosomeNames.map(nm => genome.getChromosomeDisplayName(nm)) :
|
|
70308
|
+
[];
|
|
69595
70309
|
|
|
69596
70310
|
if (this.showAllChromosomes && genome.chromosomeNames.length > 1) {
|
|
69597
70311
|
const exclude = new Set(list);
|
|
@@ -70817,7 +71531,7 @@ ${indent}columns: ${matrix.columns}
|
|
|
70817
71531
|
});
|
|
70818
71532
|
|
|
70819
71533
|
this.searchInput.addEventListener('change', () => {
|
|
70820
|
-
|
|
71534
|
+
this.doSearch(this.searchInput.value);
|
|
70821
71535
|
});
|
|
70822
71536
|
|
|
70823
71537
|
const searchIconContainer = document.createElement('div');
|
|
@@ -70828,7 +71542,7 @@ ${indent}columns: ${matrix.columns}
|
|
|
70828
71542
|
searchIconContainer.appendChild(searchIcon);
|
|
70829
71543
|
|
|
70830
71544
|
searchIconContainer.addEventListener('click', () => {
|
|
70831
|
-
|
|
71545
|
+
this.doSearch(this.searchInput.value);
|
|
70832
71546
|
});
|
|
70833
71547
|
|
|
70834
71548
|
this.windowSizePanel = new WindowSizePanel(locusSizeGroup, browser);
|
|
@@ -70976,6 +71690,22 @@ ${indent}columns: ${matrix.columns}
|
|
|
70976
71690
|
this.navigation.style.display = 'flex';
|
|
70977
71691
|
}
|
|
70978
71692
|
|
|
71693
|
+
/**
|
|
71694
|
+
|
|
71695
|
+
* Search for the locus string -- this function is called from the navbar search box, and is not part of the API.
|
|
71696
|
+
* Wraps ```search``` and presents an error dialog if false.
|
|
71697
|
+
*
|
|
71698
|
+
* @param locus
|
|
71699
|
+
* @param init
|
|
71700
|
+
* @returns {Promise<void>}
|
|
71701
|
+
*/
|
|
71702
|
+
async doSearch(locus) {
|
|
71703
|
+
const success = await this.browser.search(locus);
|
|
71704
|
+
if (!success) {
|
|
71705
|
+
this.browser.alert.present(new Error(`Unrecognized locus: <b> ${locus} </b>`));
|
|
71706
|
+
}
|
|
71707
|
+
}
|
|
71708
|
+
|
|
70979
71709
|
}
|
|
70980
71710
|
|
|
70981
71711
|
function logo() {
|
|
@@ -72227,6 +72957,9 @@ ${indent}columns: ${matrix.columns}
|
|
|
72227
72957
|
aliasRecord["_cap"] = cap;
|
|
72228
72958
|
}
|
|
72229
72959
|
|
|
72960
|
+
const chrPrefixAlias = aliasRecord.chr.startsWith("chr") ? aliasRecord.chr.substring(3) : "chr" + aliasRecord.chr;
|
|
72961
|
+
aliasRecord["_chrprefix_"] = chrPrefixAlias;
|
|
72962
|
+
|
|
72230
72963
|
}
|
|
72231
72964
|
|
|
72232
72965
|
}
|
|
@@ -72251,7 +72984,7 @@ ${indent}columns: ${matrix.columns}
|
|
|
72251
72984
|
}
|
|
72252
72985
|
|
|
72253
72986
|
async preload(chrNames) {
|
|
72254
|
-
|
|
72987
|
+
await this.reader.preload();
|
|
72255
72988
|
for(let nm of chrNames) {
|
|
72256
72989
|
await this.search(nm);
|
|
72257
72990
|
}
|
|
@@ -72303,12 +73036,6 @@ ${indent}columns: ${matrix.columns}
|
|
|
72303
73036
|
}
|
|
72304
73037
|
return this.aliasRecordCache.get(alias)
|
|
72305
73038
|
}
|
|
72306
|
-
|
|
72307
|
-
async getChromosomeNames() {
|
|
72308
|
-
await this.reader.loadHeader();
|
|
72309
|
-
return Array.from(this.reader.chrNames)
|
|
72310
|
-
}
|
|
72311
|
-
|
|
72312
73039
|
}
|
|
72313
73040
|
|
|
72314
73041
|
/**
|
|
@@ -72333,10 +73060,9 @@ ${indent}columns: ${matrix.columns}
|
|
|
72333
73060
|
this.genome = genome;
|
|
72334
73061
|
}
|
|
72335
73062
|
|
|
72336
|
-
async preload() {
|
|
72337
|
-
|
|
73063
|
+
async preload(chrNames) {
|
|
73064
|
+
// A no-op, this is a text file, no need to preload
|
|
72338
73065
|
}
|
|
72339
|
-
|
|
72340
73066
|
/**
|
|
72341
73067
|
* Return the canonical chromosome name for the alias. If none found return the alias
|
|
72342
73068
|
*
|
|
@@ -72502,7 +73228,7 @@ ${indent}columns: ${matrix.columns}
|
|
|
72502
73228
|
for (let line of lines) {
|
|
72503
73229
|
|
|
72504
73230
|
const tokens = line.split("\t");
|
|
72505
|
-
const chrName = tokens[0];
|
|
73231
|
+
const chrName = tokens[0];
|
|
72506
73232
|
if (!lastChr) lastChr = chrName;
|
|
72507
73233
|
|
|
72508
73234
|
if (chrName !== lastChr) {
|
|
@@ -72520,37 +73246,20 @@ ${indent}columns: ${matrix.columns}
|
|
|
72520
73246
|
bands.push(new Cytoband(start, end, name, stain));
|
|
72521
73247
|
}
|
|
72522
73248
|
}
|
|
72523
|
-
|
|
72524
|
-
|
|
72525
|
-
|
|
72526
|
-
/**
|
|
72527
|
-
* Infer genome chromosome names from cytoband data. This should only be used as a last resort
|
|
72528
|
-
*/
|
|
72529
|
-
async getChromosomeNames() {
|
|
72530
|
-
if(this.cytobands.size === 0) {
|
|
72531
|
-
await this.#loadCytobands();
|
|
73249
|
+
if(bands.length > 0) {
|
|
73250
|
+
this.cytobands.set(lastChr, bands);
|
|
72532
73251
|
}
|
|
72533
|
-
return Array.from(this.cytobands.keys())
|
|
72534
|
-
}
|
|
72535
73252
|
|
|
72536
|
-
/**
|
|
72537
|
-
* Infer chromosome objects from cytoband data. This should only be used as last resort.
|
|
72538
|
-
*/
|
|
72539
|
-
async getChromosomes() {
|
|
72540
|
-
if(this.cytobands.size === 0) {
|
|
72541
|
-
await this.#loadCytobands();
|
|
72542
|
-
}
|
|
72543
|
-
|
|
72544
|
-
const chromosomes = [];
|
|
72545
|
-
let order = 0;
|
|
72546
|
-
for(let [chrName, cytoList] of this.cytobands.entries()) {
|
|
72547
|
-
chromosomes.push(new Chromosome(chrName, order++, cytoList[cytoList.length - 1].end));
|
|
72548
|
-
}
|
|
72549
|
-
return chromosomes
|
|
72550
73253
|
}
|
|
72551
73254
|
|
|
72552
73255
|
}
|
|
72553
73256
|
|
|
73257
|
+
const ucsdIDMap = new Map([
|
|
73258
|
+
["1kg_ref", "hg18"],
|
|
73259
|
+
["1kg_v37", "hg19"],
|
|
73260
|
+
["b37", "hg19"]
|
|
73261
|
+
]);
|
|
73262
|
+
|
|
72554
73263
|
/**
|
|
72555
73264
|
* The Genome class represents an assembly and consists of the following elements
|
|
72556
73265
|
* sequence - Object representing the DNA sequence
|
|
@@ -72575,8 +73284,10 @@ ${indent}columns: ${matrix.columns}
|
|
|
72575
73284
|
this.config = config;
|
|
72576
73285
|
this.browser = browser;
|
|
72577
73286
|
this.id = config.id || generateGenomeID(config);
|
|
73287
|
+
this.ucscID = config.ucscID || ucsdIDMap.get(this.id) || this.id;
|
|
73288
|
+
this.blatDB = config.blatDB || this.ucscID;
|
|
72578
73289
|
this.name = config.name;
|
|
72579
|
-
this.nameSet = config.nameSet;
|
|
73290
|
+
this.nameSet = config.nameSet || 'ucsc';
|
|
72580
73291
|
}
|
|
72581
73292
|
|
|
72582
73293
|
|
|
@@ -72584,49 +73295,45 @@ ${indent}columns: ${matrix.columns}
|
|
|
72584
73295
|
|
|
72585
73296
|
const config = this.config;
|
|
72586
73297
|
|
|
73298
|
+
// Load sequence
|
|
72587
73299
|
this.sequence = await loadSequence(config, this.browser);
|
|
72588
73300
|
|
|
72589
|
-
|
|
72590
|
-
|
|
73301
|
+
|
|
73302
|
+
// Load cytobands. This is optional but required to support the ideogram. Only needed for whole genome view
|
|
73303
|
+
if(false !== config.showIdeogram && false !== config.wholeGenomeView) {
|
|
73304
|
+
if (config.cytobandURL) {
|
|
73305
|
+
this.cytobandSource = new CytobandFile(config.cytobandURL, Object.assign({}, config));
|
|
73306
|
+
} else if (config.cytobandBbURL) {
|
|
73307
|
+
this.cytobandSource = new CytobandFileBB(config.cytobandBbURL, Object.assign({}, config), this);
|
|
73308
|
+
}
|
|
73309
|
+
}
|
|
73310
|
+
|
|
73311
|
+
// Search for chromosomes, that is an array of chromosome objects containing name and length. This is
|
|
73312
|
+
// optional but required to support whole genome view.
|
|
73313
|
+
if (this.sequence.chromosomes) {
|
|
73314
|
+
this.chromosomes = this.sequence.chromosomes;
|
|
73315
|
+
} else if (config.chromSizesURL) {
|
|
72591
73316
|
this.chromosomes = await loadChromSizes(config.chromSizesURL);
|
|
72592
73317
|
} else {
|
|
72593
|
-
|
|
72594
|
-
this.chromosomes = this.sequence.chromosomes || new Map();
|
|
73318
|
+
this.chromosomes = new Map(); // Cache, chromosome are added as they are loaded
|
|
72595
73319
|
}
|
|
72596
73320
|
|
|
72597
|
-
|
|
73321
|
+
// Search for chromosome names. This is optional but required to support the chromosome pulldown
|
|
73322
|
+
if (this.sequence.chromosomeNames) {
|
|
73323
|
+
this.chromosomeNames = this.sequence.chromosomeNames; // Twobit files can supply chromosome names unless they use an external index
|
|
73324
|
+
} else if (this.chromosomes.size > 0) {
|
|
72598
73325
|
this.chromosomeNames = Array.from(this.chromosomes.keys());
|
|
72599
73326
|
}
|
|
72600
73327
|
|
|
73328
|
+
// Chromosome alias
|
|
72601
73329
|
if (config.chromAliasBbURL) {
|
|
72602
73330
|
this.chromAlias = new ChromAliasBB(config.chromAliasBbURL, Object.assign({}, config), this);
|
|
72603
|
-
if (!this.chromosomeNames) {
|
|
72604
|
-
this.chromosomeNames = await this.chromAlias.getChromosomeNames();
|
|
72605
|
-
}
|
|
72606
73331
|
} else if (config.aliasURL) {
|
|
72607
73332
|
this.chromAlias = new ChromAliasFile(config.aliasURL, Object.assign({}, config), this);
|
|
72608
73333
|
} else if (this.chromosomeNames) {
|
|
72609
73334
|
this.chromAlias = new ChromAliasDefaults(this.id, this.chromosomeNames);
|
|
72610
73335
|
}
|
|
72611
73336
|
|
|
72612
|
-
if (config.cytobandBbURL) {
|
|
72613
|
-
this.cytobandSource = new CytobandFileBB(config.cytobandBbURL, Object.assign({}, config), this);
|
|
72614
|
-
} else if (config.cytobandURL) {
|
|
72615
|
-
this.cytobandSource = new CytobandFile(config.cytobandURL, Object.assign({}, config));
|
|
72616
|
-
}
|
|
72617
|
-
|
|
72618
|
-
// Last resort for chromosome information -- retrieve it from the cytoband source if supported
|
|
72619
|
-
if (!this.chromosomeNames && typeof this.cytobandSource.getChromosomeNames === 'function') {
|
|
72620
|
-
this.chromosomeNames = await this.cytobandSource.getChromosomeNames();
|
|
72621
|
-
}
|
|
72622
|
-
if (this.chromosomes.size === 0 && typeof this.cytobandSource.getChromosomes === 'function') {
|
|
72623
|
-
const c = await this.cytobandSource.getChromosomes();
|
|
72624
|
-
for (let chromosome of c) {
|
|
72625
|
-
this.chromosomes.set(c.name, c);
|
|
72626
|
-
}
|
|
72627
|
-
}
|
|
72628
|
-
|
|
72629
|
-
|
|
72630
73337
|
if (false !== config.wholeGenomeView && this.chromosomes.size > 0) {
|
|
72631
73338
|
// Set chromosome order for WG view and chromosome pulldown. If chromosome order is not specified sort
|
|
72632
73339
|
if (config.chromosomeOrder) {
|
|
@@ -72678,9 +73385,9 @@ ${indent}columns: ${matrix.columns}
|
|
|
72678
73385
|
getHomeChromosomeName() {
|
|
72679
73386
|
if (this.showWholeGenomeView() && this.chromosomes.has("all")) {
|
|
72680
73387
|
return "all"
|
|
72681
|
-
} else {
|
|
73388
|
+
} else if (this.chromosomeNames) {
|
|
72682
73389
|
return this.chromosomeNames[0]
|
|
72683
|
-
}
|
|
73390
|
+
} else ;
|
|
72684
73391
|
}
|
|
72685
73392
|
|
|
72686
73393
|
getChromosomeName(chr) {
|
|
@@ -72731,18 +73438,18 @@ ${indent}columns: ${matrix.columns}
|
|
|
72731
73438
|
if (!aliasRecord && chr !== chr.toLowerCase()) {
|
|
72732
73439
|
aliasRecord = await this.chromAlias.search(chr.toLowerCase());
|
|
72733
73440
|
}
|
|
72734
|
-
if(aliasRecord) {
|
|
73441
|
+
if (aliasRecord) {
|
|
72735
73442
|
// Add some aliases for case insensitivy
|
|
72736
73443
|
const upper = aliasRecord.chr.toUpperCase();
|
|
72737
73444
|
const lower = aliasRecord.chr.toLowerCase();
|
|
72738
73445
|
const cap = aliasRecord.chr.charAt(0).toUpperCase() + aliasRecord.chr.slice(1);
|
|
72739
|
-
if(aliasRecord.chr !== upper) {
|
|
73446
|
+
if (aliasRecord.chr !== upper) {
|
|
72740
73447
|
aliasRecord["_uppercase"] = upper;
|
|
72741
73448
|
}
|
|
72742
|
-
if(aliasRecord.chr !== lower) {
|
|
73449
|
+
if (aliasRecord.chr !== lower) {
|
|
72743
73450
|
aliasRecord["_lowercase"] = lower;
|
|
72744
73451
|
}
|
|
72745
|
-
if(aliasRecord.chr !== cap) {
|
|
73452
|
+
if (aliasRecord.chr !== cap) {
|
|
72746
73453
|
aliasRecord["_cap"] = cap;
|
|
72747
73454
|
}
|
|
72748
73455
|
}
|
|
@@ -72874,6 +73581,10 @@ ${indent}columns: ${matrix.columns}
|
|
|
72874
73581
|
return undefined
|
|
72875
73582
|
}
|
|
72876
73583
|
}
|
|
73584
|
+
|
|
73585
|
+
getHubURLs() {
|
|
73586
|
+
return this.config.hubs
|
|
73587
|
+
}
|
|
72877
73588
|
}
|
|
72878
73589
|
|
|
72879
73590
|
/**
|
|
@@ -72916,7 +73627,7 @@ ${indent}columns: ${matrix.columns}
|
|
|
72916
73627
|
}
|
|
72917
73628
|
}
|
|
72918
73629
|
|
|
72919
|
-
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 position: fixed;\n top: 20%;\n left: 50%;\n transform: translateX(-50%);\n z-index: 2048;\n box-sizing: content-box;\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-generic-dialog-container {\n box-sizing: content-box;\n position: fixed;\n top: 20%;\n left: 75%;\n transform: translateX(-50%);\n z-index: 2048;\n background-color: white;\n cursor: pointer;\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 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 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 position: fixed;\n cursor: pointer;\n background-color: white;\n z-index: 2048;\n box-sizing: content-box;\n width: 300px;\n height: 200px;\n top: 20%;\n left: 75%;\n transform: translateX(-50%);\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:nth-child(1) {\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:nth-child(1) > 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-colorpicker-container {\n background-color: #eee;\n height: fit-content;\n flex-direction: column;\n flex-wrap: nowrap;\n}\n.igv-ui-colorpicker-container > div:nth-child(2) {\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-ui-colorpicker-container > div:nth-child(3) {\n box-sizing: border-box;\n position: relative;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n padding: 0.5rem;\n font-family: \"Open Sans\", sans-serif;\n font-size: 1rem;\n font-weight: 400;\n width: 100%;\n border-top-style: solid;\n border-top-width: thin;\n border-top-color: #373737;\n}\n.igv-ui-colorpicker-container > div:nth-child(4) {\n width: 100%;\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n justify-content: flex-start;\n align-items: center;\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 margin: 2px;\n width: 32px;\n height: 32px;\n border-style: solid;\n border-width: 0;\n border-color: white;\n border-radius: 3px;\n}\n\n.igv-ui-color-swatch-shim {\n cursor: pointer;\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 background-color: white;\n border-style: solid;\n border-width: thin;\n border-color: white;\n border-radius: 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: fixed;\n top: 20%;\n left: 75%;\n transform: translateX(-50%);\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: fixed;\n top: 20%;\n left: 75%;\n transform: translateX(-50%);\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:nth-child(1) {\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:nth-child(1) 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 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.picker_wrapper.no_alpha .picker_alpha {\n display: none;\n}\n\n.picker_wrapper.no_editor .picker_editor {\n position: absolute;\n z-index: -1;\n opacity: 0;\n}\n\n.picker_wrapper.no_cancel .picker_cancel {\n display: none;\n}\n\n.layout_default.picker_wrapper {\n display: flex;\n flex-flow: row wrap;\n justify-content: space-between;\n align-items: stretch;\n font-size: 10px;\n width: 25em;\n padding: 0.5em;\n}\n\n.layout_default.picker_wrapper input, .layout_default.picker_wrapper button {\n font-size: 1rem;\n}\n\n.layout_default.picker_wrapper > * {\n margin: 0.5em;\n}\n\n.layout_default.picker_wrapper::before {\n content: \"\";\n display: block;\n width: 100%;\n height: 0;\n order: 1;\n}\n\n.layout_default .picker_slider, .layout_default .picker_selector {\n padding: 1em;\n}\n\n.layout_default .picker_hue {\n width: 100%;\n}\n\n.layout_default .picker_sl {\n flex: 1 1 auto;\n}\n\n.layout_default .picker_sl::before {\n content: \"\";\n display: block;\n padding-bottom: 100%;\n}\n\n.layout_default .picker_editor {\n order: 1;\n width: 6.5rem;\n}\n\n.layout_default .picker_editor input {\n width: 100%;\n height: 100%;\n}\n\n.layout_default .picker_sample {\n order: 1;\n flex: 1 1 auto;\n}\n\n.layout_default .picker_done, .layout_default .picker_cancel {\n order: 1;\n}\n\n.picker_wrapper {\n box-sizing: border-box;\n background: #f2f2f2;\n box-shadow: 0 0 0 1px silver;\n cursor: default;\n font-family: sans-serif;\n color: #444;\n pointer-events: auto;\n}\n\n.picker_wrapper:focus {\n outline: none;\n}\n\n.picker_wrapper button, .picker_wrapper input {\n box-sizing: border-box;\n border: none;\n box-shadow: 0 0 0 1px silver;\n outline: none;\n}\n\n.picker_wrapper button:focus, .picker_wrapper button:active, .picker_wrapper input:focus, .picker_wrapper input:active {\n box-shadow: 0 0 2px 1px #1e90ff;\n}\n\n.picker_wrapper button {\n padding: 0.4em 0.6em;\n cursor: pointer;\n background-color: #f5f5f5;\n background-image: linear-gradient(0deg, gainsboro, transparent);\n}\n\n.picker_wrapper button:active {\n background-image: linear-gradient(0deg, transparent, gainsboro);\n}\n\n.picker_wrapper button:hover {\n background-color: #fff;\n}\n\n.picker_selector {\n position: absolute;\n z-index: 1;\n display: block;\n -webkit-transform: translate(-50%, -50%);\n transform: translate(-50%, -50%);\n border: 2px solid #fff;\n border-radius: 100%;\n box-shadow: 0 0 3px 1px #67b9ff;\n background: currentColor;\n cursor: pointer;\n}\n\n.picker_slider .picker_selector {\n border-radius: 2px;\n}\n\n.picker_hue {\n position: relative;\n background-image: linear-gradient(90deg, red, yellow, lime, cyan, blue, magenta, red);\n box-shadow: 0 0 0 1px silver;\n}\n\n.picker_sl {\n position: relative;\n box-shadow: 0 0 0 1px silver;\n background-image: linear-gradient(180deg, white, rgba(255, 255, 255, 0) 50%), linear-gradient(0deg, black, rgba(0, 0, 0, 0) 50%), linear-gradient(90deg, #808080, rgba(128, 128, 128, 0));\n}\n\n.picker_alpha, .picker_sample {\n position: relative;\n background: linear-gradient(45deg, lightgrey 25%, transparent 25%, transparent 75%, lightgrey 75%) 0 0/2em 2em, linear-gradient(45deg, lightgrey 25%, white 25%, white 75%, lightgrey 75%) 1em 1em/2em 2em;\n box-shadow: 0 0 0 1px silver;\n}\n\n.picker_alpha .picker_selector, .picker_sample .picker_selector {\n background: none;\n}\n\n.picker_editor input {\n font-family: monospace;\n padding: 0.2em 0.4em;\n}\n\n.picker_sample::before {\n content: \"\";\n position: absolute;\n display: block;\n width: 100%;\n height: 100%;\n background: currentColor;\n}\n\n.picker_arrow {\n position: absolute;\n z-index: -1;\n}\n\n.picker_wrapper.popup {\n position: absolute;\n z-index: 2;\n margin: 1.5em;\n}\n\n.picker_wrapper.popup, .picker_wrapper.popup .picker_arrow::before, .picker_wrapper.popup .picker_arrow::after {\n background: #f2f2f2;\n box-shadow: 0 0 10px 1px rgba(0, 0, 0, 0.4);\n}\n\n.picker_wrapper.popup .picker_arrow {\n width: 3em;\n height: 3em;\n margin: 0;\n}\n\n.picker_wrapper.popup .picker_arrow::before, .picker_wrapper.popup .picker_arrow::after {\n content: \"\";\n display: block;\n position: absolute;\n top: 0;\n left: 0;\n z-index: -99;\n}\n\n.picker_wrapper.popup .picker_arrow::before {\n width: 100%;\n height: 100%;\n -webkit-transform: skew(45deg);\n transform: skew(45deg);\n -webkit-transform-origin: 0 100%;\n transform-origin: 0 100%;\n}\n\n.picker_wrapper.popup .picker_arrow::after {\n width: 150%;\n height: 150%;\n box-shadow: none;\n}\n\n.popup.popup_top {\n bottom: 100%;\n left: 0;\n}\n\n.popup.popup_top .picker_arrow {\n bottom: 0;\n left: 0;\n -webkit-transform: rotate(-90deg);\n transform: rotate(-90deg);\n}\n\n.popup.popup_bottom {\n top: 100%;\n left: 0;\n}\n\n.popup.popup_bottom .picker_arrow {\n top: 0;\n left: 0;\n -webkit-transform: rotate(90deg) scale(1, -1);\n transform: rotate(90deg) scale(1, -1);\n}\n\n.popup.popup_left {\n top: 0;\n right: 100%;\n}\n\n.popup.popup_left .picker_arrow {\n top: 0;\n right: 0;\n -webkit-transform: scale(-1, 1);\n transform: scale(-1, 1);\n}\n\n.popup.popup_right {\n top: 0;\n left: 100%;\n}\n\n.popup.popup_right .picker_arrow {\n top: 0;\n left: 0;\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.igv-vertical-center {\n margin: 0 !important;\n top: 50% !important;\n -ms-transform: translateY(-50%) !important;\n transform: translateY(-50%) !important;\n}\n\n/*# sourceMappingURL=igv.css.map */\n';
|
|
73630
|
+
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 position: fixed;\n top: 20%;\n left: 50%;\n transform: translateX(-50%);\n z-index: 2048;\n box-sizing: content-box;\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-generic-dialog-container {\n box-sizing: content-box;\n position: fixed;\n top: 20%;\n left: 75%;\n transform: translateX(-50%);\n z-index: 2048;\n background-color: white;\n cursor: pointer;\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 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 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 position: fixed;\n cursor: pointer;\n background-color: white;\n z-index: 2048;\n box-sizing: content-box;\n width: 300px;\n height: 200px;\n top: 20%;\n left: 75%;\n transform: translateX(-50%);\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:nth-child(1) {\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:nth-child(1) > 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-colorpicker-container {\n background-color: #eee;\n height: fit-content;\n flex-direction: column;\n flex-wrap: nowrap;\n}\n.igv-ui-colorpicker-container > div:nth-child(2) {\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-ui-colorpicker-container > div:nth-child(3) {\n box-sizing: border-box;\n position: relative;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n padding: 0.5rem;\n font-family: \"Open Sans\", sans-serif;\n font-size: 1rem;\n font-weight: 400;\n width: 100%;\n border-top-style: solid;\n border-top-width: thin;\n border-top-color: #373737;\n}\n.igv-ui-colorpicker-container > div:nth-child(4) {\n width: 100%;\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n justify-content: flex-start;\n align-items: center;\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 margin: 2px;\n width: 32px;\n height: 32px;\n border-style: solid;\n border-width: 0;\n border-color: white;\n border-radius: 3px;\n}\n\n.igv-ui-color-swatch-shim {\n cursor: pointer;\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 background-color: white;\n border-style: solid;\n border-width: thin;\n border-color: white;\n border-radius: 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: fixed;\n top: 20%;\n left: 75%;\n transform: translateX(-50%);\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: fixed;\n top: 20%;\n left: 75%;\n transform: translateX(-50%);\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:nth-child(1) {\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:nth-child(1) 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: 2048;\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 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.picker_wrapper.no_alpha .picker_alpha {\n display: none;\n}\n\n.picker_wrapper.no_editor .picker_editor {\n position: absolute;\n z-index: -1;\n opacity: 0;\n}\n\n.picker_wrapper.no_cancel .picker_cancel {\n display: none;\n}\n\n.layout_default.picker_wrapper {\n display: flex;\n flex-flow: row wrap;\n justify-content: space-between;\n align-items: stretch;\n font-size: 10px;\n width: 25em;\n padding: 0.5em;\n}\n\n.layout_default.picker_wrapper input, .layout_default.picker_wrapper button {\n font-size: 1rem;\n}\n\n.layout_default.picker_wrapper > * {\n margin: 0.5em;\n}\n\n.layout_default.picker_wrapper::before {\n content: \"\";\n display: block;\n width: 100%;\n height: 0;\n order: 1;\n}\n\n.layout_default .picker_slider, .layout_default .picker_selector {\n padding: 1em;\n}\n\n.layout_default .picker_hue {\n width: 100%;\n}\n\n.layout_default .picker_sl {\n flex: 1 1 auto;\n}\n\n.layout_default .picker_sl::before {\n content: \"\";\n display: block;\n padding-bottom: 100%;\n}\n\n.layout_default .picker_editor {\n order: 1;\n width: 6.5rem;\n}\n\n.layout_default .picker_editor input {\n width: 100%;\n height: 100%;\n}\n\n.layout_default .picker_sample {\n order: 1;\n flex: 1 1 auto;\n}\n\n.layout_default .picker_done, .layout_default .picker_cancel {\n order: 1;\n}\n\n.picker_wrapper {\n box-sizing: border-box;\n background: #f2f2f2;\n box-shadow: 0 0 0 1px silver;\n cursor: default;\n font-family: sans-serif;\n color: #444;\n pointer-events: auto;\n}\n\n.picker_wrapper:focus {\n outline: none;\n}\n\n.picker_wrapper button, .picker_wrapper input {\n box-sizing: border-box;\n border: none;\n box-shadow: 0 0 0 1px silver;\n outline: none;\n}\n\n.picker_wrapper button:focus, .picker_wrapper button:active, .picker_wrapper input:focus, .picker_wrapper input:active {\n box-shadow: 0 0 2px 1px #1e90ff;\n}\n\n.picker_wrapper button {\n padding: 0.4em 0.6em;\n cursor: pointer;\n background-color: #f5f5f5;\n background-image: linear-gradient(0deg, gainsboro, transparent);\n}\n\n.picker_wrapper button:active {\n background-image: linear-gradient(0deg, transparent, gainsboro);\n}\n\n.picker_wrapper button:hover {\n background-color: #fff;\n}\n\n.picker_selector {\n position: absolute;\n z-index: 1;\n display: block;\n -webkit-transform: translate(-50%, -50%);\n transform: translate(-50%, -50%);\n border: 2px solid #fff;\n border-radius: 100%;\n box-shadow: 0 0 3px 1px #67b9ff;\n background: currentColor;\n cursor: pointer;\n}\n\n.picker_slider .picker_selector {\n border-radius: 2px;\n}\n\n.picker_hue {\n position: relative;\n background-image: linear-gradient(90deg, red, yellow, lime, cyan, blue, magenta, red);\n box-shadow: 0 0 0 1px silver;\n}\n\n.picker_sl {\n position: relative;\n box-shadow: 0 0 0 1px silver;\n background-image: linear-gradient(180deg, white, rgba(255, 255, 255, 0) 50%), linear-gradient(0deg, black, rgba(0, 0, 0, 0) 50%), linear-gradient(90deg, #808080, rgba(128, 128, 128, 0));\n}\n\n.picker_alpha, .picker_sample {\n position: relative;\n background: linear-gradient(45deg, lightgrey 25%, transparent 25%, transparent 75%, lightgrey 75%) 0 0/2em 2em, linear-gradient(45deg, lightgrey 25%, white 25%, white 75%, lightgrey 75%) 1em 1em/2em 2em;\n box-shadow: 0 0 0 1px silver;\n}\n\n.picker_alpha .picker_selector, .picker_sample .picker_selector {\n background: none;\n}\n\n.picker_editor input {\n font-family: monospace;\n padding: 0.2em 0.4em;\n}\n\n.picker_sample::before {\n content: \"\";\n position: absolute;\n display: block;\n width: 100%;\n height: 100%;\n background: currentColor;\n}\n\n.picker_arrow {\n position: absolute;\n z-index: -1;\n}\n\n.picker_wrapper.popup {\n position: absolute;\n z-index: 2;\n margin: 1.5em;\n}\n\n.picker_wrapper.popup, .picker_wrapper.popup .picker_arrow::before, .picker_wrapper.popup .picker_arrow::after {\n background: #f2f2f2;\n box-shadow: 0 0 10px 1px rgba(0, 0, 0, 0.4);\n}\n\n.picker_wrapper.popup .picker_arrow {\n width: 3em;\n height: 3em;\n margin: 0;\n}\n\n.picker_wrapper.popup .picker_arrow::before, .picker_wrapper.popup .picker_arrow::after {\n content: \"\";\n display: block;\n position: absolute;\n top: 0;\n left: 0;\n z-index: -99;\n}\n\n.picker_wrapper.popup .picker_arrow::before {\n width: 100%;\n height: 100%;\n -webkit-transform: skew(45deg);\n transform: skew(45deg);\n -webkit-transform-origin: 0 100%;\n transform-origin: 0 100%;\n}\n\n.picker_wrapper.popup .picker_arrow::after {\n width: 150%;\n height: 150%;\n box-shadow: none;\n}\n\n.popup.popup_top {\n bottom: 100%;\n left: 0;\n}\n\n.popup.popup_top .picker_arrow {\n bottom: 0;\n left: 0;\n -webkit-transform: rotate(-90deg);\n transform: rotate(-90deg);\n}\n\n.popup.popup_bottom {\n top: 100%;\n left: 0;\n}\n\n.popup.popup_bottom .picker_arrow {\n top: 0;\n left: 0;\n -webkit-transform: rotate(90deg) scale(1, -1);\n transform: rotate(90deg) scale(1, -1);\n}\n\n.popup.popup_left {\n top: 0;\n right: 100%;\n}\n\n.popup.popup_left .picker_arrow {\n top: 0;\n right: 0;\n -webkit-transform: scale(-1, 1);\n transform: scale(-1, 1);\n}\n\n.popup.popup_right {\n top: 0;\n left: 100%;\n}\n\n.popup.popup_right .picker_arrow {\n top: 0;\n left: 0;\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.igv-vertical-center {\n margin: 0 !important;\n top: 50% !important;\n -ms-transform: translateY(-50%) !important;\n transform: translateY(-50%) !important;\n}\n\n/*# sourceMappingURL=igv.css.map */\n';
|
|
72920
73631
|
|
|
72921
73632
|
/**
|
|
72922
73633
|
* Manages XQTL selections.
|
|
@@ -73450,7 +74161,7 @@ ${indent}columns: ${matrix.columns}
|
|
|
73450
74161
|
this.dataRangeDialog = new DataRangeDialog(this, this.root);
|
|
73451
74162
|
this.dataRangeDialog.container.id = `igv-data-range-dialog-${guid$2()}`;
|
|
73452
74163
|
|
|
73453
|
-
this.genericColorPicker = new GenericColorPicker({
|
|
74164
|
+
this.genericColorPicker = new GenericColorPicker({parent: this.root, width: 180});
|
|
73454
74165
|
this.genericColorPicker.container.id = `igv-track-color-picker-${guid$2()}`;
|
|
73455
74166
|
|
|
73456
74167
|
this.sliderDialog = new SliderDialog(this.root);
|
|
@@ -73460,10 +74171,10 @@ ${indent}columns: ${matrix.columns}
|
|
|
73460
74171
|
|
|
73461
74172
|
getSampleNameViewportWidth() {
|
|
73462
74173
|
|
|
73463
|
-
if (undefined === this.sampleNameViewportWidth) {
|
|
74174
|
+
if (false === this.showSampleNames || undefined === this.sampleNameViewportWidth) {
|
|
73464
74175
|
return 0
|
|
73465
74176
|
} else {
|
|
73466
|
-
return
|
|
74177
|
+
return this.sampleNameViewportWidth
|
|
73467
74178
|
}
|
|
73468
74179
|
|
|
73469
74180
|
}
|
|
@@ -73625,7 +74336,8 @@ ${indent}columns: ${matrix.columns}
|
|
|
73625
74336
|
} else {
|
|
73626
74337
|
session = options;
|
|
73627
74338
|
}
|
|
73628
|
-
|
|
74339
|
+
|
|
74340
|
+
await this.loadSessionObject(session);
|
|
73629
74341
|
}
|
|
73630
74342
|
|
|
73631
74343
|
/**
|
|
@@ -73654,8 +74366,7 @@ ${indent}columns: ${matrix.columns}
|
|
|
73654
74366
|
config = new XMLSession(string, knownGenomes);
|
|
73655
74367
|
|
|
73656
74368
|
} else if (filename.endsWith("hub.txt")) {
|
|
73657
|
-
|
|
73658
|
-
const hub = await Hub.loadHub(urlOrFile, options);
|
|
74369
|
+
const hub = await loadHub(urlOrFile);
|
|
73659
74370
|
const genomeConfig = hub.getGenomeConfig();
|
|
73660
74371
|
config = {
|
|
73661
74372
|
reference: genomeConfig
|
|
@@ -73746,7 +74457,7 @@ ${indent}columns: ${matrix.columns}
|
|
|
73746
74457
|
}
|
|
73747
74458
|
|
|
73748
74459
|
// ROIs
|
|
73749
|
-
if(session.showROIOverlays !== undefined) {
|
|
74460
|
+
if (session.showROIOverlays !== undefined) {
|
|
73750
74461
|
this.roiManager.showOverlays = session.showROIOverlays;
|
|
73751
74462
|
}
|
|
73752
74463
|
this.roiManager.clearROIs();
|
|
@@ -73760,12 +74471,16 @@ ${indent}columns: ${matrix.columns}
|
|
|
73760
74471
|
// Sample info
|
|
73761
74472
|
const localSampleInfoFiles = [];
|
|
73762
74473
|
if (session.sampleinfo) {
|
|
73763
|
-
for (const
|
|
73764
|
-
|
|
73765
|
-
|
|
73766
|
-
|
|
74474
|
+
for (const sampleInfoConfig of session.sampleinfo) {
|
|
74475
|
+
// The "file" property is recorded in the session when a local file is referenced. It can't be used
|
|
74476
|
+
// on reloading, its only purpose is to present an alert to the user. This could also be used
|
|
74477
|
+
// to prompt the user to load the file manually, but we don't currently do that.
|
|
74478
|
+
if (sampleInfoConfig.file) {
|
|
74479
|
+
localSampleInfoFiles.push(sampleInfoConfig.file);
|
|
73767
74480
|
} else {
|
|
73768
|
-
this.loadSampleInfo(
|
|
74481
|
+
// this.loadSampleInfo(sampleInfoConfig)
|
|
74482
|
+
await this.sampleInfo.loadSampleInfo(sampleInfoConfig);
|
|
74483
|
+
|
|
73769
74484
|
}
|
|
73770
74485
|
|
|
73771
74486
|
}
|
|
@@ -73806,23 +74521,14 @@ ${indent}columns: ${matrix.columns}
|
|
|
73806
74521
|
}
|
|
73807
74522
|
}
|
|
73808
74523
|
|
|
73809
|
-
|
|
73810
|
-
|
|
73811
|
-
|
|
73812
|
-
|
|
73813
|
-
await
|
|
74524
|
+
// Load a hidden track -- used to populate searchable database without creating a track
|
|
74525
|
+
const configHidden = nonLocalTrackConfigurations.filter(config => true === config.hidden);
|
|
74526
|
+
for (const config of configHidden) {
|
|
74527
|
+
const featureSource = FeatureSource(config, this.genome);
|
|
74528
|
+
await featureSource.getFeatures({chr: "1", start: 0, end: Number.MAX_SAFE_INTEGER});
|
|
73814
74529
|
}
|
|
73815
74530
|
|
|
73816
|
-
|
|
73817
|
-
if (this.trackViews.some(tv => tv.track.selected)) {
|
|
73818
|
-
this.navbar.setEnableTrackSelection(true);
|
|
73819
|
-
}
|
|
73820
|
-
|
|
73821
|
-
this.updateUIWithReferenceFrameList();
|
|
73822
|
-
|
|
73823
|
-
this.updateLocusSearchWidget();
|
|
73824
|
-
|
|
73825
|
-
return trackConfigurations
|
|
74531
|
+
await this.loadTrackList(nonLocalTrackConfigurations);
|
|
73826
74532
|
|
|
73827
74533
|
}
|
|
73828
74534
|
|
|
@@ -73883,13 +74589,8 @@ ${indent}columns: ${matrix.columns}
|
|
|
73883
74589
|
}
|
|
73884
74590
|
|
|
73885
74591
|
if (genomeChange) {
|
|
73886
|
-
|
|
73887
|
-
|
|
73888
|
-
// TODO -- refactor this so "hub" is not loaded twice
|
|
73889
|
-
const hub = await Hub.loadHub(genomeConfig.hubURL);
|
|
73890
|
-
trackConfigurations = hub.getGroupedTrackConfigurations();
|
|
73891
|
-
}
|
|
73892
|
-
this.fireEvent('genomechange', [{genome, trackConfigurations}]);
|
|
74592
|
+
|
|
74593
|
+
this.fireEvent('genomechange', [{genome}]);
|
|
73893
74594
|
|
|
73894
74595
|
if (this.circularView) {
|
|
73895
74596
|
this.circularView.setAssembly({
|
|
@@ -73928,7 +74629,7 @@ ${indent}columns: ${matrix.columns}
|
|
|
73928
74629
|
let genomeConfig;
|
|
73929
74630
|
const isHubGenome = idOrConfig.hubURL || (idOrConfig.url && isString$2(idOrConfig.url) && idOrConfig.url.endsWith("/hub.txt"));
|
|
73930
74631
|
if (isHubGenome) {
|
|
73931
|
-
const hub = await
|
|
74632
|
+
const hub = await loadHub(idOrConfig.hubURL || idOrConfig.url);
|
|
73932
74633
|
genomeConfig = hub.getGenomeConfig();
|
|
73933
74634
|
} else if (isString$2(idOrConfig) || !(idOrConfig.url || idOrConfig.fastaURL || idOrConfig.twoBitURL || idOrConfig.gbkURL)) {
|
|
73934
74635
|
// Either an ID, a json string, or an object missing required properties.
|
|
@@ -73958,22 +74659,9 @@ ${indent}columns: ${matrix.columns}
|
|
|
73958
74659
|
|
|
73959
74660
|
await this.loadTrackList(tracks);
|
|
73960
74661
|
|
|
73961
|
-
await this.updateViews();
|
|
73962
|
-
|
|
73963
74662
|
return this.genome
|
|
73964
74663
|
}
|
|
73965
74664
|
|
|
73966
|
-
/**
|
|
73967
|
-
* Load a UCSC single-file genome assembly hub.
|
|
73968
|
-
* @param options
|
|
73969
|
-
* @returns {Promise<void>}
|
|
73970
|
-
*/
|
|
73971
|
-
async loadTrackHub(options) {
|
|
73972
|
-
const hub = await Hub.loadHub(options.url, options);
|
|
73973
|
-
const genomeConfig = setDefaults(hub.getGenomeConfig());
|
|
73974
|
-
return this.loadGenome(genomeConfig)
|
|
73975
|
-
}
|
|
73976
|
-
|
|
73977
74665
|
/**
|
|
73978
74666
|
* Called after a session load, search, pan (horizontal drag), or resize
|
|
73979
74667
|
*
|
|
@@ -74070,18 +74758,23 @@ ${indent}columns: ${matrix.columns}
|
|
|
74070
74758
|
}
|
|
74071
74759
|
|
|
74072
74760
|
const promises = [];
|
|
74073
|
-
for (
|
|
74074
|
-
promises.push(this
|
|
74761
|
+
for (const config of configList) {
|
|
74762
|
+
promises.push(this.#loadTrackHelper(config));
|
|
74075
74763
|
}
|
|
74076
74764
|
|
|
74077
74765
|
const loadedTracks = await Promise.all(promises);
|
|
74078
74766
|
|
|
74079
|
-
|
|
74080
|
-
|
|
74081
|
-
|
|
74082
|
-
if (groupAutoscaleViews.length > 0) {
|
|
74083
|
-
this.updateViews();
|
|
74767
|
+
// If any tracks are selected show the selection buttons
|
|
74768
|
+
if (this.trackViews.some(({track}) => track.selected)) {
|
|
74769
|
+
this.navbar.setEnableTrackSelection(true);
|
|
74084
74770
|
}
|
|
74771
|
+
|
|
74772
|
+
this.reorderTracks();
|
|
74773
|
+
|
|
74774
|
+
await resize.call(this);
|
|
74775
|
+
|
|
74776
|
+
this.fireEvent('trackorderchanged', [this.getTrackOrder()]);
|
|
74777
|
+
|
|
74085
74778
|
return loadedTracks
|
|
74086
74779
|
}
|
|
74087
74780
|
|
|
@@ -74095,54 +74788,23 @@ ${indent}columns: ${matrix.columns}
|
|
|
74095
74788
|
*/
|
|
74096
74789
|
async loadTrack(config) {
|
|
74097
74790
|
|
|
74098
|
-
|
|
74099
|
-
|
|
74100
|
-
|
|
74101
|
-
const newTrack = this._loadTrack(config);
|
|
74102
|
-
|
|
74103
|
-
if (newTrack && config.autoscaleGroup) {
|
|
74104
|
-
// Await newTrack load and update all views
|
|
74105
|
-
await newTrack;
|
|
74791
|
+
const loadedTracks = await this.loadTrackList([config]);
|
|
74792
|
+
if (config.autoscaleGroup) {
|
|
74106
74793
|
this.updateViews();
|
|
74107
74794
|
}
|
|
74108
|
-
|
|
74109
|
-
return newTrack
|
|
74795
|
+
return loadedTracks[0]
|
|
74110
74796
|
}
|
|
74111
74797
|
|
|
74112
|
-
|
|
74113
|
-
* Return a promise to load a track. Private function used by loadTrack() and loadTrackList()
|
|
74114
|
-
*
|
|
74115
|
-
* @param config
|
|
74116
|
-
* @returns {*}
|
|
74117
|
-
*/
|
|
74118
|
-
|
|
74119
|
-
async _loadTrack(config) {
|
|
74798
|
+
async #loadTrackHelper(config) {
|
|
74120
74799
|
|
|
74121
74800
|
// config might be json
|
|
74122
74801
|
if (isString$2(config)) {
|
|
74123
74802
|
config = JSON.parse(config);
|
|
74124
74803
|
}
|
|
74125
74804
|
|
|
74805
|
+
let track;
|
|
74126
74806
|
try {
|
|
74127
|
-
|
|
74128
|
-
// Load a hidden track -- used to populate searchable database without creating a track
|
|
74129
|
-
if (config.hidden) {
|
|
74130
|
-
const featureSource = FeatureSource(config, this.genome);
|
|
74131
|
-
await featureSource.getFeatures({chr: "1", start: 0, end: Number.MAX_SAFE_INTEGER});
|
|
74132
|
-
return
|
|
74133
|
-
}
|
|
74134
|
-
|
|
74135
|
-
const newTrack = await this.createTrack(config);
|
|
74136
|
-
|
|
74137
|
-
if ('sampleinfo' === config.type) {
|
|
74138
|
-
this.layoutChange();
|
|
74139
|
-
return
|
|
74140
|
-
} else if (undefined === newTrack) {
|
|
74141
|
-
return
|
|
74142
|
-
}
|
|
74143
|
-
|
|
74144
|
-
return this.addTrack(config, newTrack)
|
|
74145
|
-
|
|
74807
|
+
track = await this.createTrack(config);
|
|
74146
74808
|
} catch (error) {
|
|
74147
74809
|
|
|
74148
74810
|
let msg = error.message || error.error || error.toString();
|
|
@@ -74159,62 +74821,53 @@ ${indent}columns: ${matrix.columns}
|
|
|
74159
74821
|
}
|
|
74160
74822
|
|
|
74161
74823
|
msg = `${msg} : ${isFile(config.url) ? config.url.name : config.url}`;
|
|
74162
|
-
// msg += (": " + FileUtils.isFile(config.url) ? config.url.name : config.url)
|
|
74163
74824
|
const err = new Error(msg);
|
|
74164
74825
|
console.error(err);
|
|
74165
74826
|
throw err
|
|
74166
|
-
// this.alert.present(new Error(msg), undefined)
|
|
74167
74827
|
}
|
|
74168
|
-
}
|
|
74169
74828
|
|
|
74170
|
-
|
|
74829
|
+
if (track) {
|
|
74830
|
+
return await this.addTrack(track)
|
|
74831
|
+
} else {
|
|
74832
|
+
return undefined
|
|
74833
|
+
}
|
|
74834
|
+
|
|
74835
|
+
}
|
|
74171
74836
|
|
|
74837
|
+
async addTrack(track) {
|
|
74172
74838
|
|
|
74173
74839
|
// Set order field of track here, otherwise track order might get shuffled during asynchronous load
|
|
74174
|
-
if (undefined ===
|
|
74175
|
-
|
|
74840
|
+
if (undefined === track.order) {
|
|
74841
|
+
track.order = this.trackViews.length;
|
|
74176
74842
|
}
|
|
74177
74843
|
|
|
74178
|
-
const trackView = new TrackView(this, this.columnContainer,
|
|
74844
|
+
const trackView = new TrackView(this, this.columnContainer, track);
|
|
74179
74845
|
this.trackViews.push(trackView);
|
|
74180
74846
|
toggleTrackLabels(this.trackViews, this.doShowTrackLabels);
|
|
74181
74847
|
|
|
74182
|
-
if (typeof
|
|
74848
|
+
if (typeof track.postInit === 'function') {
|
|
74183
74849
|
try {
|
|
74184
74850
|
trackView.startSpinner();
|
|
74185
|
-
await
|
|
74851
|
+
await track.postInit();
|
|
74186
74852
|
} finally {
|
|
74187
74853
|
trackView.stopSpinner();
|
|
74188
74854
|
}
|
|
74189
74855
|
}
|
|
74190
74856
|
|
|
74191
|
-
if (
|
|
74192
|
-
// Group autoscale will get updated later (as a group)
|
|
74193
|
-
if (config.sync) {
|
|
74194
|
-
await trackView.updateViews();
|
|
74195
|
-
} else {
|
|
74196
|
-
trackView.updateViews();
|
|
74197
|
-
}
|
|
74198
|
-
}
|
|
74199
|
-
|
|
74200
|
-
if (typeof newTrack.hasSamples === 'function' && newTrack.hasSamples()) {
|
|
74857
|
+
if (typeof track.hasSamples === 'function' && track.hasSamples()) {
|
|
74201
74858
|
|
|
74202
74859
|
if (this.sampleInfo.hasAttributes()) {
|
|
74203
74860
|
this.sampleInfoControl.setButtonVisibility(true);
|
|
74204
74861
|
}
|
|
74205
74862
|
|
|
74206
74863
|
if (this.config.showSampleNameButton !== false) {
|
|
74207
|
-
this.sampleNameControl.show();
|
|
74864
|
+
this.sampleNameControl.show();
|
|
74208
74865
|
}
|
|
74209
74866
|
}
|
|
74210
74867
|
|
|
74211
|
-
|
|
74212
|
-
this.reorderTracks();
|
|
74213
|
-
this.fireEvent('trackorderchanged', [this.getTrackOrder()]);
|
|
74868
|
+
track.trackView.enableTrackSelection(this.navbar.getEnableTrackSelection());
|
|
74214
74869
|
|
|
74215
|
-
|
|
74216
|
-
|
|
74217
|
-
return newTrack
|
|
74870
|
+
return track
|
|
74218
74871
|
|
|
74219
74872
|
}
|
|
74220
74873
|
|
|
@@ -74359,7 +75012,6 @@ ${indent}columns: ${matrix.columns}
|
|
|
74359
75012
|
}
|
|
74360
75013
|
}
|
|
74361
75014
|
|
|
74362
|
-
|
|
74363
75015
|
reorderTracks() {
|
|
74364
75016
|
|
|
74365
75017
|
this.trackViews.sort(function (a, b) {
|
|
@@ -74596,19 +75248,19 @@ ${indent}columns: ${matrix.columns}
|
|
|
74596
75248
|
|
|
74597
75249
|
this.updateLocusSearchWidget();
|
|
74598
75250
|
|
|
74599
|
-
for (
|
|
74600
|
-
if (
|
|
74601
|
-
await this.genome.getSequence(
|
|
75251
|
+
for (const {bpPerPixel, chr, start} of this.referenceFrameList) {
|
|
75252
|
+
if (bpPerPixel <= bppSequenceThreshold) {
|
|
75253
|
+
await this.genome.getSequence(chr, start, start + 1);
|
|
74602
75254
|
}
|
|
74603
75255
|
}
|
|
74604
75256
|
|
|
74605
|
-
for (
|
|
75257
|
+
for (const centerGuide of this.centerLineList) {
|
|
74606
75258
|
centerGuide.repaint();
|
|
74607
75259
|
}
|
|
74608
75260
|
|
|
74609
75261
|
// Don't autoscale while dragging.
|
|
74610
75262
|
if (this.dragObject) {
|
|
74611
|
-
for (
|
|
75263
|
+
for (const trackView of trackViews) {
|
|
74612
75264
|
await trackView.updateViews();
|
|
74613
75265
|
}
|
|
74614
75266
|
} else {
|
|
@@ -74632,8 +75284,8 @@ ${indent}columns: ${matrix.columns}
|
|
|
74632
75284
|
// Calculate group autoscale dataRange
|
|
74633
75285
|
if (Object.entries(groupAutoscaleTrackViews).length > 0) {
|
|
74634
75286
|
for (const [group, trackViews] of Object.entries(groupAutoscaleTrackViews)) {
|
|
74635
|
-
const
|
|
74636
|
-
const dataRange = doAutoscale(
|
|
75287
|
+
const inViewFeatures = await Promise.all(trackViews.map(trackView => trackView.getInViewFeatures()));
|
|
75288
|
+
const dataRange = doAutoscale(inViewFeatures.flat());
|
|
74637
75289
|
for (const trackView of trackViews) {
|
|
74638
75290
|
trackView.track.dataRange = Object.assign({}, dataRange);
|
|
74639
75291
|
trackView.track.autoscale = false;
|
|
@@ -74642,7 +75294,7 @@ ${indent}columns: ${matrix.columns}
|
|
|
74642
75294
|
}
|
|
74643
75295
|
}
|
|
74644
75296
|
|
|
74645
|
-
await Promise.all(otherTrackViews.map(
|
|
75297
|
+
await Promise.all(otherTrackViews.map(trackView => trackView.updateViews()));
|
|
74646
75298
|
}
|
|
74647
75299
|
|
|
74648
75300
|
}
|
|
@@ -74665,10 +75317,10 @@ ${indent}columns: ${matrix.columns}
|
|
|
74665
75317
|
referenceFrame.end = referenceFrame.start + referenceFrame.bpPerPixel * width;
|
|
74666
75318
|
}
|
|
74667
75319
|
|
|
74668
|
-
const chrName = referenceFrameList.length === 1 ? this.referenceFrameList[0].chr : '';
|
|
74669
|
-
|
|
74670
75320
|
const loc = this.referenceFrameList.map(rf => rf.getLocusString()).join(' ');
|
|
74671
75321
|
|
|
75322
|
+
const chrName = referenceFrameList.length === 1 ? this.genome.getChromosomeDisplayName(this.referenceFrameList[0].chr) : '';
|
|
75323
|
+
|
|
74672
75324
|
this.navbar.updateLocus(loc, chrName);
|
|
74673
75325
|
|
|
74674
75326
|
this.fireEvent('locuschange', [this.referenceFrameList]);
|
|
@@ -74685,6 +75337,46 @@ ${indent}columns: ${matrix.columns}
|
|
|
74685
75337
|
return Math.floor(width / columnCount)
|
|
74686
75338
|
}
|
|
74687
75339
|
|
|
75340
|
+
/**
|
|
75341
|
+
* Update reference frames based on new viewport width
|
|
75342
|
+
* @param {number} viewportWidth - The calculated viewport width
|
|
75343
|
+
*/
|
|
75344
|
+
updateReferenceFrames(viewportWidth) {
|
|
75345
|
+
|
|
75346
|
+
for (const referenceFrame of this.referenceFrameList) {
|
|
75347
|
+
referenceFrame.updateForViewportWidth(viewportWidth);
|
|
75348
|
+
}
|
|
75349
|
+
}
|
|
75350
|
+
|
|
75351
|
+
/**
|
|
75352
|
+
* Update DOM viewport elements with new width
|
|
75353
|
+
* @param {number} viewportWidth - The calculated viewport width
|
|
75354
|
+
*/
|
|
75355
|
+
updateViewportElements(viewportWidth) {
|
|
75356
|
+
|
|
75357
|
+
for (let i = 0; i < this.referenceFrameList.length; i++) {
|
|
75358
|
+
|
|
75359
|
+
for (const {viewports} of this.trackViews) {
|
|
75360
|
+
viewports[i].setWidth(viewportWidth);
|
|
75361
|
+
}
|
|
75362
|
+
|
|
75363
|
+
for (const {sampleInfoViewport} of this.trackViews) {
|
|
75364
|
+
sampleInfoViewport.setWidth(this.getSampleInfoColumnWidth());
|
|
75365
|
+
sampleInfoViewport.repaint();
|
|
75366
|
+
}
|
|
75367
|
+
|
|
75368
|
+
}
|
|
75369
|
+
}
|
|
75370
|
+
|
|
75371
|
+
/**
|
|
75372
|
+
* Synchronize UI state after viewport updates
|
|
75373
|
+
* @returns {Promise<void>}
|
|
75374
|
+
*/
|
|
75375
|
+
async syncUIState() {
|
|
75376
|
+
this.updateUIWithReferenceFrameList();
|
|
75377
|
+
await this.updateViews(true);
|
|
75378
|
+
}
|
|
75379
|
+
|
|
74688
75380
|
minimumBases() {
|
|
74689
75381
|
return this.config.minimumBases
|
|
74690
75382
|
}
|
|
@@ -74870,29 +75562,12 @@ ${indent}columns: ${matrix.columns}
|
|
|
74870
75562
|
}
|
|
74871
75563
|
|
|
74872
75564
|
/**
|
|
74873
|
-
* @deprecated This is a deprecated method with no known usages.
|
|
75565
|
+
* @deprecated This is a deprecated method with no known usages.
|
|
74874
75566
|
*/
|
|
74875
75567
|
async goto(chr, start, end) {
|
|
74876
75568
|
await this.search(chr + ":" + start + "-" + end);
|
|
74877
75569
|
}
|
|
74878
75570
|
|
|
74879
|
-
/**
|
|
74880
|
-
|
|
74881
|
-
* Search for the locus string -- this function is called from various igv.js GUI elements, and is not part of the
|
|
74882
|
-
* API. Wraps ```search``` and presents an error dialog if false.
|
|
74883
|
-
*
|
|
74884
|
-
* @param string
|
|
74885
|
-
* @param init
|
|
74886
|
-
* @returns {Promise<void>}
|
|
74887
|
-
*/
|
|
74888
|
-
async doSearch(string, init) {
|
|
74889
|
-
const success = await this.search(string, init);
|
|
74890
|
-
if (!success) {
|
|
74891
|
-
this.alert.present(new Error(`Unrecognized locus: <b> ${string} </b>`));
|
|
74892
|
-
}
|
|
74893
|
-
return success
|
|
74894
|
-
}
|
|
74895
|
-
|
|
74896
75571
|
|
|
74897
75572
|
/**
|
|
74898
75573
|
* Search for the locus string
|
|
@@ -74905,6 +75580,10 @@ ${indent}columns: ${matrix.columns}
|
|
|
74905
75580
|
async search(stringOrArray, init) {
|
|
74906
75581
|
|
|
74907
75582
|
const loci = await search(this, stringOrArray);
|
|
75583
|
+
return this.updateLoci(loci, init)
|
|
75584
|
+
}
|
|
75585
|
+
|
|
75586
|
+
async updateLoci(loci, init) {
|
|
74908
75587
|
|
|
74909
75588
|
if (loci && loci.length > 0) {
|
|
74910
75589
|
|
|
@@ -74941,9 +75620,9 @@ ${indent}columns: ${matrix.columns}
|
|
|
74941
75620
|
}
|
|
74942
75621
|
}
|
|
74943
75622
|
|
|
74944
|
-
async loadSampleInfo(
|
|
75623
|
+
async loadSampleInfo(sampleInfoConfig) {
|
|
74945
75624
|
|
|
74946
|
-
await this.sampleInfo.
|
|
75625
|
+
await this.sampleInfo.loadSampleInfo(sampleInfoConfig);
|
|
74947
75626
|
|
|
74948
75627
|
for (const {sampleInfoViewport} of this.trackViews) {
|
|
74949
75628
|
sampleInfoViewport.setWidth(this.getSampleInfoColumnWidth());
|
|
@@ -75081,9 +75760,9 @@ ${indent}columns: ${matrix.columns}
|
|
|
75081
75760
|
json["locus"] = locus.length === 1 ? locus[0] : locus;
|
|
75082
75761
|
|
|
75083
75762
|
const roiSets = this.roiManager.toJSON();
|
|
75084
|
-
if(roiSets) {
|
|
75763
|
+
if (roiSets) {
|
|
75085
75764
|
json["roi"] = roiSets;
|
|
75086
|
-
if(!this.roiManager.showOverlays){
|
|
75765
|
+
if (!this.roiManager.showOverlays) {
|
|
75087
75766
|
json["showROIOverlays"] = false; // true is the default
|
|
75088
75767
|
}
|
|
75089
75768
|
}
|
|
@@ -75426,8 +76105,6 @@ ${indent}columns: ${matrix.columns}
|
|
|
75426
76105
|
}
|
|
75427
76106
|
}
|
|
75428
76107
|
|
|
75429
|
-
|
|
75430
|
-
|
|
75431
76108
|
// Navbar delegates
|
|
75432
76109
|
get sampleInfoControl() {
|
|
75433
76110
|
return this.navbar.sampleInfoControl
|
|
@@ -75449,6 +76126,9 @@ ${indent}columns: ${matrix.columns}
|
|
|
75449
76126
|
return this.navbar.sampleNameControl
|
|
75450
76127
|
}
|
|
75451
76128
|
|
|
76129
|
+
async blat(sequence) {
|
|
76130
|
+
return createBlatTrack({sequence, browser: this, name: 'Blat', title: 'Blat'})
|
|
76131
|
+
}
|
|
75452
76132
|
}
|
|
75453
76133
|
|
|
75454
76134
|
function getFileExtension(input) {
|
|
@@ -75474,7 +76154,7 @@ ${indent}columns: ${matrix.columns}
|
|
|
75474
76154
|
}
|
|
75475
76155
|
|
|
75476
76156
|
/**
|
|
75477
|
-
*
|
|
76157
|
+
* Called when window is resized, or visibility changed (e.g. "show" from a tab). This is a function rather
|
|
75478
76158
|
* than class method because it needs to be copied and bound to specific instances of browser to support listener
|
|
75479
76159
|
* removal
|
|
75480
76160
|
*
|
|
@@ -75482,40 +76162,14 @@ ${indent}columns: ${matrix.columns}
|
|
|
75482
76162
|
*/
|
|
75483
76163
|
async function resize() {
|
|
75484
76164
|
|
|
75485
|
-
if (
|
|
75486
|
-
|
|
75487
|
-
const viewportWidth = this.calculateViewportWidth(this.referenceFrameList.length);
|
|
75488
|
-
|
|
75489
|
-
for (let referenceFrame of this.referenceFrameList) {
|
|
75490
|
-
|
|
75491
|
-
const index = this.referenceFrameList.indexOf(referenceFrame);
|
|
75492
|
-
|
|
75493
|
-
const {chr, genome} = referenceFrame;
|
|
75494
|
-
|
|
75495
|
-
const {bpLength} = genome.getChromosome(referenceFrame.chr);
|
|
75496
|
-
|
|
75497
|
-
const viewportWidthBP = referenceFrame.toBP(viewportWidth);
|
|
75498
|
-
|
|
75499
|
-
// viewportWidthBP > bpLength occurs when locus is full chromosome and user widens browser
|
|
75500
|
-
if (GenomeUtils.isWholeGenomeView(chr) || viewportWidthBP > bpLength) {
|
|
75501
|
-
// console.log(`${ Date.now() } Recalc referenceFrame(${ index }) bpp. viewport ${ StringUtils.numberFormatter(viewportWidthBP) } > ${ StringUtils.numberFormatter(bpLength) }.`)
|
|
75502
|
-
referenceFrame.bpPerPixel = bpLength / viewportWidth;
|
|
75503
|
-
} else {
|
|
75504
|
-
// console.log(`${ Date.now() } Recalc referenceFrame(${ index }) end.`)
|
|
75505
|
-
referenceFrame.end = referenceFrame.start + referenceFrame.toBP(viewportWidth);
|
|
75506
|
-
}
|
|
75507
|
-
|
|
75508
|
-
for (let {viewports} of this.trackViews) {
|
|
75509
|
-
viewports[index].setWidth(viewportWidth);
|
|
75510
|
-
}
|
|
75511
|
-
|
|
76165
|
+
if (undefined === this.referenceFrameList || 0 === this.referenceFrameList.length) {
|
|
76166
|
+
return
|
|
75512
76167
|
}
|
|
75513
76168
|
|
|
75514
|
-
this.
|
|
75515
|
-
|
|
75516
|
-
|
|
75517
|
-
|
|
75518
|
-
await this.updateViews(true);
|
|
76169
|
+
const viewportWidth = this.calculateViewportWidth(this.referenceFrameList.length);
|
|
76170
|
+
this.updateReferenceFrames(viewportWidth);
|
|
76171
|
+
this.updateViewportElements(viewportWidth);
|
|
76172
|
+
await this.syncUIState();
|
|
75519
76173
|
}
|
|
75520
76174
|
|
|
75521
76175
|
|
|
@@ -75984,7 +76638,8 @@ ${indent}columns: ${matrix.columns}
|
|
|
75984
76638
|
registerTrackClass,
|
|
75985
76639
|
registerTrackCreatorFunction,
|
|
75986
76640
|
registerFileFormats,
|
|
75987
|
-
loadSessionFile: Browser.loadSessionFile
|
|
76641
|
+
loadSessionFile: Browser.loadSessionFile,
|
|
76642
|
+
loadHub
|
|
75988
76643
|
};
|
|
75989
76644
|
|
|
75990
76645
|
return index;
|