igv 3.3.0 → 3.4.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/dist/igv.js CHANGED
@@ -379,7 +379,7 @@
379
379
  * @param x
380
380
  * @returns {boolean}
381
381
  */
382
- function isString$2(x) {
382
+ function isString$3(x) {
383
383
  return typeof x === "string" || x instanceof String
384
384
  }
385
385
 
@@ -479,7 +479,7 @@
479
479
 
480
480
  if (urlOrFile.name !== undefined) {
481
481
  return urlOrFile.name
482
- } else if (isString$2(urlOrFile)) {
482
+ } else if (isString$3(urlOrFile)) {
483
483
 
484
484
  let index = urlOrFile.lastIndexOf("/");
485
485
  let filename = index < 0 ? urlOrFile : urlOrFile.substr(index + 1);
@@ -10684,7 +10684,7 @@
10684
10684
  };
10685
10685
 
10686
10686
  async function getFilename$1(url) {
10687
- if (isString$2(url) && url.startsWith("https://drive.google.com")) {
10687
+ if (isString$3(url) && url.startsWith("https://drive.google.com")) {
10688
10688
  // This will fail if Google API key is not defined
10689
10689
  if (getApiKey() === undefined) {
10690
10690
  throw Error("Google drive is referenced, but API key is not defined. An API key is required for Google Drive access")
@@ -10726,7 +10726,7 @@
10726
10726
 
10727
10727
 
10728
10728
  function isDataURL(obj) {
10729
- return (isString$2(obj) && obj.startsWith("data:"))
10729
+ return (isString$3(obj) && obj.startsWith("data:"))
10730
10730
  }
10731
10731
 
10732
10732
  function createColumn(columnContainer, className) {
@@ -10952,7 +10952,7 @@
10952
10952
  return list;
10953
10953
  }
10954
10954
 
10955
- /*! @license DOMPurify 3.2.5 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.2.5/LICENSE */
10955
+ /*! @license DOMPurify 3.2.6 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.2.6/LICENSE */
10956
10956
 
10957
10957
  const {
10958
10958
  entries,
@@ -11156,7 +11156,7 @@
11156
11156
  const TMPLIT_EXPR = seal(/\$\{[\w\W]*/gm); // eslint-disable-line unicorn/better-regex
11157
11157
  const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]+$/); // eslint-disable-line no-useless-escape
11158
11158
  const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
11159
- const IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
11159
+ const IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
11160
11160
  );
11161
11161
  const IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
11162
11162
  const ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex
@@ -11253,7 +11253,7 @@
11253
11253
  function createDOMPurify() {
11254
11254
  let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
11255
11255
  const DOMPurify = root => createDOMPurify(root);
11256
- DOMPurify.version = '3.2.5';
11256
+ DOMPurify.version = '3.2.6';
11257
11257
  DOMPurify.removed = [];
11258
11258
  if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
11259
11259
  // Not running in a browser, provide a factory function
@@ -11492,8 +11492,8 @@
11492
11492
  URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR, transformCaseFunc) : DEFAULT_URI_SAFE_ATTRIBUTES;
11493
11493
  DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS') ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS, transformCaseFunc) : DEFAULT_DATA_URI_TAGS;
11494
11494
  FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
11495
- FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : {};
11496
- FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : {};
11495
+ FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : clone({});
11496
+ FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : clone({});
11497
11497
  USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES : false;
11498
11498
  ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
11499
11499
  ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
@@ -11858,7 +11858,7 @@
11858
11858
  allowedTags: ALLOWED_TAGS
11859
11859
  });
11860
11860
  /* Detect mXSS attempts abusing namespace confusion */
11861
- if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(/<[/\w!]/g, currentNode.innerHTML) && regExpTest(/<[/\w!]/g, currentNode.textContent)) {
11861
+ if (SAFE_FOR_XML && currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(/<[/\w!]/g, currentNode.innerHTML) && regExpTest(/<[/\w!]/g, currentNode.textContent)) {
11862
11862
  _forceRemove(currentNode);
11863
11863
  return true;
11864
11864
  }
@@ -12010,7 +12010,8 @@
12010
12010
  value: attrValue
12011
12011
  } = attr;
12012
12012
  const lcName = transformCaseFunc(name);
12013
- let value = name === 'value' ? attrValue : stringTrim(attrValue);
12013
+ const initValue = attrValue;
12014
+ let value = name === 'value' ? initValue : stringTrim(initValue);
12014
12015
  /* Execute a hook if present */
12015
12016
  hookEvent.attrName = lcName;
12016
12017
  hookEvent.attrValue = value;
@@ -12036,10 +12037,9 @@
12036
12037
  if (hookEvent.forceKeepAttr) {
12037
12038
  continue;
12038
12039
  }
12039
- /* Remove attribute */
12040
- _removeAttribute(name, currentNode);
12041
12040
  /* Did the hooks approve of the attribute? */
12042
12041
  if (!hookEvent.keepAttr) {
12042
+ _removeAttribute(name, currentNode);
12043
12043
  continue;
12044
12044
  }
12045
12045
  /* Work around a security issue in jQuery 3.0 */
@@ -12056,6 +12056,7 @@
12056
12056
  /* Is `value` valid for this attribute? */
12057
12057
  const lcTag = transformCaseFunc(currentNode.nodeName);
12058
12058
  if (!_isValidAttribute(lcTag, lcName, value)) {
12059
+ _removeAttribute(name, currentNode);
12059
12060
  continue;
12060
12061
  }
12061
12062
  /* Handle attributes that require Trusted Types */
@@ -12076,19 +12077,23 @@
12076
12077
  }
12077
12078
  }
12078
12079
  /* Handle invalid data-* attribute set by try-catching it */
12079
- try {
12080
- if (namespaceURI) {
12081
- currentNode.setAttributeNS(namespaceURI, name, value);
12082
- } else {
12083
- /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
12084
- currentNode.setAttribute(name, value);
12085
- }
12086
- if (_isClobbered(currentNode)) {
12087
- _forceRemove(currentNode);
12088
- } else {
12089
- arrayPop(DOMPurify.removed);
12080
+ if (value !== initValue) {
12081
+ try {
12082
+ if (namespaceURI) {
12083
+ currentNode.setAttributeNS(namespaceURI, name, value);
12084
+ } else {
12085
+ /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
12086
+ currentNode.setAttribute(name, value);
12087
+ }
12088
+ if (_isClobbered(currentNode)) {
12089
+ _forceRemove(currentNode);
12090
+ } else {
12091
+ arrayPop(DOMPurify.removed);
12092
+ }
12093
+ } catch (_) {
12094
+ _removeAttribute(name, currentNode);
12090
12095
  }
12091
- } catch (_) {}
12096
+ }
12092
12097
  }
12093
12098
  /* Execute a hook if present */
12094
12099
  _executeHooks(hooks.afterSanitizeAttributes, currentNode, null);
@@ -16909,7 +16914,7 @@
16909
16914
  */
16910
16915
 
16911
16916
  const fixColor = (colorString) => {
16912
- if (isString$2(colorString)) {
16917
+ if (isString$3(colorString)) {
16913
16918
  return (colorString.indexOf(",") > 0 && !(colorString.startsWith("rgb(") || colorString.startsWith("rgba("))) ?
16914
16919
  `rgb(${colorString})` : colorString
16915
16920
  } else {
@@ -16981,7 +16986,7 @@
16981
16986
  this.name = config.name || config.label;
16982
16987
  } else if (isFile(config.url)) {
16983
16988
  this.name = config.url.name;
16984
- } else if (isString$2(config.url) && !config.url.startsWith("data:")) {
16989
+ } else if (isString$3(config.url) && !config.url.startsWith("data:")) {
16985
16990
  this.name = getFilename$2(config.url);
16986
16991
  }
16987
16992
 
@@ -20903,7 +20908,7 @@
20903
20908
  * THE SOFTWARE.
20904
20909
  */
20905
20910
 
20906
- const isString$1 = isString$2;
20911
+ const isString$2 = isString$3;
20907
20912
 
20908
20913
 
20909
20914
  class CustomServiceReader {
@@ -20942,7 +20947,7 @@
20942
20947
  if (data) {
20943
20948
  if (typeof this.config.parser === "function") {
20944
20949
  features = this.config.parser(data);
20945
- } else if (isString$1(data)) {
20950
+ } else if (isString$2(data)) {
20946
20951
  features = JSON.parse(data);
20947
20952
  } else {
20948
20953
  features = data;
@@ -23025,7 +23030,7 @@
23025
23030
  // TODO -- align all feature attribute names with UCSC, an use specific column
23026
23031
  for (let key of Object.keys(f)) {
23027
23032
  const v = f[key];
23028
- if (isString$2(v) && v.toLowerCase() === term.toLowerCase()) {
23033
+ if (isString$3(v) && v.toLowerCase() === term.toLowerCase()) {
23029
23034
  return true
23030
23035
  }
23031
23036
  }
@@ -23323,7 +23328,7 @@
23323
23328
  class ZoomLevelHeader {
23324
23329
  constructor(index, byteBuffer) {
23325
23330
  this.index = index;
23326
- this.reductionLevel = byteBuffer.getInt();
23331
+ this.reductionLevel = byteBuffer.getUInt();
23327
23332
  this.reserved = byteBuffer.getInt();
23328
23333
  this.dataOffset = byteBuffer.getLong();
23329
23334
  this.indexOffset = byteBuffer.getLong();
@@ -27649,7 +27654,7 @@
27649
27654
  fd.name &&
27650
27655
  fd.name.toLowerCase() === "name" &&
27651
27656
  fd.value &&
27652
- isString$2(fd.value) &&
27657
+ isString$3(fd.value) &&
27653
27658
  !fd.value.startsWith("<")) {
27654
27659
  const href = infoURL.replace("$$", feature.name);
27655
27660
  fd.value = `<a target=_blank href=${href}>${fd.value}</a>`;
@@ -28293,7 +28298,7 @@
28293
28298
  async postInit() {
28294
28299
  if(!this.featureSource) {
28295
28300
  // This will be the case when restoring from a session
28296
- const db = this.browser.genome.id; // TODO -- blat specific property
28301
+ const db = this.browser.genome.ucscID; // TODO -- blat specific property
28297
28302
  const url = this.browser.config["blatServerURL"];
28298
28303
  const features = await blat({url, userSeq: this.sequence, db});
28299
28304
  this._features = features;
@@ -28310,7 +28315,7 @@
28310
28315
  if (undefined === this.table) {
28311
28316
 
28312
28317
  const rows = this._features.map(f => [
28313
- f.chr,
28318
+ this.browser.genome.getChromosomeDisplayName(f.chr),
28314
28319
  (f.start + 1),
28315
28320
  f.end,
28316
28321
  f.strand,
@@ -28385,7 +28390,7 @@
28385
28390
 
28386
28391
  try {
28387
28392
 
28388
- const db = browser.genome.id; // TODO -- blat specific property
28393
+ const db = browser.genome.ucscID; // TODO -- blat specific property
28389
28394
  const url = browser.config["blatServerURL"] || defaultBlatServer;
28390
28395
  const features = await blat({url, userSeq: sequence, db});
28391
28396
 
@@ -30615,13 +30620,6 @@
30615
30620
  this.trackStanzas = trackStanzas;
30616
30621
  }
30617
30622
 
30618
- findCytobandURL() {
30619
- for (const t of this.trackStanzas) {
30620
- if (t.name === "cytoBandIdeo" && t.hasProperty("bigDataUrl")) {
30621
- return t.getProperty("bigDataUrl")
30622
- }
30623
- }
30624
- }
30625
30623
 
30626
30624
  getSupportedTrackCount() {
30627
30625
  let count = 0;
@@ -30961,6 +30959,7 @@
30961
30959
 
30962
30960
  const idMappings = new Map([
30963
30961
  ["hg38", "GCF_000001405.40"],
30962
+ ["hg38_1kg", "GCF_000001405.40"],
30964
30963
  ["mm39", "GCF_000001635.27"],
30965
30964
  ["mm10", "GCF_000001635.26"],
30966
30965
  ["bosTau9", "GCF_002263795.1"],
@@ -30997,12 +30996,16 @@
30997
30996
  this.genomeStanzas = genomeStanzas;
30998
30997
  this.trackStanzas = trackStanzas;
30999
30998
  this.groupStanzas = groupStanzas;
30999
+ this.cytobandStanza = null;
31000
31000
  this.trackHubMap = new Map();
31001
31001
 
31002
31002
  // trackStanzas will not be null if this is a "onefile" hub
31003
31003
  if (trackStanzas) {
31004
31004
  const genomeId = genomeStanzas[0].getProperty("genome"); // Assumption here this is a single genome hub
31005
31005
  this.trackHubMap.set(genomeId, new TrackDbHub(trackStanzas, groupStanzas));
31006
+
31007
+ // Search for cytoband track. This supports a special but important case -- Genark assembly hubs
31008
+ this.cytobandStanza = this.trackStanzas.find(t => t.name === "cytoBandIdeo" && t.hasProperty("bigDataUrl")) || null;
31006
31009
  }
31007
31010
  }
31008
31011
 
@@ -31104,6 +31107,10 @@
31104
31107
  config.twoBitBptURL = genomeStanza.getProperty("twoBitBptUrl");
31105
31108
  }
31106
31109
 
31110
+ if(this.cytobandStanza){
31111
+ config.cytobandBbURL = this.cytobandStanza.getProperty("bigDataUrl");
31112
+ }
31113
+
31107
31114
  if (this.hubStanza.hasProperty("longLabel")) {
31108
31115
  config.description = this.hubStanza.getProperty("longLabel").replace("/", "\n");
31109
31116
  } else {
@@ -31497,8 +31504,8 @@
31497
31504
  return host
31498
31505
  }
31499
31506
 
31500
- const DEFAULT_GENOMES_URL = "https://igv.org/genomes/genomes.json";
31501
- const BACKUP_GENOMES_URL = "https://raw.githubusercontent.com/igvteam/igv-genomes/refs/heads/main/dist/genomes.json";
31507
+ const DEFAULT_GENOMES_URL = "https://igv.org/genomes/genomes3.json";
31508
+ const BACKUP_GENOMES_URL = "https://raw.githubusercontent.com/igvteam/igv-data/refs/heads/main/genomes/web/genomes.json";
31502
31509
 
31503
31510
  const GenomeUtils = {
31504
31511
 
@@ -31523,7 +31530,7 @@
31523
31530
  } catch (error) {
31524
31531
  try {
31525
31532
  console.error("Error initializing default genomes:", error);
31526
- const jsonArray = await igvxhr.loadJson(BACKUP_GENOMES_URL, {timeout: 2000});
31533
+ const jsonArray = await igvxhr.loadJson(BACKUP_GENOMES_URL, {timeout: 10000});
31527
31534
  processJson(jsonArray, table);
31528
31535
  } catch (e) {
31529
31536
  console.error("Error initializing backup genomes:", error);
@@ -31553,7 +31560,7 @@
31553
31560
  expandReference: async function (alert, idOrConfig) {
31554
31561
 
31555
31562
  // idOrConfig might be a json string? I'm actually not sure how this arises.
31556
- if (isString$2(idOrConfig) && idOrConfig.startsWith("{")) {
31563
+ if (isString$3(idOrConfig) && idOrConfig.startsWith("{")) {
31557
31564
  try {
31558
31565
  idOrConfig = JSON.parse(idOrConfig);
31559
31566
  } catch (e) {
@@ -31562,7 +31569,7 @@
31562
31569
  }
31563
31570
 
31564
31571
  let genomeID;
31565
- if (isString$2(idOrConfig)) {
31572
+ if (isString$3(idOrConfig)) {
31566
31573
  genomeID = idOrConfig;
31567
31574
  } else if (idOrConfig.genome) {
31568
31575
  genomeID = idOrConfig.genome;
@@ -32508,7 +32515,7 @@
32508
32515
  let track = this.trackView.track;
32509
32516
  const dataList = track.popupData(clickState);
32510
32517
 
32511
- const popupClickHandlerResult = this.browser.fireEvent('trackclick', [track, dataList]);
32518
+ const popupClickHandlerResult = this.browser.fireEvent('trackclick', [track, dataList, clickState.genomicLocation]);
32512
32519
 
32513
32520
  let content;
32514
32521
  if (undefined === popupClickHandlerResult || true === popupClickHandlerResult) {
@@ -40998,6 +41005,9 @@
40998
41005
  return true
40999
41006
  }
41000
41007
 
41008
+ /**
41009
+ * Track for segmented copy number, mut, maf and shoebox files.
41010
+ */
41001
41011
  class SegTrack extends TrackBase {
41002
41012
 
41003
41013
  #sortDirections = new Map()
@@ -41145,7 +41155,10 @@
41145
41155
  }, e);
41146
41156
  }
41147
41157
 
41148
- menuItems.push({ element: createElementWithString('<div>Set color scale threshold</div>'), dialog: dialogPresentationHandler});
41158
+ menuItems.push({
41159
+ element: createElementWithString('<div>Set color scale threshold</div>'),
41160
+ dialog: dialogPresentationHandler
41161
+ });
41149
41162
  }
41150
41163
 
41151
41164
  menuItems.push('<hr/>');
@@ -41176,12 +41189,75 @@
41176
41189
 
41177
41190
  getSamples() {
41178
41191
  return {
41179
- names: this.sampleKeys,
41192
+ names: this.filteredSampleKeys,
41180
41193
  height: this.sampleHeight,
41181
41194
  yOffset: 0
41182
41195
  }
41183
41196
  }
41184
41197
 
41198
+ /**
41199
+ * Set the sample filter object. This is used to filter samples from the set based on values over a specified
41200
+ * genomic region. The values compared depend on the track data type:
41201
+ * - "seg" and "shoebox" -- average value over the region
41202
+ * - "mut" and "maf" -- count of features overlapping the region
41203
+ *
41204
+ * The method is asynchronous because it may need to fetch data from the server to compute the scores.
41205
+ * Computed scores are stored and used to filter the sample keys on demand.
41206
+ *
41207
+ * @param filterObject
41208
+ * @returns {Promise<void>}
41209
+ */
41210
+ async setSampleFilter(filterObject) {
41211
+ if (!filterObject) {
41212
+ this.config.filterObject = undefined;
41213
+ this.filterObject = undefined;
41214
+ this.trackView.repaintViews();
41215
+ } else {
41216
+ const filterObjectCopy = Object.assign({}, filterObject);
41217
+ this.config.filterObject = filterObjectCopy;
41218
+
41219
+ filterObject.scores = await this.computeRegionScores(filterObject);
41220
+ this.filterObject = filterObject;
41221
+ this.trackView.checkContentHeight();
41222
+ this.trackView.repaintViews();
41223
+ }
41224
+ // TODO - store filter object in session
41225
+ }
41226
+
41227
+
41228
+ /**
41229
+ * Filter function for sample keys.
41230
+ *
41231
+ * @param sampleKey
41232
+ * @returns {boolean}
41233
+ */
41234
+ filter(sampleKey) {
41235
+ if (this.filterObject) {
41236
+ const filterObject = this.filterObject;
41237
+ const scores = filterObject.scores;
41238
+ const score = scores[sampleKey];
41239
+
41240
+ if (this.type === 'seg') {
41241
+ if (filterObject.op === '>') {
41242
+ return score > filterObject.value
41243
+ } else if (filterObject.op === '<') {
41244
+ return score < filterObject.value
41245
+ }
41246
+ } else if (this.type === 'mut' || this.type === 'maf') {
41247
+ return 'HAS' === filterObject.op ? score : !score
41248
+ }
41249
+ }
41250
+ // else if (this.config.sampleFilter) {
41251
+ // return this.config.sampleFilter(sampleKey)
41252
+ // }
41253
+ return true
41254
+ }
41255
+
41256
+ get filteredSampleKeys() {
41257
+ return this.sampleKeys.filter(sampleKey => this.filter(sampleKey))
41258
+ }
41259
+
41260
+
41185
41261
  async getFeatures(chr, start, end) {
41186
41262
  const features = await this.featureSource.getFeatures({chr, start, end});
41187
41263
  // New segments could conceivably add new samples
@@ -41189,8 +41265,10 @@
41189
41265
 
41190
41266
  if (this.initialSort) {
41191
41267
  const sort = this.initialSort;
41268
+
41192
41269
  if (sort.option === undefined || sort.option.toUpperCase() === "VALUE") {
41193
- this.sortByValue(sort, features);
41270
+ const sortFeatures = (sort.chr === chr && sort.start >= start && sort.end <= end) ? features : undefined;
41271
+ this.sortByValue(sort, sortFeatures);
41194
41272
  } else if ("ATTRIBUTE" === sort.option.toUpperCase() && sort.attribute) {
41195
41273
  const sortDirection = "DESC" === sort.direction ? 1 : -1;
41196
41274
  this.sortByAttribute(sort.attribute, sortDirection);
@@ -41205,6 +41283,7 @@
41205
41283
 
41206
41284
  IGVGraphics.fillRect(context, 0, pixelTop, pixelWidth, pixelHeight, {'fillStyle': "rgb(255, 255, 255)"});
41207
41285
 
41286
+
41208
41287
  if (features && features.length > 0) {
41209
41288
 
41210
41289
  this.checkForLog(features);
@@ -41216,14 +41295,15 @@
41216
41295
 
41217
41296
  // Create a map for fast id -> row lookup
41218
41297
  const samples = {};
41219
- this.sampleKeys.forEach(function (id, index) {
41298
+ const filteredKeys = this.filteredSampleKeys;
41299
+ filteredKeys.forEach(function (id, index) {
41220
41300
  samples[id] = index;
41221
41301
  });
41222
41302
 
41223
41303
  let border;
41224
41304
  switch (this.displayMode) {
41225
41305
  case "FILL":
41226
- this.sampleHeight = pixelHeight / this.sampleKeys.length;
41306
+ this.sampleHeight = pixelHeight / filteredKeys.length;
41227
41307
  border = 0;
41228
41308
  break
41229
41309
 
@@ -41361,7 +41441,7 @@
41361
41441
  if (!features) return 0
41362
41442
  const sampleHeight = ("SQUISHED" === this.displayMode) ? this.squishedRowHeight : this.expandedRowHeight;
41363
41443
  this.updateSampleKeys(features);
41364
- return this.sampleKeys.length * sampleHeight
41444
+ return this.filteredSampleKeys.length * sampleHeight
41365
41445
  }
41366
41446
 
41367
41447
  /**
@@ -41370,16 +41450,38 @@
41370
41450
  async sortByValue(sort, featureList) {
41371
41451
 
41372
41452
  const chr = sort.chr;
41453
+ const start = sort.position !== undefined ? sort.position - 1 : sort.start;
41454
+ const end = sort.end === undefined ? start + 1 : sort.end;
41455
+ const scores = await this.computeRegionScores({chr, start, end}, featureList);
41456
+ const d2 = (sort.direction === "ASC" ? 1 : -1);
41457
+
41458
+ this.sampleKeys.sort(function (a, b) {
41459
+ let s1 = scores[a];
41460
+ let s2 = scores[b];
41461
+ if (!s1) s1 = d2 * Number.MAX_VALUE;
41462
+ if (!s2) s2 = d2 * Number.MAX_VALUE;
41463
+ if (s1 === s2) return 0
41464
+ else if (s1 > s2) return d2
41465
+ else return d2 * -1
41466
+ });
41467
+
41468
+ this.config.sort = sort;
41469
+ this.trackView.repaintViews();
41470
+ }
41471
+
41472
+
41473
+ async computeRegionScores(filterObject, featureList) {
41474
+
41475
+ const chr = filterObject.chr;
41373
41476
  let start, end;
41374
- if (sort.position) {
41375
- start = sort.position - 1;
41477
+ if (filterObject.position) {
41478
+ start = filterObject.position - 1;
41376
41479
  end = start + 1;
41377
41480
  } else {
41378
- start = sort.start;
41379
- end = sort.end;
41481
+ start = filterObject.start;
41482
+ end = filterObject.end;
41380
41483
  }
41381
41484
 
41382
-
41383
41485
  if (!featureList) {
41384
41486
  featureList = await this.featureSource.getFeatures({chr, start, end});
41385
41487
  }
@@ -41388,62 +41490,40 @@
41388
41490
  this.updateSampleKeys(featureList);
41389
41491
 
41390
41492
  const scores = {};
41391
- const d2 = (sort.direction === "ASC" ? 1 : -1);
41493
+ const bpLength = end - start + 1;
41494
+
41495
+ const mutationTypes = filterObject.value ? new Set(filterObject.value) : undefined;
41496
+
41497
+ for (let segment of featureList) {
41498
+ if (segment.end < start) continue
41499
+ if (segment.start > end) break
41500
+ const sampleKey = segment.sampleKey || segment.sample;
41501
+
41502
+ if ("mut" === this.type) {
41503
+ if (mutationTypes) {
41504
+ const mutationType = segment.getAttribute("Variant_Classification");
41505
+ if (mutationTypes.has(mutationType)) {
41506
+ // Just count features overlapping region per sample
41507
+ scores[sampleKey] = (scores[sampleKey] || 0) + 1;
41508
+ }
41509
+ } else {
41510
+ // Just count features overlapping region per sample
41511
+ scores[sampleKey] = (scores[sampleKey] || 0) + 1;
41512
+ }
41513
+ } else {
41392
41514
 
41393
- const sortSeg = () => {
41394
- // Compute weighted average score for each sample
41395
- const bpLength = end - start + 1;
41396
- for (let segment of featureList) {
41397
- if (segment.end < start) continue
41398
- if (segment.start > end) break
41399
41515
  const min = Math.max(start, segment.start);
41400
41516
  const max = Math.min(end, segment.end);
41401
41517
  const f = (max - min) / bpLength;
41402
- const sampleKey = segment.sampleKey || segment.sample;
41403
- const s = scores[sampleKey] || 0;
41404
- scores[sampleKey] = s + f * segment.value;
41405
- }
41406
-
41407
- // Now sort sample names by score
41408
- this.sampleKeys.sort(function (a, b) {
41409
- let s1 = scores[a];
41410
- let s2 = scores[b];
41411
- if (!s1) s1 = d2 * Number.MAX_VALUE;
41412
- if (!s2) s2 = d2 * Number.MAX_VALUE;
41413
- if (s1 === s2) return 0
41414
- else if (s1 > s2) return d2
41415
- else return d2 * -1
41416
- });
41417
- };
41418
-
41419
- const sortMut = () => {
41420
- // Compute weighted average score for each sample
41421
- for (let segment of featureList) {
41422
- if (segment.end < start) continue
41423
- if (segment.start > end) break
41424
- const sampleKey = segment.sampleKey || segment.sample;
41425
- if (!scores.hasOwnProperty(sampleKey) || segment.value.localeCompare(scores[sampleKey]) > 0) {
41426
- scores[sampleKey] = segment.value;
41427
- }
41428
- }
41429
- // Now sort sample names by score
41430
- this.sampleKeys.sort(function (a, b) {
41431
- let sa = scores[a] || "";
41432
- let sb = scores[b] || "";
41433
- return d2 * (sa.localeCompare(sb))
41434
- });
41435
- };
41436
-
41437
- if ("mut" === this.type) {
41438
- sortMut();
41439
- } else {
41440
- sortSeg();
41518
+ scores[sampleKey] = (scores[sampleKey] || 0) + f * segment.value;
41519
+ }
41441
41520
  }
41442
41521
 
41443
- this.trackView.repaintViews();
41522
+ return scores
41444
41523
 
41445
41524
  }
41446
41525
 
41526
+
41447
41527
  sortByAttribute(attribute, sortDirection) {
41448
41528
 
41449
41529
  this.sampleKeys = this.browser.sampleInfo.getSortedSampleKeysByAttribute(this.sampleKeys, attribute, sortDirection);
@@ -41512,7 +41592,7 @@
41512
41592
  const dirLabel = direction === "DESC" ? "descending" : "ascending";
41513
41593
  const sortLabel = this.type === 'seg' || this.type === 'shoebox' ?
41514
41594
  `Sort by value (${dirLabel})` :
41515
- `Sort by type (${dirLabel})`;
41595
+ `Sort by count (${dirLabel})`;
41516
41596
  return {
41517
41597
  label: sortLabel,
41518
41598
  click: () => {
@@ -41524,7 +41604,6 @@
41524
41604
  end: Math.floor(genomicLocation + bpWidth)
41525
41605
  };
41526
41606
  sortHandler(sort);
41527
- this.config.sort = sort;
41528
41607
  }
41529
41608
  }
41530
41609
  })
@@ -41694,13 +41773,13 @@
41694
41773
  }
41695
41774
  }
41696
41775
 
41697
- popupData(genomicLocation) {
41776
+ popupData(genomicLocation, hiddenTags, showTags) {
41698
41777
 
41699
- let nameValues = this.firstAlignment.popupData(genomicLocation);
41778
+ let nameValues = this.firstAlignment.popupData(genomicLocation, hiddenTags, showTags);
41700
41779
 
41701
41780
  if (this.secondAlignment) {
41702
41781
  nameValues.push("-------------------------------");
41703
- nameValues = nameValues.concat(this.secondAlignment.popupData(genomicLocation));
41782
+ nameValues = nameValues.concat(this.secondAlignment.popupData(genomicLocation, hiddenTags, showTags));
41704
41783
  }
41705
41784
  return nameValues
41706
41785
  }
@@ -43414,7 +43493,7 @@
43414
43493
  isNegativeStrand() {
43415
43494
  return (this.flags & READ_STRAND_FLAG$2) !== 0
43416
43495
  }
43417
-
43496
+
43418
43497
  isMateNegativeStrand() {
43419
43498
  return (this.flags & MATE_STRAND_FLAG$2) !== 0
43420
43499
  }
@@ -43462,7 +43541,7 @@
43462
43541
  return (genomicLocation >= s && genomicLocation <= (s + l))
43463
43542
  }
43464
43543
 
43465
- popupData(genomicLocation) {
43544
+ popupData(genomicLocation, hiddenTags, showTags) {
43466
43545
 
43467
43546
  // if the user clicks on a base next to an insertion, show just the
43468
43547
  // inserted bases in a popup (like in desktop IGV).
@@ -43545,15 +43624,20 @@
43545
43624
  }
43546
43625
  }
43547
43626
 
43548
- const hiddenTags = new Set(['SA', 'MD']);
43549
43627
  nameValues.push('<hr/>');
43550
43628
  for (let key in tagDict) {
43551
- if (!hiddenTags.has(key)) {
43629
+ if (showTags?.has(key)) {
43630
+ nameValues.push({name: key, value: tagDict[key]});
43631
+ } else if (showTags) {
43632
+ hiddenTags.add(key);
43633
+ } else if (!hiddenTags.has(key)) {
43552
43634
  nameValues.push({name: key, value: tagDict[key]});
43553
43635
  }
43554
43636
  }
43555
43637
 
43556
- nameValues.push({name: 'Hidden Tags', value: 'SA, MD'});
43638
+ if (hiddenTags && hiddenTags.size > 0) {
43639
+ nameValues.push({name: 'Hidden Tags', value: Array.from(hiddenTags).join(", ")});
43640
+ }
43557
43641
 
43558
43642
  nameValues.push('<hr/>');
43559
43643
  nameValues.push({name: 'Genomic Location: ', value: numberFormatter$1(1 + genomicLocation)});
@@ -43561,18 +43645,18 @@
43561
43645
  nameValues.push({name: 'Base Quality:', value: this.readBaseQualityAt(genomicLocation)});
43562
43646
 
43563
43647
  const bmSets = this.getBaseModificationSets();
43564
- if(bmSets) {
43648
+ if (bmSets) {
43565
43649
  const i = this.positionToReadIndex(genomicLocation);
43566
- if(undefined !== i) {
43650
+ if (undefined !== i) {
43567
43651
  let found = false;
43568
43652
  for (let bmSet of bmSets) {
43569
43653
  if (bmSet.containsPosition(i)) {
43570
- if(!found) {
43654
+ if (!found) {
43571
43655
  nameValues.push('<hr/>');
43572
43656
  nameValues.push('<b>Base modifications:</b>');
43573
43657
  found = true;
43574
43658
  }
43575
- const lh = Math.round((100/255) * byteToUnsignedInt(bmSet.likelihoods.get(i)));
43659
+ const lh = Math.round((100 / 255) * byteToUnsignedInt(bmSet.likelihoods.get(i)));
43576
43660
  nameValues.push(`${bmSet.fullName()} @ likelihood = ${lh}%`);
43577
43661
  }
43578
43662
  }
@@ -43662,7 +43746,7 @@
43662
43746
  const mm = this.tagDict["MM"] || this.tagDict["Mm"];
43663
43747
  const ml = this.tagDict["ML"] || this.tagDict["Ml"];
43664
43748
 
43665
- if (isString$2(mm) && (!ml || Array.isArray(ml))) { // minimal validation, 10X uses these reserved tags for something completely different
43749
+ if (isString$3(mm) && (!ml || Array.isArray(ml))) { // minimal validation, 10X uses these reserved tags for something completely different
43666
43750
  if (mm.length === 0) {
43667
43751
  this.baseModificationSets = EMPTY_SET;
43668
43752
  } else {
@@ -43675,7 +43759,7 @@
43675
43759
  return this.baseModificationSets
43676
43760
  }
43677
43761
 
43678
- getGroupValue( groupBy, tag, expectedPairOrientation) {
43762
+ getGroupValue(groupBy, tag, expectedPairOrientation) {
43679
43763
 
43680
43764
  const al = this;
43681
43765
  switch (groupBy) {
@@ -43712,7 +43796,7 @@
43712
43796
  }
43713
43797
  }
43714
43798
 
43715
- positionToReadIndex( position) {
43799
+ positionToReadIndex(position) {
43716
43800
  const block = blockAtGenomicLocation(this.blocks, position);
43717
43801
  if (block) {
43718
43802
  return (position - block.start) + block.seqOffset
@@ -45691,7 +45775,7 @@
45691
45775
 
45692
45776
  function inferIndexPath(url, extension) {
45693
45777
 
45694
- if (isString$2(url)) {
45778
+ if (isString$3(url)) {
45695
45779
  if (url.includes("?")) {
45696
45780
  const idx = url.indexOf("?");
45697
45781
  return url.substring(0, idx) + "." + extension + url.substring(idx)
@@ -45850,7 +45934,7 @@
45850
45934
  this.bamReader = new CramReader(config, genome, browser);
45851
45935
  } else {
45852
45936
  if (!this.config.indexURL && config.indexed !== false) {
45853
- if (isString$2(this.config.url)) {
45937
+ if (isString$3(this.config.url)) {
45854
45938
  const indexPath = inferIndexPath(this.config.url, "bai");
45855
45939
  if (indexPath) {
45856
45940
  console.warn(`Warning: no indexURL specified for ${this.config.url}. Guessing ${indexPath}`);
@@ -48162,7 +48246,7 @@
48162
48246
  highlightColor: undefined,
48163
48247
  minTLEN: undefined,
48164
48248
  maxTLEN: undefined,
48165
- tagColorPallete: "Set1"
48249
+ tagColorPallete: "Set1",
48166
48250
  }
48167
48251
 
48168
48252
  _colorTables = new Map()
@@ -48177,6 +48261,18 @@
48177
48261
  this.colorTable = new ColorTable(config.tagColorTable);
48178
48262
  }
48179
48263
 
48264
+ // Only one of showTags / hideTags should be specified. If both are specified showTags takes precedence.
48265
+ if (config.showTags && config.hideTags) {
48266
+ console.warn("Both showTags and hideTags specified. showTags will be used.");
48267
+ }
48268
+ if (config.showTags) {
48269
+ this.showTags = new Set(config.showTags);
48270
+ this.hiddenTags = new Set();
48271
+ } else {
48272
+ this.hiddenTags = new Set(config.hideTags || ["SA", "MD"]);
48273
+ }
48274
+
48275
+
48180
48276
  // Backward compatibility overrides
48181
48277
  if (config.largeFragmentLengthColor) this.largeTLENColor = config.largeFragmentLengthColor;
48182
48278
  if (config.pairOrienation) this.expectedPairOrientation = config.pairOrientation;
@@ -48737,7 +48833,7 @@
48737
48833
 
48738
48834
  popupData(clickState) {
48739
48835
  const clickedObject = this.getClickedObject(clickState);
48740
- return clickedObject ? clickedObject.popupData(clickState.genomicLocation) : undefined
48836
+ return clickedObject?.popupData(clickState.genomicLocation, this.hiddenTags, this.showTags)
48741
48837
  };
48742
48838
 
48743
48839
  /**
@@ -48768,7 +48864,10 @@
48768
48864
  colorByMenuItems.push({key: 'tlen', label: 'insert size (TLEN)'});
48769
48865
  colorByMenuItems.push({key: 'unexpectedPair', label: 'pair orientation & insert size (TLEN)'});
48770
48866
  }
48771
- colorByMenuItems.push({key: 'tag', label: 'tag'});
48867
+ if(this.colorBy && this.colorBy.startsWith("tag:")) {
48868
+ colorByMenuItems.push({key: this.colorBy, label: this.colorBy});
48869
+ }
48870
+ colorByMenuItems.push({key: 'tag', label: 'tag...'});
48772
48871
  for (let item of colorByMenuItems) {
48773
48872
  const selected = (this.colorBy === undefined && item.key === 'none') || this.colorBy === item.key;
48774
48873
  menuItems.push(this.colorByCB(item, selected));
@@ -49494,14 +49593,19 @@
49494
49593
  case "tag":
49495
49594
  const tagValue = alignment.tags()[tag];
49496
49595
  if (tagValue !== undefined) {
49497
- if (this.bamColorTag === tag) {
49596
+
49597
+ // If the tag value can be interpreted as a color, use it
49598
+ if(typeof tagValue.startsWith === 'function') {
49498
49599
  color = IGVColor.createColorStringSafe(tagValue);
49499
49600
  }
49500
- if (!this.colorTable) {
49501
- this.colorTable = new PaletteColorTable(this.tagColorPallete);
49502
- }
49503
- color = this.colorTable.getColor(tagValue);
49504
49601
 
49602
+ // Tag value is not a color, use a color table
49603
+ if (!color) {
49604
+ if (!this.colorTable) {
49605
+ this.colorTable = new PaletteColorTable(this.tagColorPallete);
49606
+ }
49607
+ color = this.colorTable.getColor(tagValue);
49608
+ }
49505
49609
  }
49506
49610
  break
49507
49611
  }
@@ -65718,7 +65822,7 @@ ${indent}columns: ${matrix.columns}
65718
65822
  * THE SOFTWARE.
65719
65823
  */
65720
65824
 
65721
- const isString = isString$2;
65825
+ const isString$1 = isString$3;
65722
65826
 
65723
65827
  const DEFAULT_VISIBILITY_WINDOW = 1000000;
65724
65828
  const TOP_MARGIN = 10;
@@ -65841,7 +65945,7 @@ ${indent}columns: ${matrix.columns}
65841
65945
  }
65842
65946
  if (undefined === this.visibilityWindow && this.config.indexed !== false) {
65843
65947
  const fn = isFile(this.config.url) ? this.config.url.name : this.config.url;
65844
- if (isString(fn) && fn.toLowerCase().includes("gnomad")) {
65948
+ if (isString$1(fn) && fn.toLowerCase().includes("gnomad")) {
65845
65949
  this.visibilityWindow = 1000; // these are known to be very dense
65846
65950
  } else if (typeof this.featureSource.defaultVisibilityWindow === 'function') {
65847
65951
  this.visibilityWindow = await this.featureSource.defaultVisibilityWindow();
@@ -69725,6 +69829,9 @@ ${indent}columns: ${matrix.columns}
69725
69829
  ['image', (config, browser) => new ImageTrack(config, browser)]
69726
69830
  ]);
69727
69831
 
69832
+ function knownTrackTypes () {
69833
+ return new Set(trackFunctions.keys())
69834
+ }
69728
69835
 
69729
69836
  /**
69730
69837
  * Return a track of the given type, passing configuration and a point to the IGV "Browser" object to its constructor function*
@@ -70217,7 +70324,7 @@ ${indent}columns: ${matrix.columns}
70217
70324
  })
70218
70325
  }
70219
70326
 
70220
- const _version = "3.3.0";
70327
+ const _version = "3.4.0";
70221
70328
  function version() {
70222
70329
  return _version
70223
70330
  }
@@ -70268,7 +70375,7 @@ ${indent}columns: ${matrix.columns}
70268
70375
  if (this.select.value.trim().toLowerCase() === "all" || this.select.value === "*") {
70269
70376
  if (browser.genome.wholeGenomeView) {
70270
70377
  const wgChr = browser.genome.getChromosome("all");
70271
- return {chr: "all", start: 0, end: wgChr.bpLength}
70378
+ browser.updateLoci([{chr: "all", start: 0, end: wgChr.bpLength}]);
70272
70379
  }
70273
70380
  } else {
70274
70381
  const chromosome = await browser.genome.loadChromosome(this.select.value);
@@ -72073,6 +72180,26 @@ ${indent}columns: ${matrix.columns}
72073
72180
  }
72074
72181
 
72075
72182
 
72183
+ const found = this.browser.findTracks(track => typeof track.sortByValue === 'function');
72184
+ if (found.length > 0) {
72185
+ const { chr, start, end } = feature;
72186
+ items.push(
72187
+ '<hr/>',
72188
+ {
72189
+ label: 'Sort by value (ascending)',
72190
+ click: () => Promise.all(found.map(track => track.sortByValue({ option: 'VALUE', direction: 'ASC', chr, start, end })))
72191
+ });
72192
+
72193
+ items.push(
72194
+ '<hr/>',
72195
+ {
72196
+ label: 'Sort by value (descending)',
72197
+ click: () => Promise.all(found.map(track => track.sortByValue({ option: 'VALUE', direction: 'DESC', chr, start, end })))
72198
+ });
72199
+
72200
+ }
72201
+
72202
+
72076
72203
  if (roiSet.isUserDefined) {
72077
72204
  items.push(
72078
72205
  '<hr/>',
@@ -72081,7 +72208,7 @@ ${indent}columns: ${matrix.columns}
72081
72208
  click: async () => {
72082
72209
  roiSet.removeFeature(feature);
72083
72210
  const userDefinedFeatures = await roiSet.getAllFeatures();
72084
-
72211
+
72085
72212
  // Delete user defined ROI Set if it is empty
72086
72213
  if (Object.keys(userDefinedFeatures).length === 0) {
72087
72214
  roiManager.deleteUserDefinedROISet();
@@ -73254,6 +73381,189 @@ ${indent}columns: ${matrix.columns}
73254
73381
 
73255
73382
  }
73256
73383
 
73384
+ /**
73385
+ * Update deprecated fasta & index urls in the reference object.
73386
+ */
73387
+
73388
+ const isString = (x) => {
73389
+ return (x && typeof x === "string") || x instanceof String
73390
+ };
73391
+
73392
+ /**
73393
+ * Replaces deprecated s3 fasta URLs with twoBitURLs. The purpose here is to rescue references from saved sessions
73394
+ * that contain pointers to deprectated IGV s3 buckets. These buckets will eventually be deleted
73395
+ *
73396
+ * @param reference
73397
+ */
73398
+ function updateReference(reference) {
73399
+
73400
+ if (!(requiresUpdate(reference) && updates[reference.id])) {
73401
+ return
73402
+ }
73403
+
73404
+ const updatedReference = updates[reference.id];
73405
+ if (updatedReference) {
73406
+ delete reference.fastaURL;
73407
+ if (reference.indexURL) delete reference.indexURL;
73408
+ reference.twoBitURL = updatedReference.twoBitURL;
73409
+ if (updatedReference.twoBitBptURL) reference.twoBitBptURL = updatedReference.twoBitBptURL;
73410
+ if (updatedReference.chromSizesURL) reference.chromSizesURL = updatedReference.chromSizesURL;
73411
+ }
73412
+ }
73413
+
73414
+ function requiresUpdate(reference) {
73415
+ return isString(reference.fastaURL) &&
73416
+ (reference.fastaURL.startsWith("https://igv.org") ||
73417
+ ["igv.org.genomes", "igv.broadinstitute.org", "igv.genepattern.org", "igvdata.broadinstitute.org",
73418
+ "igv-genepattern-org"].some(bucket => reference.fastaURL.includes(bucket)))
73419
+ }
73420
+
73421
+ const updates = {
73422
+ "hs1": {
73423
+ "twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/hs1/bigZips/hs1.2bit",
73424
+ "twoBitBptURL": "https://hgdownload.soe.ucsc.edu/goldenPath/hs1/bigZips/hs1.2bit.bpt",
73425
+ "chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/hs1/bigZips/hs1.chrom.sizes.txt"
73426
+ },
73427
+ "hg38": {
73428
+ "twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/hg38/bigZips/hg38.2bit",
73429
+ "chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/hg38/bigZips/hg38.chrom.sizes"
73430
+ },
73431
+ "hg19": {
73432
+ "twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/hg19/bigZips/hg19.2bit",
73433
+ "chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/hg19/bigZips/hg19.chrom.sizes"
73434
+ },
73435
+ "hg18": {
73436
+ "twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/hg18/bigZips/hg18.2bit",
73437
+ "chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/hg18/bigZips/hg18.chrom.sizes"
73438
+ },
73439
+ "mm39": {
73440
+ "twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/mm39/bigZips/mm39.2bit",
73441
+ "chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/mm39/bigZips/mm39.chrom.sizes"
73442
+ },
73443
+ "mm10": {
73444
+ "twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/mm10/bigZips/mm10.2bit",
73445
+ "chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/mm10/bigZips/mm10.chrom.sizes"
73446
+ },
73447
+ "mm9": {
73448
+ "twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/mm9/bigZips/mm9.2bit",
73449
+ "chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/mm9/bigZips/mm9.chrom.sizes"
73450
+ },
73451
+ "rn7": {
73452
+ "twoBitURL": "https://hgdownload.soe.ucsc.edu/hubs/GCF/015/227/675/GCF_015227675.2/GCF_015227675.2.2bit",
73453
+ "twoBitBptURL": "https://hgdownload.soe.ucsc.edu/hubs/GCF/015/227/675/GCF_015227675.2/GCF_015227675.2.2bit.bpt",
73454
+ "chromSizesURL": "https://hgdownload.soe.ucsc.edu/hubs/GCF/015/227/675/GCF_015227675.2/GCF_015227675.2.chrom.sizes.txt"
73455
+ },
73456
+ "rn6": {
73457
+ "twoBitURL": "https://hgdownload.soe.ucsc.edu/hubs/GCF/000/001/895/GCF_000001895.5/GCF_000001895.5.2bit",
73458
+ "twoBitBptURL": "https://hgdownload.soe.ucsc.edu/hubs/GCF/000/001/895/GCF_000001895.5/GCF_000001895.5.2bit.bpt",
73459
+ "chromSizesURL": "https://hgdownload.soe.ucsc.edu/hubs/GCF/000/001/895/GCF_000001895.5/GCF_000001895.5.chrom.sizes.txt"
73460
+ },
73461
+ "gorGor6": {
73462
+ "twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/gorGor6/bigZips/gorGor6.2bit",
73463
+ "chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/gorGor6/bigZips/gorGor6.chrom.sizes"
73464
+ },
73465
+ "gorGor4": {
73466
+ "twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/gorGor4/bigZips/gorGor4.2bit",
73467
+ "chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/gorGor4/bigZips/gorGor4.chrom.sizes"
73468
+ },
73469
+ "panTro6": {
73470
+ "twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/panTro6/bigZips/panTro6.2bit",
73471
+ "chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/panTro6/bigZips/panTro6.chrom.sizes"
73472
+ },
73473
+ "panTro5": {
73474
+ "twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/panTro5/bigZips/panTro5.2bit",
73475
+ "chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/panTro5/bigZips/panTro5.chrom.sizes"
73476
+ },
73477
+ "panTro4": {
73478
+ "twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/panTro4/bigZips/panTro4.2bit",
73479
+ "chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/panTro4/bigZips/panTro4.chrom.sizes"
73480
+ },
73481
+ "macFas5": {
73482
+ "twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/macFas5/bigZips/macFas5.2bit",
73483
+ "chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/macFas5/bigZips/macFas5.chrom.sizes"
73484
+ },
73485
+ "panPan2": {
73486
+ "twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/panPan2/bigZips/panPan2.2bit",
73487
+ "chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/panPan2/bigZips/panPan2.chrom.sizes"
73488
+ },
73489
+ "canFam6": {
73490
+ "twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/canFam6/bigZips/canFam6.2bit",
73491
+ "chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/canFam6/bigZips/canFam6.chrom.sizes"
73492
+ },
73493
+ "canFam5": {
73494
+ "twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/canFam5/bigZips/canFam5.2bit",
73495
+ "chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/canFam5/bigZips/canFam5.chrom.sizes"
73496
+ },
73497
+ "canFam4": {
73498
+ "twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/canFam4/bigZips/canFam4.2bit",
73499
+ "chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/canFam4/bigZips/canFam4.chrom.sizes"
73500
+ },
73501
+ "canFam3": {
73502
+ "twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/canFam3/bigZips/canFam3.2bit",
73503
+ "chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/canFam3/bigZips/canFam3.chrom.sizes"
73504
+ },
73505
+ "bosTau9": {
73506
+ "twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/bosTau9/bigZips/bosTau9.2bit",
73507
+ "chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/bosTau9/bigZips/bosTau9.chrom.sizes"
73508
+ },
73509
+ "bosTau8": {
73510
+ "twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/bosTau8/bigZips/bosTau8.2bit",
73511
+ "chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/bosTau8/bigZips/bosTau8.chrom.sizes"
73512
+ },
73513
+ "susScr11": {
73514
+ "twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/susScr11/bigZips/susScr11.2bit",
73515
+ "chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/susScr11/bigZips/susScr11.chrom.sizes"
73516
+ },
73517
+ "galGal6": {
73518
+ "twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/galGal6/bigZips/galGal6.2bit",
73519
+ "chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/galGal6/bigZips/galGal6.chrom.sizes"
73520
+ },
73521
+ "danRer11": {
73522
+ "twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/danRer11/bigZips/danRer11.2bit",
73523
+ "chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/danRer11/bigZips/danRer11.chrom.sizes"
73524
+ },
73525
+ "danRer10": {
73526
+ "twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/danRer10/bigZips/danRer10.2bit",
73527
+ "chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/danRer10/bigZips/danRer10.chrom.sizes"
73528
+ },
73529
+ "ce11": {
73530
+ "twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/ce11/bigZips/ce11.2bit",
73531
+ "chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/ce11/bigZips/ce11.chrom.sizes"
73532
+ },
73533
+ "dm6": {
73534
+ "twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/dm6/bigZips/dm6.2bit",
73535
+ "chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/dm6/bigZips/dm6.chrom.sizes"
73536
+ },
73537
+ "dm3": {
73538
+ "twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/dm3/bigZips/dm3.2bit",
73539
+ "chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/dm3/bigZips/dm3.chrom.sizes"
73540
+ },
73541
+ "sacCer3": {
73542
+ "twoBitURL": "https://hgdownload.soe.ucsc.edu/hubs/GCF/000/146/045/GCF_000146045.2/GCF_000146045.2.2bit",
73543
+ "twoBitBptURL": "https://hgdownload.soe.ucsc.edu/hubs/GCF/000/146/045/GCF_000146045.2/GCF_000146045.2.2bit.bpt",
73544
+ "chromSizesURL": "https://hgdownload.soe.ucsc.edu/hubs/GCF/000/146/045/GCF_000146045.2/GCF_000146045.2.chrom.sizes.txt"
73545
+ },
73546
+ "GCF_000002945.1": {
73547
+ "twoBitURL": "https://hgdownload.soe.ucsc.edu/hubs/GCF/000/002/945/GCF_000002945.1/GCF_000002945.1.2bit",
73548
+ "twoBitBptURL": "https://hgdownload.soe.ucsc.edu/hubs/GCF/000/002/945/GCF_000002945.1/GCF_000002945.1.2bit.bpt",
73549
+ "chromSizesURL": "https://hgdownload.soe.ucsc.edu/hubs/GCF/000/002/945/GCF_000002945.1/GCF_000002945.1.chrom.sizes.txt"
73550
+ },
73551
+ "GCF_009858895.2": {
73552
+ "twoBitURL": "https://hgdownload.soe.ucsc.edu/hubs/GCF/009/858/895/GCF_009858895.2/GCF_009858895.2.2bit",
73553
+ "twoBitBptURL": "https://hgdownload.soe.ucsc.edu/hubs/GCF/009/858/895/GCF_009858895.2/GCF_009858895.2.2bit.bpt",
73554
+ "chromSizesURL": "https://hgdownload.soe.ucsc.edu/hubs/GCF/009/858/895/GCF_009858895.2/GCF_009858895.2.chrom.sizes.txt"
73555
+ },
73556
+ "tair10": {
73557
+ "twoBitURL": "https://hgdownload.soe.ucsc.edu/hubs/GCF/000/001/735/GCF_000001735.3/GCF_000001735.3.2bit",
73558
+ "chromSizesURL": "https://hgdownload.soe.ucsc.edu/hubs/GCF/000/001/735/GCF_000001735.3/GCF_000001735.3.chrom.sizes.txt"
73559
+ },
73560
+ "GCA_000022165.1": {
73561
+ "twoBitURL": "https://hgdownload.soe.ucsc.edu/hubs/GCA/000/022/165/GCA_000022165.1/GCA_000022165.1.2bit",
73562
+ "twoBitBptURL": "https://hgdownload.soe.ucsc.edu/hubs/GCA/000/022/165/GCA_000022165.1/GCA_000022165.1.2bit.bpt",
73563
+ "chromSizesURL": "https://hgdownload.soe.ucsc.edu/hubs/GCA/000/022/165/GCA_000022165.1/GCA_000022165.1.chrom.sizes.txt"
73564
+ }
73565
+ };
73566
+
73257
73567
  const ucsdIDMap = new Map([
73258
73568
  ["1kg_ref", "hg18"],
73259
73569
  ["1kg_v37", "hg19"],
@@ -73275,6 +73585,7 @@ ${indent}columns: ${matrix.columns}
73275
73585
 
73276
73586
  static async createGenome(options, browser) {
73277
73587
 
73588
+ updateReference(options);
73278
73589
  const genome = new Genome(options, browser);
73279
73590
  await genome.init();
73280
73591
  return genome
@@ -73298,7 +73609,6 @@ ${indent}columns: ${matrix.columns}
73298
73609
  // Load sequence
73299
73610
  this.sequence = await loadSequence(config, this.browser);
73300
73611
 
73301
-
73302
73612
  // Load cytobands. This is optional but required to support the ideogram. Only needed for whole genome view
73303
73613
  if(false !== config.showIdeogram && false !== config.wholeGenomeView) {
73304
73614
  if (config.cytobandURL) {
@@ -73342,10 +73652,14 @@ ${indent}columns: ${matrix.columns}
73342
73652
  } else {
73343
73653
  this.#wgChromosomeNames = config.chromosomeOrder.split(',').map(nm => nm.trim());
73344
73654
  }
73655
+ // Trim to remove non-existent chromosomes
73656
+ await this.chromAlias.preload(this.#wgChromosomeNames);
73657
+ this.#wgChromosomeNames =
73658
+ this.#wgChromosomeNames.map(c => this.getChromosomeName(c)).filter(c => this.chromosomes.has(c));
73345
73659
  } else {
73346
73660
  this.#wgChromosomeNames = trimSmallChromosomes(this.chromosomes);
73661
+ await this.chromAlias.preload(this.#wgChromosomeNames);
73347
73662
  }
73348
- await this.chromAlias.preload(this.#wgChromosomeNames);
73349
73663
  }
73350
73664
 
73351
73665
  // Optionally create the psuedo chromosome "all" to support whole genome view
@@ -73618,7 +73932,7 @@ ${indent}columns: ${matrix.columns}
73618
73932
  function generateGenomeID(config) {
73619
73933
  if (config.id !== undefined) {
73620
73934
  return config.id
73621
- } else if (config.fastaURL && isString$2(config.fastaURL) && !config.fastaURL.startsWith("data:")) {
73935
+ } else if (config.fastaURL && isString$3(config.fastaURL) && !config.fastaURL.startsWith("data:")) {
73622
73936
  return config.fastaURL
73623
73937
  } else if (config.fastaURL && config.fastaURL.name) {
73624
73938
  return config.fastaURL.name
@@ -74351,7 +74665,7 @@ ${indent}columns: ${matrix.columns}
74351
74665
  const urlOrFile = options.url || options.file;
74352
74666
 
74353
74667
  let config;
74354
- if (options.url && isString$2(options.url) && (options.url.startsWith("blob:") || options.url.startsWith("data:"))) {
74668
+ if (options.url && isString$3(options.url) && (options.url.startsWith("blob:") || options.url.startsWith("data:"))) {
74355
74669
  const json = Browser.uncompressSession(options.url);
74356
74670
  config = JSON.parse(json);
74357
74671
  } else {
@@ -74430,7 +74744,7 @@ ${indent}columns: ${matrix.columns}
74430
74744
  return
74431
74745
  }
74432
74746
 
74433
- const genomeConfig = isString$2(genomeOrReference) ?
74747
+ const genomeConfig = isString$3(genomeOrReference) ?
74434
74748
  await GenomeUtils.expandReference(this.alert, genomeOrReference) :
74435
74749
  genomeOrReference;
74436
74750
 
@@ -74552,7 +74866,7 @@ ${indent}columns: ${matrix.columns}
74552
74866
  }
74553
74867
 
74554
74868
  /**
74555
- * Load a reference genome object. This includes the fasta, and optional cytoband, but no tracks. This method
74869
+ * Load a reference genome object. This includes the sequence, and optional cytoband, but no tracks. This method
74556
74870
  * is used by loadGenome and loadSession.
74557
74871
  *
74558
74872
  * @param genomeConfig
@@ -74617,7 +74931,7 @@ ${indent}columns: ${matrix.columns}
74617
74931
 
74618
74932
  // Translate the generic "url" field, used by clients such as igv-webapp
74619
74933
  if (idOrConfig.url) {
74620
- if (isString$2(idOrConfig.url) && idOrConfig.url.endsWith("/hub.txt")) {
74934
+ if (isString$3(idOrConfig.url) && idOrConfig.url.endsWith("/hub.txt")) {
74621
74935
  idOrConfig.hubURL = idOrConfig.url;
74622
74936
  delete idOrConfig.url;
74623
74937
  } else if ("gbk" === getFileExtension(idOrConfig.url)) {
@@ -74627,11 +74941,11 @@ ${indent}columns: ${matrix.columns}
74627
74941
  }
74628
74942
 
74629
74943
  let genomeConfig;
74630
- const isHubGenome = idOrConfig.hubURL || (idOrConfig.url && isString$2(idOrConfig.url) && idOrConfig.url.endsWith("/hub.txt"));
74944
+ const isHubGenome = idOrConfig.hubURL || (idOrConfig.url && isString$3(idOrConfig.url) && idOrConfig.url.endsWith("/hub.txt"));
74631
74945
  if (isHubGenome) {
74632
74946
  const hub = await loadHub(idOrConfig.hubURL || idOrConfig.url);
74633
74947
  genomeConfig = hub.getGenomeConfig();
74634
- } else if (isString$2(idOrConfig) || !(idOrConfig.url || idOrConfig.fastaURL || idOrConfig.twoBitURL || idOrConfig.gbkURL)) {
74948
+ } else if (isString$3(idOrConfig) || !(idOrConfig.url || idOrConfig.fastaURL || idOrConfig.twoBitURL || idOrConfig.gbkURL)) {
74635
74949
  // Either an ID, a json string, or an object missing required properties.
74636
74950
  genomeConfig = await GenomeUtils.expandReference(this.alert, idOrConfig);
74637
74951
  } else {
@@ -74798,7 +75112,7 @@ ${indent}columns: ${matrix.columns}
74798
75112
  async #loadTrackHelper(config) {
74799
75113
 
74800
75114
  // config might be json
74801
- if (isString$2(config)) {
75115
+ if (isString$3(config)) {
74802
75116
  config = JSON.parse(config);
74803
75117
  }
74804
75118
 
@@ -74928,7 +75242,7 @@ ${indent}columns: ${matrix.columns}
74928
75242
 
74929
75243
  // Resolve function and promise urls
74930
75244
  let url = await resolveURL(config.url || config.fastaURL);
74931
- if (isString$2(url)) {
75245
+ if (isString$3(url)) {
74932
75246
  url = url.trim();
74933
75247
  }
74934
75248
 
@@ -74976,7 +75290,7 @@ ${indent}columns: ${matrix.columns}
74976
75290
  const featureSource = FeatureSource(config, this.genome);
74977
75291
  config._featureSource = featureSource; // This is a temp variable, bit of a hack
74978
75292
  const trackType = await featureSource.trackType();
74979
- if (trackType) {
75293
+ if (trackType && knownTrackTypes().has(trackType)) {
74980
75294
  type = trackType;
74981
75295
  } else {
74982
75296
  type = "annotation";