igv 3.0.8 → 3.0.9

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.esm.js CHANGED
@@ -18593,7 +18593,7 @@ function rgbStringHeatMapLerp(_a, _b, interpolant) {
18593
18593
 
18594
18594
  }
18595
18595
 
18596
- const colorPickerTrackTypeSet = new Set(['bedtype', 'alignment', 'annotation', 'variant', 'wig', 'interact']);
18596
+ const colorPickerTrackTypeSet = new Set(['bedtype', 'alignment', 'annotation', 'variant', 'wig', 'interact', 'shoebox']);
18597
18597
 
18598
18598
  const vizWindowTypes = new Set(['alignment', 'annotation', 'variant', 'eqtl', 'qtl', 'snp', 'shoebox', 'wig']);
18599
18599
 
@@ -20936,125 +20936,6 @@ function createDOMPurify() {
20936
20936
  }
20937
20937
  var purify = createDOMPurify();
20938
20938
 
20939
- class SliderDialog {
20940
-
20941
- constructor(parent) {
20942
-
20943
- this.parent = parent;
20944
-
20945
- // dialog container
20946
- this.container = div({class: 'igv-ui-generic-dialog-container'});
20947
- parent.appendChild(this.container);
20948
-
20949
- // dialog header
20950
- const header = div({class: 'igv-ui-generic-dialog-header'});
20951
- this.container.appendChild(header);
20952
-
20953
- // dialog label
20954
- this.label = div({class: 'igv-ui-generic-dialog-one-liner'});
20955
- this.container.appendChild(this.label);
20956
- this.label.text = 'Unlabeled';
20957
-
20958
- // input container
20959
- this.input_container = div({class: 'igv-ui-generic-dialog-input'});
20960
- this.container.appendChild(this.input_container);
20961
-
20962
- // input element
20963
- let html = `<input type="range" id="igv-slider-dialog-input" name="igv-slider-dialog-input" />`;
20964
- this._input = document.createRange().createContextualFragment(html).firstChild;
20965
- this.input_container.appendChild(this._input);
20966
-
20967
- // output element
20968
- html = `<output id="igv-slider-dialog-output" name="igv-slider-dialog-output" for="igv-slider-dialog-input"></output>`;
20969
- this._output = document.createRange().createContextualFragment(html).firstChild;
20970
- this.input_container.appendChild(this._output);
20971
-
20972
-
20973
- // ok | cancel
20974
- const buttons = div({class: 'igv-ui-generic-dialog-ok-cancel'});
20975
- this.container.appendChild(buttons);
20976
-
20977
- // ok
20978
- this.ok = div();
20979
- buttons.appendChild(this.ok);
20980
- this.ok.textContent = 'OK';
20981
-
20982
- // cancel
20983
- this.cancel = div();
20984
- buttons.appendChild(this.cancel);
20985
- this.cancel.textContent = 'Cancel';
20986
-
20987
- hide(this.container);
20988
-
20989
- this._input.addEventListener('input', () => {
20990
- const number = parseFloat(this._input.value)/this._scaleFactor;
20991
- this.callback(number);
20992
- this._output.value = `${number.toFixed(2)}`;
20993
- }, false);
20994
-
20995
- this.ok.addEventListener('click', () => {
20996
- if (typeof this.callback === 'function') {
20997
- const number = parseFloat(this._input.value)/this._scaleFactor;
20998
- this.callback(number);
20999
- this.callback = undefined;
21000
- }
21001
- this._input.value = undefined;
21002
- hide(this.container);
21003
- });
21004
-
21005
- const cancel = () => {
21006
- this._input.value = undefined;
21007
- hide(this.container);
21008
- };
21009
-
21010
- this.cancel.addEventListener('click', cancel);
21011
-
21012
- attachDialogCloseHandlerWithParent(header, cancel);
21013
- makeDraggable(this.container, header);
21014
-
21015
- }
21016
-
21017
- get value() {
21018
- return purify.sanitize(this._input.value)
21019
- }
21020
-
21021
- present(options, e) {
21022
-
21023
- this.label.textContent = options.label;
21024
-
21025
- this._scaleFactor = options.scaleFactor;
21026
- const [ minS, maxS, valueS ] = [ options.min, options.max, options.value ].map(number => (Math.floor(this._scaleFactor * number)).toString());
21027
-
21028
- this._input.min = minS;
21029
- this._input.max = maxS;
21030
- this._input.value = valueS;
21031
-
21032
- const numer = parseFloat(valueS);
21033
- const denom = this._scaleFactor;
21034
- const number = numer/denom;
21035
- this._output.value = `${number.toFixed(2)}`;
21036
-
21037
- this.callback = options.callback || options.click;
21038
-
21039
- show(this.container);
21040
- this.clampLocation(e.clientX, e.clientY);
21041
-
21042
- }
21043
-
21044
- clampLocation(clientX, clientY) {
21045
-
21046
- const {width: w, height: h} = this.container.getBoundingClientRect();
21047
- const wh = window.innerHeight;
21048
- const ww = window.innerWidth;
21049
-
21050
- const y = Math.min(wh - h, clientY);
21051
- const x = Math.min(ww - w, clientX);
21052
- this.container.style.left = `${x}px`;
21053
- this.container.style.top = `${y}px`;
21054
-
21055
- }
21056
- }
21057
-
21058
20939
  class InputDialog {
21059
20940
 
21060
20941
  constructor(parent) {
@@ -24508,6 +24389,29 @@ function decodeSNP(tokens, header) {
24508
24389
 
24509
24390
  }
24510
24391
 
24392
+ function decodeShoebox(tokens, header, maxColumnCount = Number.MAX_SAFE_INTEGER) {
24393
+
24394
+ if (tokens.length < 4) return undefined
24395
+
24396
+ const chr = tokens[0];
24397
+ const start = parseInt(tokens[1]);
24398
+ const end = tokens.length > 2 ? parseInt(tokens[2]) : start + 1;
24399
+ if (isNaN(start) || isNaN(end)) {
24400
+ return new DecodeError(`Unparsable bed record.`)
24401
+ }
24402
+ const feature = new UCSCBedFeature({chr: chr, start: start, end: end, score: 1000});
24403
+
24404
+ const values = [];
24405
+ for(let i = 3; i< tokens.length; i++) {
24406
+ values.push(Number.parseInt(tokens[i]));
24407
+ }
24408
+ feature.values = values;
24409
+
24410
+
24411
+ return feature
24412
+ }
24413
+
24414
+
24511
24415
  class UCSCBedFeature {
24512
24416
 
24513
24417
  constructor(properties) {
@@ -25872,6 +25776,10 @@ class FeatureParser {
25872
25776
  this.decode = decodeGcnv;
25873
25777
  this.delimiter = "\t";
25874
25778
  break
25779
+ case "shoebox":
25780
+ this.decode = decodeShoebox;
25781
+ this.delimiter = "\t";
25782
+ break
25875
25783
  default:
25876
25784
  const customFormat = getFormat(format);
25877
25785
  if (customFormat !== undefined) {
@@ -31958,14 +31866,25 @@ class BWReader {
31958
31866
  this.config = config;
31959
31867
  this.bufferSize = BUFFER_SIZE;
31960
31868
  this.loader = isDataURL(this.path) ?
31961
- new DataBuffer(this.path) : igvxhr;
31869
+ new DataBuffer(decodeDataURI$1(this.path).buffer) :
31870
+ igvxhr;
31962
31871
 
31963
- if (config.searchTrix) {
31964
- this._trix = new Trix(`${config.searchTrix}x`, config.searchTrix);
31872
+ const trixURL = config.trixURL || config.searchTrix;
31873
+ if (trixURL) {
31874
+ this._trix = new Trix(`${trixURL}x`, trixURL);
31965
31875
  }
31966
31876
 
31967
31877
  }
31968
31878
 
31879
+ /**
31880
+ * Preload all the data for this bb file
31881
+ * @returns {Promise<void>}
31882
+ */
31883
+ async preload() {
31884
+ const data = await igvxhr.loadArrayBuffer(this.path);
31885
+ this.loader = new DataBuffer(data);
31886
+ }
31887
+
31969
31888
  async readWGFeatures(bpPerPixel, windowFunction) {
31970
31889
  await this.loadHeader();
31971
31890
  const chrIdx1 = 0;
@@ -32631,8 +32550,8 @@ function decodeZoomData(data, chrIdx1, bpStart, chrIdx2, bpEnd, featureArray, ch
32631
32550
 
32632
32551
  class DataBuffer {
32633
32552
 
32634
- constructor(dataURI) {
32635
- this.data = decodeDataURI$1(dataURI).buffer;
32553
+ constructor(data) {
32554
+ this.data = data;
32636
32555
  }
32637
32556
 
32638
32557
  /**
@@ -38019,6 +37938,16 @@ class Hub {
38019
37938
  const groupsTxtURL = baseURL + genome.getProperty("groups");
38020
37939
  groups = await loadStanzas(groupsTxtURL);
38021
37940
  }
37941
+
37942
+ // If the genome has a chromSizes file, and it is not too large, set the chromSizesURL property. This will
37943
+ // enable whole genome view and the chromosome pulldown
37944
+ if (genome.hasProperty("chromSizes")) {
37945
+ const chromSizesURL = baseURL + genome.getProperty("chromSizes");
37946
+ const l = await getContentLength(chromSizesURL);
37947
+ if (l !== null && Number.parseInt(l) < 1000000) {
37948
+ genome.setProperty("chromSizesURL", chromSizesURL);
37949
+ }
37950
+ }
38022
37951
  }
38023
37952
 
38024
37953
  // TODO -- categorize extra "user" supplied and other tracks in some distinctive way before including them
@@ -38121,10 +38050,15 @@ isPcr dynablat-01.soe.ucsc.edu 4040 dynamic GCF/000/186/305/GCF_000186305.1
38121
38050
  name: name,
38122
38051
  twoBitURL: this.baseURL + this.genomeStanza.getProperty("twoBitPath"),
38123
38052
  nameSet: "ucsc",
38124
- wholeGenomeView: false,
38125
- showChromosomeWidget: false
38126
38053
  };
38127
38054
 
38055
+ if (this.genomeStanza.hasProperty("chromSizesURL")) {
38056
+ config.chromSizesURL = this.genomeStanza.getProperty("chromSizesURL");
38057
+ } else {
38058
+ config.wholeGenomeView = false;
38059
+ config.showChromosomeWidget = false;
38060
+ }
38061
+
38128
38062
  if (this.genomeStanza.hasProperty("defaultPos")) {
38129
38063
  const hubLocus = this.genomeStanza.getProperty("defaultPos");
38130
38064
  // Strip out coordinates => whole chromosome view
@@ -38325,7 +38259,7 @@ isPcr dynablat-01.soe.ucsc.edu 4040 dynamic GCF/000/186/305/GCF_000186305.1
38325
38259
  config.searchIndex = t.getProperty("searchIndex");
38326
38260
  }
38327
38261
  if (t.hasProperty("searchTrix")) {
38328
- config.searchTrix = this.baseURL + t.getProperty("searchTrix");
38262
+ config.trixURL = this.baseURL + t.getProperty("searchTrix");
38329
38263
  }
38330
38264
 
38331
38265
  if (t.hasProperty("group")) {
@@ -38412,6 +38346,26 @@ class Stanza {
38412
38346
  }
38413
38347
  }
38414
38348
 
38349
+
38350
+ /**
38351
+ * Return the content length of the resource. If the content length cannot be determined return null;
38352
+ * @param url
38353
+ * @returns {Promise<number|string>}
38354
+ */
38355
+ async function getContentLength(url) {
38356
+ try {
38357
+ const response = await fetch(url, {method: 'HEAD'});
38358
+ const headers = response.headers;
38359
+ if (headers.has("content-length")) {
38360
+ return headers.get("content-length")
38361
+ } else {
38362
+ return null
38363
+ }
38364
+ } catch (e) {
38365
+ return null
38366
+ }
38367
+ }
38368
+
38415
38369
  /**
38416
38370
  * Parse a UCSC file
38417
38371
  * @param url
@@ -41960,7 +41914,7 @@ const hideAllMenuPopups = parent => {
41960
41914
 
41961
41915
  class NavbarButton {
41962
41916
 
41963
- constructor(browser, parent, title, buttonLabel, imageSVG, imageHoverSVG, initialButtonState) {
41917
+ constructor(parent, browser, title, buttonLabel, imageSVG, imageHoverSVG, initialButtonState) {
41964
41918
 
41965
41919
  this.browser = browser;
41966
41920
 
@@ -42017,7 +41971,7 @@ class NavbarButton {
42017
41971
  }
42018
41972
 
42019
41973
  configureTextButton(textContent) {
42020
-
41974
+ console.log(`text ${this.title}`);
42021
41975
  this.button.classList.add('igv-navbar-text-button');
42022
41976
 
42023
41977
  const tempDiv = document.createElement('div');
@@ -42032,6 +41986,7 @@ class NavbarButton {
42032
41986
  }
42033
41987
 
42034
41988
  configureIconButton() {
41989
+ console.log(`icon ${this.title}`);
42035
41990
  this.button.classList.add('igv-navbar-icon-button');
42036
41991
  }
42037
41992
 
@@ -42071,11 +42026,6 @@ class NavbarButton {
42071
42026
  this.hide();
42072
42027
  }
42073
42028
  }
42074
-
42075
- static currentNavbarButtonClass(browser) {
42076
- const el = browser.$navigation.get(0).querySelector('.igv-navbar-text-button');
42077
- return el ? 'igv-navbar-text-button' : 'igv-navbar-icon-button'
42078
- }
42079
42029
  }
42080
42030
 
42081
42031
  const overlayTrackImage =
@@ -42284,6 +42234,19 @@ class MergedTrack extends TrackBase {
42284
42234
  return this._autoscale
42285
42235
  }
42286
42236
 
42237
+ set autoscaleGroup(g) {
42238
+ if(this.tracks) {
42239
+ for(let t of this.tracks) t.autoscaleGroup = g;
42240
+ }
42241
+ }
42242
+
42243
+ get autoscaleGroup() {
42244
+ if(this.tracks && this.tracks.length > 0) {
42245
+ const g = this.tracks[0].autoscaleGroup;
42246
+ return (this.tracks.some(t => g !== t.autoscaleGroup)) ? undefined : g
42247
+ }
42248
+ }
42249
+
42287
42250
  /**
42288
42251
  * Set the data range of all constitutive numeric tracks. This method is called from the menu item, i.e. an explicit
42289
42252
  * setting, so it should disable autoscale as well.
@@ -42648,9 +42611,9 @@ const numericTracks = (tracks) => {
42648
42611
  };
42649
42612
 
42650
42613
  class OverlayTrackButton extends NavbarButton {
42651
- constructor(browser, parent) {
42614
+ constructor(parent, browser) {
42652
42615
 
42653
- super(browser, parent, 'Overlay Tracks', buttonLabel, overlayTrackImage, overlayTrackImageHover, false);
42616
+ super(parent, browser, 'Overlay Tracks', buttonLabel, overlayTrackImage, overlayTrackImageHover, false);
42654
42617
 
42655
42618
  this.button.addEventListener('mouseenter', () => this.setState(true));
42656
42619
  this.button.addEventListener('mouseleave', () => this.setState(false));
@@ -42673,15 +42636,15 @@ function trackOverlayClickHandler(e) {
42673
42636
 
42674
42637
  if (true === isOverlayTrackCriteriaMet(this.browser)) {
42675
42638
 
42676
- const tracks = this.browser.getSelectedTrackViews().map(({ track }) => track);
42639
+ const tracks = this.browser.getSelectedTrackViews().map(({track}) => track);
42677
42640
  for (const track of tracks) {
42678
42641
  track.selected = false;
42679
42642
  }
42680
42643
 
42681
- // Flatten any merged tracks. Must do this before there removal
42644
+ // Flatten any merged tracks. Must do this before their removal
42682
42645
  const flattenedTracks = [];
42683
- for(let t of tracks) {
42684
- if("merged" === t.type) {
42646
+ for (let t of tracks) {
42647
+ if ("merged" === t.type) {
42685
42648
  flattenedTracks.push(...t.tracks);
42686
42649
  } else {
42687
42650
  flattenedTracks.push(t);
@@ -42694,17 +42657,20 @@ function trackOverlayClickHandler(e) {
42694
42657
  type: 'merged',
42695
42658
  autoscale: false,
42696
42659
  alpha: 0.5, //fudge * (1.0/tracks.length),
42697
- height: Math.max(...tracks.map(({ height }) => height)),
42698
- order: Math.min(...tracks.map(({ order }) => order)),
42660
+ height: Math.max(...tracks.map(({height}) => height)),
42661
+ order: Math.min(...tracks.map(({order}) => order)),
42699
42662
  };
42700
42663
 
42701
42664
  const mergedTrack = new MergedTrack(config, this.browser, flattenedTracks);
42702
42665
 
42703
42666
  for (const track of tracks) {
42704
- this.browser.removeTrack(track);
42667
+ const idx = this.browser.trackViews.indexOf(track.trackView);
42668
+ this.browser.trackViews.splice(idx, 1);
42669
+ track.trackView.dispose();
42705
42670
  }
42706
42671
 
42707
42672
  this.browser.addTrack(config, mergedTrack);
42673
+ mergedTrack.trackView.updateViews();
42708
42674
 
42709
42675
  }
42710
42676
 
@@ -42716,9 +42682,9 @@ function isOverlayTrackCriteriaMet(browser) {
42716
42682
 
42717
42683
  if (selected && selected.length > 1) {
42718
42684
 
42719
- const criteriaSet = new Set([ 'wig', 'merged' ]);
42685
+ const criteriaSet = new Set(['wig', 'merged']);
42720
42686
 
42721
- const list = selected.filter(({ track }) => criteriaSet.has(track.type));
42687
+ const list = selected.filter(({track}) => criteriaSet.has(track.type));
42722
42688
 
42723
42689
  return list.length > 1
42724
42690
 
@@ -42833,6 +42799,8 @@ class TrackView {
42833
42799
  createAxis(browser, track) {
42834
42800
 
42835
42801
  const axis = div();
42802
+ this.axis = axis;
42803
+
42836
42804
  browser.columnContainer.querySelector('.igv-axis-column').appendChild(axis);
42837
42805
 
42838
42806
  axis.dataset.tracktype = track.type;
@@ -42850,12 +42818,12 @@ class TrackView {
42850
42818
 
42851
42819
  if (false === multiTrackSelectExclusionTypes.has(this.track.type)) {
42852
42820
 
42853
- const trackSelectionContainer = div();
42854
- axis.appendChild(trackSelectionContainer);
42821
+ this.trackSelectionContainer = div();
42822
+ axis.appendChild(this.trackSelectionContainer);
42855
42823
 
42856
42824
  const html = `<input type="checkbox" name="track-select">`;
42857
42825
  const input = document.createRange().createContextualFragment(html).firstChild;
42858
- trackSelectionContainer.appendChild(input);
42826
+ this.trackSelectionContainer.appendChild(input);
42859
42827
  input.checked = this.track.selected || false;
42860
42828
 
42861
42829
  input.addEventListener('change', event => {
@@ -42863,10 +42831,10 @@ class TrackView {
42863
42831
  event.stopPropagation();
42864
42832
  this.track.selected = event.target.checked;
42865
42833
  this.setDragHandleSelectionState(event.target.checked);
42866
- this.browser.overlayTrackButton.setVisibility( isOverlayTrackCriteriaMet(this.browser) );
42834
+ this.browser.overlayTrackButton.setVisibility(isOverlayTrackCriteriaMet(this.browser));
42867
42835
  });
42868
42836
 
42869
- this.setTrackSelectionState(axis, false);
42837
+ this.enableTrackSelection(false);
42870
42838
 
42871
42839
  }
42872
42840
 
@@ -43676,13 +43644,19 @@ class TrackView {
43676
43644
  return Math.max(...this.viewports.map(viewport => viewport.getContentHeight()))
43677
43645
  }
43678
43646
 
43679
- setTrackSelectionState(axis, doEnableMultiSelection) {
43647
+ enableTrackSelection(doEnableMultiSelection) {
43680
43648
 
43681
- const container = axis.querySelector('div');
43649
+ const container = this.trackSelectionContainer;
43650
+
43651
+ if (!container || multiTrackSelectExclusionTypes.has(this.track.type)) {
43652
+ return
43653
+ }
43682
43654
 
43683
43655
  if (false !== doEnableMultiSelection) {
43684
43656
  container.style.display = 'grid';
43685
43657
  } else {
43658
+ // If disabling selection set track selection state to false
43659
+ this.track.selected = false;
43686
43660
 
43687
43661
  const trackSelectInput = container.querySelector('[name=track-select]');
43688
43662
  trackSelectInput.checked = this.track.selected;
@@ -43708,13 +43682,11 @@ class TrackView {
43708
43682
  dragHandle.classList.remove('igv-track-drag-handle-selected-color');
43709
43683
  dragHandle.classList.add('igv-track-drag-handle-color');
43710
43684
  }
43711
-
43712
43685
  }
43713
43686
 
43714
43687
  }
43715
43688
 
43716
43689
 
43717
-
43718
43690
  function renderSVGAxis(context, track, axisCanvas, deltaX, deltaY) {
43719
43691
 
43720
43692
  if (typeof track.paintAxis === 'function') {
@@ -43828,13 +43800,13 @@ class ConstantColorScale {
43828
43800
  *
43829
43801
  */
43830
43802
 
43831
- const defaultColorScaleConfig = {threshold: 2000, r: 0, g: 0, b: 255};
43803
+ const defaultColorScaleConfig$1 = {threshold: 2000, r: 0, g: 0, b: 255};
43832
43804
 
43833
43805
  class HicColorScale {
43834
43806
 
43835
43807
  constructor(scale) {
43836
43808
 
43837
- scale = scale || defaultColorScaleConfig;
43809
+ scale = scale || defaultColorScaleConfig$1;
43838
43810
  this.threshold = scale.threshold;
43839
43811
  this.r = scale.r;
43840
43812
  this.g = scale.g;
@@ -54037,6 +54009,10 @@ async function inferFileFormatFromContents(config) {
54037
54009
  if (firstLine.startsWith("##gff-version")) {
54038
54010
  return "gff"
54039
54011
  }
54012
+ if(firstLine.startsWith("##fileformat=")) {
54013
+ return firstLine.substring(13).toLowerCase(); // Non standard extension of VCF convention
54014
+ }
54015
+
54040
54016
 
54041
54017
  // QTL test must preceed GWAS test as GWAS files will also pass the QTL test
54042
54018
  if (QTLParser.isQTL(firstLine)) {
@@ -57273,10 +57249,6 @@ class AlignmentTrack extends TrackBase {
57273
57249
 
57274
57250
  function clickHandler() {
57275
57251
  this.alignmentTrack.colorBy = menuItem.key;
57276
- if ('strand' !== this.alignmentTrack.groupBy) {
57277
- this.alignmentTrack.groupBy = 'strand';
57278
- this.alignmentTrack.repackAlignments();
57279
- }
57280
57252
  this.trackView.repaintViews();
57281
57253
  }
57282
57254
 
@@ -67865,6 +67837,7 @@ async function searchFeatures(browser, name) {
67865
67837
  const searchConfig = browser.searchConfig || DEFAULT_SEARCH_CONFIG;
67866
67838
  let feature;
67867
67839
 
67840
+ name = name.toUpperCase();
67868
67841
  const searchableTracks = browser.tracks.filter(t => t.searchable);
67869
67842
  for (let track of searchableTracks) {
67870
67843
  const feature = await track.search(name);
@@ -70203,6 +70176,343 @@ class SpliceJunctionTrack extends TrackBase {
70203
70176
  }
70204
70177
  }
70205
70178
 
70179
+ /*
70180
+ * The MIT License (MIT)
70181
+ *
70182
+ * Copyright (c) 2016-2017 The Regents of the University of California
70183
+ *
70184
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
70185
+ * associated documentation files (the "Software"), to deal in the Software without restriction, including
70186
+ * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
70187
+ * copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
70188
+ * following conditions:
70189
+ *
70190
+ * The above copyright notice and this permission notice shall be included in all copies or substantial
70191
+ * portions of the Software.
70192
+ *
70193
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
70194
+ * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
70195
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
70196
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
70197
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
70198
+ * THE SOFTWARE.
70199
+ *
70200
+ */
70201
+
70202
+ const defaultColorScaleConfig = {min: 0, max: 3000, color: "rgb(0,0,255)"};
70203
+
70204
+ class ShoeboxColorScale {
70205
+
70206
+ constructor(scale) {
70207
+
70208
+ scale = scale || defaultColorScaleConfig;
70209
+ this.max = scale.max;
70210
+ this.min = scale.min || 0;
70211
+ this.cache = [];
70212
+ this.nbins = 1000;
70213
+ this.binsize = (this.max - this.min) / this.nbins;
70214
+ this.updateColor(scale.color || "rgb(0,0,255)");
70215
+
70216
+ }
70217
+
70218
+ updateColor(color) {
70219
+ const comps = color.substring(4).replace(")", "").split(",");
70220
+ if (comps.length === 3) {
70221
+ this.r = Number.parseInt(comps[0].trim());
70222
+ this.g = Number.parseInt(comps[1].trim());
70223
+ this.b = Number.parseInt(comps[2].trim());
70224
+ }
70225
+ this.cache = [];
70226
+ }
70227
+
70228
+ setMinMax(min, max) {
70229
+ this.min = min;
70230
+ this.max = max;
70231
+ this.cache = [];
70232
+ this.binsize = (this.max - this.min) / this.nbins;
70233
+ }
70234
+
70235
+ getColor(value) {
70236
+ const low = 0;
70237
+ if (value < this.min) return "white"
70238
+
70239
+ const bin = Math.floor((Math.min(this.max, value) - this.min) / this.binsize);
70240
+ if (undefined === this.cache[bin]) {
70241
+ const alpha = (IGVMath.clamp(value, low, this.max) - low) / (this.max - low);
70242
+ this.cache[bin] = `rgba(${this.r},${this.g},${this.b}, ${alpha})`;
70243
+ }
70244
+ return this.cache[bin]
70245
+ }
70246
+
70247
+ /**
70248
+ *
70249
+ * @returns {{min: (*|number), color: string, max}}
70250
+ */
70251
+ toJson() {
70252
+ return {
70253
+ min: this.min,
70254
+ max: this.max,
70255
+ color: `rgb(${this.r},${this.g},${this.b})`
70256
+ }
70257
+ }
70258
+
70259
+ // For short-term backward compatibility
70260
+ static parse(str) {
70261
+
70262
+ const tokens = str.split(",");
70263
+
70264
+ const cs = {
70265
+ min: Number.parseFloat(tokens[0]),
70266
+ max: Number.parseFloat(tokens[1]),
70267
+ color: `${tokens[2]},${tokens[3]},${tokens[4]}`
70268
+ };
70269
+ return new ShoeboxColorScale(cs)
70270
+ }
70271
+
70272
+ }
70273
+
70274
+ class ShoeboxTrack extends TrackBase {
70275
+
70276
+ static defaults = {
70277
+ height: 300,
70278
+ rowHeight: 3,
70279
+ max: 3000,
70280
+ visibilityWindow: 10000
70281
+ }
70282
+
70283
+ constructor(config, browser) {
70284
+ super(config, browser);
70285
+ }
70286
+
70287
+ init(config) {
70288
+ super.init(config);
70289
+
70290
+ this.type = "shoebox";
70291
+ this.height = config.height || 300;
70292
+ this.rowHeight = config.rowHeight || 3;
70293
+ this.max = config.max || 3000;
70294
+
70295
+ // Hardcoded -- todo get from track line
70296
+ this.sampleKeys = [];
70297
+ for (let i = 1; i <= 100; i++) {
70298
+ this.sampleKeys.push(i);
70299
+ }
70300
+
70301
+ // Create featureSource
70302
+ const configCopy = Object.assign({}, this.config);
70303
+ configCopy.format = 'shoebox'; // bit of a hack
70304
+ this.featureSource = FeatureSource(configCopy, this.browser.genome);
70305
+ }
70306
+
70307
+ async postInit() {
70308
+ if (typeof this.featureSource.getHeader === "function") {
70309
+ this.header = await this.featureSource.getHeader();
70310
+ if (this.disposed) return // This track was removed during async load
70311
+ }
70312
+ // Set properties from track line
70313
+ if (this.header) {
70314
+ this.setTrackProperties(this.header);
70315
+ }
70316
+
70317
+ // Must do the following after setting track properties as they can be overriden via a track line
70318
+
70319
+ // Color settings
70320
+ if (this.config.colorScale && this.config.colorScale.max && this.config.colorScale.color) { // Minimal validation
70321
+ this.colorScale = new ShoeboxColorScale(this.config.colorScale);
70322
+
70323
+ } else {
70324
+ const min = this.dataRange.min;
70325
+ const max = this.dataRange.max;
70326
+ this.colorScale = new ShoeboxColorScale({min, max, color: this.color});
70327
+ }
70328
+ }
70329
+
70330
+ get color() {
70331
+ return this._color || "rgb(0,0,255)"
70332
+ }
70333
+
70334
+ set color(color) {
70335
+ this._color = color;
70336
+ if (this.colorScale) {
70337
+ this.colorScale.updateColor(color);
70338
+ }
70339
+ }
70340
+
70341
+ menuItemList() {
70342
+
70343
+ const menuItems = [];
70344
+
70345
+
70346
+ menuItems.push('<hr/>');
70347
+
70348
+ // Data range
70349
+ let object = $$1('<div>');
70350
+ object.text('Set data range');
70351
+
70352
+ function dialogPresentationHandler() {
70353
+
70354
+ if (this.trackView.track.selected) {
70355
+ this.browser.dataRangeDialog.configure(this.trackView.browser.getSelectedTrackViews());
70356
+ } else {
70357
+ this.browser.dataRangeDialog.configure(this.trackView);
70358
+ }
70359
+ this.browser.dataRangeDialog.present($$1(this.browser.columnContainer));
70360
+ }
70361
+
70362
+ menuItems.push({object, dialog: dialogPresentationHandler});
70363
+
70364
+ return menuItems
70365
+ }
70366
+
70367
+ setDataRange({min, max}) {
70368
+ this.dataRange.min = min;
70369
+ this.dataRange.max = max;
70370
+ this.colorScale.min = min;
70371
+ this.colorScale.max = max;
70372
+ this.trackView.repaintViews();
70373
+ }
70374
+
70375
+ hasSamples() {
70376
+ return true // by definition
70377
+ }
70378
+
70379
+ getSamples() {
70380
+ return {
70381
+ names: this.sampleKeys,
70382
+ height: this.rowHeight,
70383
+ yOffset: 0
70384
+ }
70385
+ }
70386
+
70387
+ async getFeatures(chr, start, end, bpPerPixel) {
70388
+ const visibilityWindow = this.visibilityWindow;
70389
+ return this.featureSource.getFeatures({chr, start, end, bpPerPixel, visibilityWindow})
70390
+ }
70391
+
70392
+
70393
+ draw({context, pixelTop, pixelWidth, pixelHeight, features, bpPerPixel, bpStart}) {
70394
+
70395
+
70396
+ IGVGraphics.fillRect(context, 0, pixelTop, pixelWidth, pixelHeight, {'fillStyle': "rgb(255, 255, 255)"});
70397
+
70398
+ if (features && features.length > 0) {
70399
+
70400
+ const rowHeight = this.rowHeight;
70401
+ const pixelBottom = pixelTop + pixelHeight;
70402
+ const bpEnd = bpStart + pixelWidth * bpPerPixel + 1;
70403
+
70404
+ const h = rowHeight;
70405
+
70406
+ for (let f of features) {
70407
+
70408
+ // Test for overlap with in-view region
70409
+ if (f.end < bpStart || f.start > bpEnd) continue
70410
+
70411
+ // Pixel x values
70412
+ const xLeft = Math.round((f.start - bpStart) / bpPerPixel);
70413
+ const xRight = Math.round((f.end - bpStart) / bpPerPixel);
70414
+ const w = Math.max(1, xRight - xLeft);
70415
+
70416
+ // Loop through value array
70417
+ let row = 0;
70418
+
70419
+ for (let i = f.values.length - 1; i >= 0; i--) {
70420
+
70421
+ const v = f.values[i];
70422
+
70423
+ const y = row * rowHeight;
70424
+
70425
+
70426
+ const bottom = y + rowHeight;
70427
+
70428
+ if (bottom < pixelTop || y > pixelBottom) {
70429
+ continue
70430
+ }
70431
+
70432
+ const color = this.colorScale.getColor(v);
70433
+
70434
+ context.fillStyle = color;
70435
+
70436
+ context.fillRect(xLeft, y, w, h);
70437
+
70438
+ row++;
70439
+ }
70440
+ }
70441
+ }
70442
+
70443
+ }
70444
+
70445
+ /**
70446
+ * Optional method to compute pixel height to accomodate the list of features.
70447
+ *
70448
+ * @param features
70449
+ * @returns {number}
70450
+ */
70451
+ computePixelHeight(features) {
70452
+ if (!features || features.length === 0) return 0
70453
+ return features[0].values.length * this.rowHeight
70454
+ }
70455
+
70456
+
70457
+ clickedFeatures(clickState) {
70458
+
70459
+ const allFeatures = super.clickedFeatures(clickState);
70460
+ const y = clickState.y;
70461
+ return allFeatures.filter(function (feature) {
70462
+ const rect = feature.pixelRect;
70463
+ return rect && y >= rect.y && y <= (rect.y + rect.h)
70464
+ })
70465
+
70466
+ }
70467
+
70468
+ hoverText(clickState) {
70469
+ const features = this.clickedFeatures(clickState);
70470
+ if (features && features.length > 0) {
70471
+ return `${features[0].sample}: ${features[0].value}`
70472
+ }
70473
+ }
70474
+
70475
+ popupData(clickState, featureList) {
70476
+
70477
+ if (featureList === undefined) featureList = this.clickedFeatures(clickState);
70478
+
70479
+ const items = [];
70480
+
70481
+ for (let feature of featureList) {
70482
+
70483
+ // Double line divider between features
70484
+ if (items.length > 0) {
70485
+ items.push('<hr/>');
70486
+ items.push('<hr/>');
70487
+ }
70488
+
70489
+ // hack for whole genome features, which save the original feature as "_f"
70490
+ const f = feature._f || feature;
70491
+
70492
+ const data = (typeof f.popupData === 'function') ?
70493
+ f.popupData(this.type, this.browser.genome.id) :
70494
+ this.extractPopupData(f);
70495
+ Array.prototype.push.apply(items, data);
70496
+
70497
+ }
70498
+
70499
+ return items
70500
+ }
70501
+
70502
+ get supportsWholeGenome() {
70503
+ return false
70504
+ }
70505
+
70506
+ getState() {
70507
+
70508
+ const config = super.getState();
70509
+ config.colorScale = this.colorScale.toJson();
70510
+ return config
70511
+
70512
+ }
70513
+
70514
+ }
70515
+
70206
70516
  //import CNVPytorTrack from "./CNVpytor/cnvpytorTrack.js"
70207
70517
 
70208
70518
 
@@ -70214,7 +70524,7 @@ const trackFunctions =
70214
70524
  ['seg', (config, browser) => new SegTrack(config, browser)],
70215
70525
  ['mut', (config, browser) => new SegTrack(config, browser)],
70216
70526
  ['maf', (config, browser) => new SegTrack(config, browser)],
70217
- ['shoebox', (config, browser) => new SegTrack(config, browser)],
70527
+ ['shoebox', (config, browser) => new ShoeboxTrack(config, browser)],
70218
70528
  ['wig', (config, browser) => new WigTrack(config, browser)],
70219
70529
  ['merged', (config, browser) => new MergedTrack(config, browser)],
70220
70530
  ['alignment', (config, browser) => new BAMTrack(config, browser)],
@@ -70706,94 +71016,11 @@ function createReferenceFrameList(loci, genome, browserFlanking, minimumBases, v
70706
71016
  })
70707
71017
  }
70708
71018
 
70709
- const _version = "3.0.8";
71019
+ const _version = "3.0.9";
70710
71020
  function version() {
70711
71021
  return _version
70712
71022
  }
70713
71023
 
70714
- /*
70715
- * The MIT License (MIT)
70716
- *
70717
- * Copyright (c) 2014 Broad Institute
70718
- *
70719
- * Permission is hereby granted, free of charge, to any person obtaining a copy
70720
- * of this software and associated documentation files (the "Software"), to deal
70721
- * in the Software without restriction, including without limitation the rights
70722
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
70723
- * copies of the Software, and to permit persons to whom the Software is
70724
- * furnished to do so, subject to the following conditions:
70725
- *
70726
- * The above copyright notice and this permission notice shall be included in
70727
- * all copies or substantial portions of the Software.
70728
- *
70729
- *
70730
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
70731
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
70732
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
70733
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
70734
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
70735
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
70736
- * THE SOFTWARE.
70737
- */
70738
-
70739
- const navbarResponsiveClasses = {};
70740
-
70741
- const responsiveThreshold = 8;
70742
- let textButtonContainerWidth = undefined;
70743
-
70744
- function navbarDidResize(browser, width) {
70745
-
70746
- const currentClass = NavbarButton.currentNavbarButtonClass(browser);
70747
- if ('igv-navbar-text-button' === currentClass) {
70748
- textButtonContainerWidth = browser.$navigation.get(0).querySelector('.igv-navbar-right-container').getBoundingClientRect().width;
70749
- }
70750
-
70751
- const responsiveClasses = getResponsiveClasses(browser, width);
70752
-
70753
- $$1(browser.zoomWidget.zoomContainer).removeClass();
70754
- $$1(browser.zoomWidget.zoomContainer).addClass(responsiveClasses.zoomContainer);
70755
-
70756
- browser.fireEvent('navbar-resize', [ responsiveClasses.navbarButton ]);
70757
- }
70758
-
70759
- function getResponsiveClasses(browser, navbarWidth) {
70760
-
70761
- const isWGV =
70762
- (browser.isMultiLocusWholeGenomeView()) ||
70763
- (browser.referenceFrameList && GenomeUtils.isWholeGenomeView(browser.referenceFrameList[0].chr));
70764
-
70765
- isWGV ? browser.windowSizePanel.hide() : browser.windowSizePanel.show();
70766
-
70767
- const { x: leftContainerX, width: leftContainerWidth } = browser.$navigation.get(0).querySelector('.igv-navbar-left-container').getBoundingClientRect();
70768
- const leftContainerExtent = leftContainerX + leftContainerWidth;
70769
- const { x:rightContainerX} = browser.$navigation.get(0).querySelector('.igv-navbar-right-container').getBoundingClientRect();
70770
-
70771
- const delta = rightContainerX - leftContainerExtent;
70772
-
70773
- const currentClass = NavbarButton.currentNavbarButtonClass(browser);
70774
-
70775
- // console.log(`Current class ${ currentClass } Delta: ${ StringUtils.numberFormatter(Math.floor(delta))}`)
70776
-
70777
- if ('igv-navbar-text-button' === currentClass && delta < responsiveThreshold) {
70778
- navbarResponsiveClasses.navbarButton = 'igv-navbar-icon-button';
70779
- } else if (textButtonContainerWidth && 'igv-navbar-icon-button' === currentClass) {
70780
- const length = navbarWidth - leftContainerExtent;
70781
- if (length - textButtonContainerWidth > responsiveThreshold) {
70782
- navbarResponsiveClasses.navbarButton = 'igv-navbar-text-button';
70783
- }
70784
-
70785
- }
70786
-
70787
-
70788
- if (isWGV) {
70789
- navbarResponsiveClasses.zoomContainer = 'igv-zoom-widget-hidden';
70790
- } else {
70791
- navbarResponsiveClasses.zoomContainer = navbarWidth > 860 ? 'igv-zoom-widget' : 'igv-zoom-widget-900';
70792
- }
70793
-
70794
- return navbarResponsiveClasses
70795
- }
70796
-
70797
71024
  /*
70798
71025
  * The MIT License (MIT)
70799
71026
  *
@@ -70841,7 +71068,7 @@ class ChromosomeSelectWidget {
70841
71068
  });
70842
71069
 
70843
71070
  this.showAllChromosomes = browser.config.showAllChromosomes !== false; // i.e. default to true
70844
-
71071
+ this.genome = browser.genome;
70845
71072
  }
70846
71073
 
70847
71074
  show() {
@@ -70852,10 +71079,16 @@ class ChromosomeSelectWidget {
70852
71079
  this.container.style.display = 'none';
70853
71080
  }
70854
71081
 
71082
+ setValue(chrName) {
71083
+ this.select.value = this.genome.getChromosomeDisplayName(chrName);
71084
+ }
71085
+
70855
71086
  update(genome) {
70856
71087
 
71088
+ this.genome = genome;
71089
+
70857
71090
  // Start with explicit chromosome name list
70858
- const list = genome.wgChromosomeNames || [];
71091
+ const list = genome.wgChromosomeNames.map(nm => genome.getChromosomeDisplayName(nm)) || [];
70859
71092
 
70860
71093
  if (this.showAllChromosomes && genome.chromosomeNames.length > 1) {
70861
71094
  const exclude = new Set(list);
@@ -70866,6 +71099,7 @@ class ChromosomeSelectWidget {
70866
71099
  break
70867
71100
  }
70868
71101
  if (!exclude.has(nm)) {
71102
+ nm = genome.getChromosomeDisplayName(nm);
70869
71103
  list.push(nm);
70870
71104
  }
70871
71105
  }
@@ -70946,6 +71180,99 @@ class WindowSizePanel {
70946
71180
  }
70947
71181
  }
70948
71182
 
71183
+ const multiSelectImage =
71184
+ `<svg width="625px" height="625px" viewBox="0 0 625 625" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
71185
+ <title>multi select</title>
71186
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
71187
+ <g id="multi-select">
71188
+ <rect id="backdrop-copy-3" stroke="#737373" stroke-width="12" fill="#FFFFFF" x="6" y="6" width="613" height="613" rx="135"></rect>
71189
+ <g id="row-copy-3" transform="translate(81, 427)" fill="#737373">
71190
+ <rect id="Rectangle" x="134" y="0" width="329" height="70"></rect>
71191
+ <rect id="Rectangle-Copy-16" stroke="#737373" stroke-width="12" x="6" y="6" width="58" height="58"></rect>
71192
+ </g>
71193
+ <g id="row-copy-2" transform="translate(82, 277)">
71194
+ <rect id="Rectangle" fill-opacity="0.33" fill="#CFCECE" x="133" y="0" width="329" height="70"></rect>
71195
+ <rect id="Rectangle-Copy-16" stroke-opacity="0.32659528" stroke="#CFCECE" stroke-width="12" x="6" y="6" width="58" height="58"></rect>
71196
+ </g>
71197
+ <g id="row-copy" transform="translate(81, 119)" fill="#737373">
71198
+ <rect id="Rectangle" x="134" y="0" width="329" height="70"></rect>
71199
+ <rect id="Rectangle-Copy-17" stroke="#737373" stroke-width="12" x="6" y="6" width="58" height="58"></rect>
71200
+ </g>
71201
+ </g>
71202
+ </g>
71203
+ </svg>`;
71204
+
71205
+ const multiSelectImageHover =
71206
+ `<svg width="625px" height="625px" viewBox="0 0 625 625" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
71207
+ <title>multi select hover</title>
71208
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
71209
+ <g id="multi-select-hover">
71210
+ <rect id="backdrop-copy-4" stroke="#737373" stroke-width="12" fill="#737373" x="6" y="6" width="613" height="613" rx="135"></rect>
71211
+ <g id="row-copy-3" transform="translate(81, 427)" fill="#FFFFFF">
71212
+ <rect id="Rectangle" x="134" y="0" width="329" height="70"></rect>
71213
+ <rect id="Rectangle-Copy-16" stroke="#FFFFFF" stroke-width="12" x="6" y="6" width="58" height="58"></rect>
71214
+ </g>
71215
+ <g id="row-copy-2" transform="translate(82, 277)">
71216
+ <rect id="Rectangle" fill-opacity="0.33" fill="#CFCECE" x="133" y="0" width="329" height="70"></rect>
71217
+ <rect id="Rectangle-Copy-16" stroke-opacity="0.33" stroke="#CFCECE" stroke-width="12" x="6" y="6" width="58" height="58"></rect>
71218
+ </g>
71219
+ <g id="row-copy" transform="translate(81, 119)" fill="#FFFFFF">
71220
+ <rect id="Rectangle" x="134" y="0" width="329" height="70"></rect>
71221
+ <rect id="Rectangle-Copy-17" stroke="#FFFFFF" stroke-width="12" x="6" y="6" width="58" height="58"></rect>
71222
+ </g>
71223
+ </g>
71224
+ </g>
71225
+ </svg>`;
71226
+
71227
+ class MultiTrackSelectButton extends NavbarButton {
71228
+
71229
+ constructor(parent, browser, navbar, enableMultiTrackSelection) {
71230
+
71231
+ super(parent, browser, 'Select Tracks', buttonLabel, multiSelectImage, multiSelectImageHover, false);
71232
+
71233
+ this.navbar = navbar;
71234
+ this.enableMultiTrackSelection = false; // Initial state
71235
+ this.button.addEventListener('mouseenter', event => {
71236
+ if (false === enableMultiTrackSelection) {
71237
+ this.setState(true);
71238
+ }
71239
+ });
71240
+
71241
+ this.button.addEventListener('mouseleave', event => {
71242
+ if (false === enableMultiTrackSelection) {
71243
+ this.setState(false);
71244
+ }
71245
+ });
71246
+
71247
+ const mouseClickHandler = () => {
71248
+ // Toggle the selection state
71249
+ this.setMultiTrackSelection(!this.enableMultiTrackSelection);
71250
+ };
71251
+
71252
+ this.boundMouseClickHandler = mouseClickHandler.bind(this);
71253
+
71254
+ this.button.addEventListener('click', this.boundMouseClickHandler);
71255
+
71256
+ }
71257
+
71258
+ setMultiTrackSelection(enableMultiTrackSelection) {
71259
+
71260
+ this.enableMultiTrackSelection = enableMultiTrackSelection;
71261
+ this.setState(this.enableMultiTrackSelection);
71262
+
71263
+ // If enableMultiTrackSelection is false hide the Overly button
71264
+ if (false === this.enableMultiTrackSelection) {
71265
+ this.navbar.overlayTrackButton.setVisibility(false);
71266
+ }
71267
+
71268
+ for (const trackView of this.browser.trackViews) {
71269
+ trackView.enableTrackSelection(enableMultiTrackSelection);
71270
+ }
71271
+
71272
+ }
71273
+
71274
+ }
71275
+
70949
71276
  class CursorGuide {
70950
71277
 
70951
71278
  constructor(columnContainer, browser) {
@@ -71127,9 +71454,9 @@ const cursorImageHover =
71127
71454
 
71128
71455
  class CursorGuideButton extends NavbarButton {
71129
71456
 
71130
- constructor(browser, parent) {
71457
+ constructor(parent, browser) {
71131
71458
 
71132
- super(browser, parent, 'Crosshairs', buttonLabel, cursorImage, cursorImageHover, browser.doShowCursorGuide);
71459
+ super(parent, browser, 'Crosshairs', buttonLabel, cursorImage, cursorImageHover, browser.doShowCursorGuide);
71133
71460
 
71134
71461
  this.button.addEventListener('mouseenter', () => {
71135
71462
  if (false === browser.doShowCursorGuide) {
@@ -71215,9 +71542,9 @@ const centerlineImageHover =
71215
71542
 
71216
71543
  class CenterLineButton extends NavbarButton {
71217
71544
 
71218
- constructor(browser, parent) {
71545
+ constructor(parent, browser) {
71219
71546
 
71220
- super(browser, parent, 'Center Line', buttonLabel, centerlineImage, centerlineImageHover, browser.config.showCenterGuide);
71547
+ super(parent, browser, 'Center Line', buttonLabel, centerlineImage, centerlineImageHover, browser.config.showCenterGuide);
71221
71548
 
71222
71549
  this.button.addEventListener('mouseenter', () => {
71223
71550
  if (false === browser.doShowCenterLine) {
@@ -71306,7 +71633,7 @@ class TrackLabelControl extends NavbarButton {
71306
71633
 
71307
71634
  constructor(parent, browser) {
71308
71635
 
71309
- super(browser, parent, 'Track Labels', buttonLabel, trackLabelsImage, trackLabelsImageHover, browser.config.showTrackLabels);
71636
+ super(parent, browser, 'Track Labels', buttonLabel, trackLabelsImage, trackLabelsImageHover, browser.config.showTrackLabels);
71310
71637
 
71311
71638
  this.button.addEventListener('mouseenter', () => {
71312
71639
  if (false === browser.doShowTrackLabels) {
@@ -71336,28 +71663,28 @@ class TrackLabelControl extends NavbarButton {
71336
71663
 
71337
71664
  }
71338
71665
 
71339
- const sampleNameImage =
71666
+ const roiImage =
71340
71667
  `<svg width="625px" height="625px" viewBox="0 0 625 625" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
71341
- <title>sample names</title>
71668
+ <title>roi</title>
71342
71669
  <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
71343
- <g id="sample-names" stroke="#737373">
71344
- <rect id="Rectangle-Copy-13" stroke-width="12" fill="#FFFFFF" x="6" y="6" width="613" height="613" rx="135"></rect>
71345
- <line x1="80" y1="465" x2="541" y2="464.5" id="Line-3-Copy-3" stroke-width="32"></line>
71346
- <line x1="80" y1="312.5" x2="542" y2="313" id="Line-3" stroke-width="32"></line>
71347
- <line x1="80" y1="158" x2="541" y2="158" id="Line-3-Copy" stroke-width="32"></line>
71670
+ <g id="roi">
71671
+ <rect id="Rectangle-Copy-23" stroke="#737373" stroke-width="12" fill="#FFFFFF" x="6" y="6" width="613" height="613" rx="135"></rect>
71672
+ <text id="ROI" font-family="HelveticaNeue-Bold, Helvetica Neue" font-size="258" font-weight="bold" fill="#737373">
71673
+ <tspan x="81.445" y="389">ROI</tspan>
71674
+ </text>
71348
71675
  </g>
71349
71676
  </g>
71350
71677
  </svg>`;
71351
71678
 
71352
- const sampleNameImageHover =
71679
+ const roiImageHover =
71353
71680
  `<svg width="625px" height="625px" viewBox="0 0 625 625" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
71354
- <title>sample names hover</title>
71681
+ <title>roi hover</title>
71355
71682
  <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
71356
- <g id="sample-names-hover">
71357
- <rect id="Rectangle-Copy-18" stroke="#737373" stroke-width="12" fill="#737373" x="6" y="6" width="613" height="613" rx="135"></rect>
71358
- <line x1="80" y1="465" x2="541" y2="464.5" id="Line-3-Copy-3" stroke="#FFFFFF" stroke-width="32" fill="#FFFFFF"></line>
71359
- <line x1="80" y1="312.5" x2="542" y2="313" id="Line-3" stroke="#FFFFFF" stroke-width="32" fill="#FFFFFF"></line>
71360
- <line x1="80" y1="158" x2="541" y2="158" id="Line-3-Copy" stroke="#FFFFFF" stroke-width="32" fill="#FFFFFF"></line>
71683
+ <g id="roi-hover">
71684
+ <rect id="Rectangle-Copy-24" stroke="#737373" stroke-width="12" fill="#737373" x="6" y="6" width="613" height="613" rx="135"></rect>
71685
+ <text id="ROI" font-family="HelveticaNeue-Bold, Helvetica Neue" font-size="258" font-weight="bold" fill="#FFFFFF">
71686
+ <tspan x="81.445" y="389">ROI</tspan>
71687
+ </text>
71361
71688
  </g>
71362
71689
  </g>
71363
71690
  </svg>`;
@@ -71388,49 +71715,34 @@ const sampleNameImageHover =
71388
71715
  * THE SOFTWARE.
71389
71716
  */
71390
71717
 
71391
- class SampleNameControl extends NavbarButton {
71718
+ class ROITableControl extends NavbarButton {
71392
71719
 
71393
- constructor(parent, browser) {
71720
+ constructor(parent, browser) {
71394
71721
 
71395
- super(browser, parent, 'Sample Names', sampleNameButtonLabel, sampleNameImage, sampleNameImageHover, browser.config.showSampleNames);
71722
+ super(parent, browser, ['ROI', 'Regions of Interest Table'], buttonLabel, roiImage, roiImageHover, false);
71396
71723
 
71397
71724
  this.button.addEventListener('mouseenter', () => {
71398
- if (false === browser.showSampleNames) {
71725
+ if (false === browser.doShowROITable) {
71399
71726
  this.setState(true);
71400
71727
  }
71401
71728
  });
71402
71729
 
71403
71730
  this.button.addEventListener('mouseleave', () => {
71404
- if (false === browser.showSampleNames) {
71731
+ if (false === browser.doShowROITable) {
71405
71732
  this.setState(false);
71406
71733
  }
71407
71734
  });
71408
71735
 
71409
- this.button.addEventListener('click', () => {
71410
- this.performClickWithState(browser, undefined);
71411
- });
71736
+ this.button.addEventListener('click', () => this.buttonHandler(!browser.doShowROITable));
71412
71737
 
71413
- if (true === browser.config.showSampleNameButton) {
71414
- this.show();
71415
- } else {
71416
- this.hide();
71417
- }
71738
+ this.setVisibility(false); // Hide initially, it will be un-hidden if ROIs are loaded
71418
71739
 
71419
71740
  }
71420
71741
 
71421
- performClickWithState(browser, doShowSampleNamesOrUndefined) {
71422
-
71423
- browser.showSampleNames = undefined === doShowSampleNamesOrUndefined ? !browser.showSampleNames : doShowSampleNamesOrUndefined;
71424
-
71425
- const column = browser.columnContainer.querySelector('.igv-sample-name-column');
71426
- column.style.display = false === browser.showSampleNames ? 'none' : 'flex';
71427
-
71428
- this.setState(browser.showSampleNames);
71429
-
71430
- browser.layoutChange();
71431
-
71742
+ buttonHandler(status) {
71743
+ this.setState(status);
71744
+ this.browser.setROITableVisibility(status);
71432
71745
  }
71433
-
71434
71746
  }
71435
71747
 
71436
71748
  const sampleInfoImage =
@@ -71534,7 +71846,7 @@ class SampleInfoControl extends NavbarButton {
71534
71846
 
71535
71847
  constructor(parent, browser) {
71536
71848
 
71537
- super(browser, parent, 'Sample Info', buttonLabel, sampleInfoImage, sampleInfoImageHover, false);
71849
+ super(parent, browser, 'Sample Info', buttonLabel, sampleInfoImage, sampleInfoImageHover, false);
71538
71850
 
71539
71851
  this.showSampleInfo = false;
71540
71852
 
@@ -71584,141 +71896,102 @@ class SampleInfoControl extends NavbarButton {
71584
71896
 
71585
71897
  }
71586
71898
 
71587
- const sliderMin = 0;
71588
- let sliderMax = 23;
71589
- let sliderValueRaw = 0;
71590
-
71591
- const ZoomWidget = function (browser, parent) {
71592
-
71593
- this.browser = browser;
71594
-
71595
- this.zoomContainer = div({class: 'igv-zoom-widget'});
71596
- parent.appendChild(this.zoomContainer);
71597
-
71598
- // zoom out
71599
- this.zoomOutButton = div();
71600
- this.zoomContainer.appendChild(this.zoomOutButton);
71601
- this.zoomOutButton.appendChild(createIcon('minus-circle'));
71602
- this.zoomOutButton.addEventListener('click', () => {
71603
- // browser.zoomWithScaleFactor(2.0)
71604
- browser.zoomOut();
71605
- });
71606
-
71607
- // Range slider
71608
- const el = div();
71609
- this.zoomContainer.appendChild(el);
71610
- this.slider = document.createElement('input');
71611
- this.slider.type = 'range';
71612
-
71613
- this.slider.min = `${sliderMin}`;
71614
- this.slider.max = `${sliderMax}`;
71615
-
71616
- el.appendChild(this.slider);
71617
-
71618
- this.slider.addEventListener('change', e => {
71619
-
71620
- e.preventDefault();
71621
- e.stopPropagation();
71622
-
71623
- const referenceFrame = browser.referenceFrameList[0];
71624
- const {bpLength} = referenceFrame.genome.getChromosome(referenceFrame.chr);
71625
- const {end, start} = referenceFrame;
71899
+ const sampleNameImage =
71900
+ `<svg width="625px" height="625px" viewBox="0 0 625 625" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
71901
+ <title>sample names</title>
71902
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
71903
+ <g id="sample-names" stroke="#737373">
71904
+ <rect id="Rectangle-Copy-13" stroke-width="12" fill="#FFFFFF" x="6" y="6" width="613" height="613" rx="135"></rect>
71905
+ <line x1="80" y1="465" x2="541" y2="464.5" id="Line-3-Copy-3" stroke-width="32"></line>
71906
+ <line x1="80" y1="312.5" x2="542" y2="313" id="Line-3" stroke-width="32"></line>
71907
+ <line x1="80" y1="158" x2="541" y2="158" id="Line-3-Copy" stroke-width="32"></line>
71908
+ </g>
71909
+ </g>
71910
+ </svg>`;
71626
71911
 
71627
- const extent = end - start;
71912
+ const sampleNameImageHover =
71913
+ `<svg width="625px" height="625px" viewBox="0 0 625 625" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
71914
+ <title>sample names hover</title>
71915
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
71916
+ <g id="sample-names-hover">
71917
+ <rect id="Rectangle-Copy-18" stroke="#737373" stroke-width="12" fill="#737373" x="6" y="6" width="613" height="613" rx="135"></rect>
71918
+ <line x1="80" y1="465" x2="541" y2="464.5" id="Line-3-Copy-3" stroke="#FFFFFF" stroke-width="32" fill="#FFFFFF"></line>
71919
+ <line x1="80" y1="312.5" x2="542" y2="313" id="Line-3" stroke="#FFFFFF" stroke-width="32" fill="#FFFFFF"></line>
71920
+ <line x1="80" y1="158" x2="541" y2="158" id="Line-3-Copy" stroke="#FFFFFF" stroke-width="32" fill="#FFFFFF"></line>
71921
+ </g>
71922
+ </g>
71923
+ </svg>`;
71628
71924
 
71629
- // bpLength/(end - start)
71630
- const scaleFactor = Math.pow(2, e.target.valueAsNumber);
71925
+ /*
71926
+ * The MIT License (MIT)
71927
+ *
71928
+ * Copyright (c) 2016 University of California San Diego
71929
+ * Author: Jim Robinson
71930
+ *
71931
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
71932
+ * of this software and associated documentation files (the "Software"), to deal
71933
+ * in the Software without restriction, including without limitation the rights
71934
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
71935
+ * copies of the Software, and to permit persons to whom the Software is
71936
+ * furnished to do so, subject to the following conditions:
71937
+ *
71938
+ * The above copyright notice and this permission notice shall be included in
71939
+ * all copies or substantial portions of the Software.
71940
+ *
71941
+ *
71942
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
71943
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
71944
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
71945
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
71946
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
71947
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
71948
+ * THE SOFTWARE.
71949
+ */
71631
71950
 
71632
- // (end - start) = bpLength/scaleFactor
71633
- const zoomedExtent = bpLength / scaleFactor;
71951
+ class SampleNameControl extends NavbarButton {
71634
71952
 
71635
- // console.log(`zoom-widget - slider ${ e.target.value } scaleFactor ${ scaleFactor } extent-zoomed ${ StringUtils.numberFormatter(Math.round(zoomedExtent)) }`)
71953
+ constructor(parent, browser) {
71636
71954
 
71637
- browser.zoomWithScaleFactor(zoomedExtent / extent);
71955
+ super(parent, browser, 'Sample Names', sampleNameButtonLabel, sampleNameImage, sampleNameImageHover, browser.config.showSampleNames);
71638
71956
 
71639
- });
71957
+ this.button.addEventListener('mouseenter', () => {
71958
+ if (false === browser.showSampleNames) {
71959
+ this.setState(true);
71960
+ }
71961
+ });
71640
71962
 
71641
- // zoom in
71642
- this.zoomInButton = div();
71643
- this.zoomContainer.appendChild(this.zoomInButton);
71644
- this.zoomInButton.appendChild(createIcon('plus-circle'));
71645
- this.zoomInButton.addEventListener('click', () => {
71646
- // browser.zoomWithScaleFactor(0.5)
71647
- browser.zoomIn();
71648
- });
71963
+ this.button.addEventListener('mouseleave', () => {
71964
+ if (false === browser.showSampleNames) {
71965
+ this.setState(false);
71966
+ }
71967
+ });
71649
71968
 
71650
- browser.on('locuschange', (referenceFrameList) => {
71969
+ this.button.addEventListener('click', () => {
71970
+ this.performClickWithState(browser, undefined);
71971
+ });
71651
71972
 
71652
- if (this.browser.isMultiLocusMode()) {
71653
- this.disable();
71973
+ if (true === browser.config.showSampleNameButton) {
71974
+ this.show();
71654
71975
  } else {
71655
- this.enable();
71656
- this.update(referenceFrameList);
71976
+ this.hide();
71657
71977
  }
71658
71978
 
71659
- });
71660
-
71661
- };
71662
-
71663
- ZoomWidget.prototype.update = function (referenceFrameList) {
71664
-
71665
- const referenceFrame = referenceFrameList[0];
71666
- const {bpLength} = referenceFrame.genome.getChromosome(referenceFrame.chr);
71667
- const {start, end} = referenceFrame;
71668
-
71669
- sliderMax = Math.ceil(Math.log2(bpLength / this.browser.minimumBases()));
71670
-
71671
- this.slider.max = `${sliderMax}`;
71672
-
71673
- const scaleFactor = bpLength / (end - start);
71674
- sliderValueRaw = Math.log2(scaleFactor);
71675
- this.slider.value = `${Math.round(sliderValueRaw)}`;
71676
-
71677
- // referenceFrame.description('zoom.update')
71678
-
71679
- // console.log(`${ Date.now() } update - slider ${ this.slider.value } scaleFactor ${ Math.round(scaleFactor) } extent ${ StringUtils.numberFormatter(Math.round(extent)) }`)
71680
-
71681
- // console.log(`update - sliderMin ${ sliderMin } sliderValue ${ this.slider.value } sliderMax ${ sliderMax } scaleFactor ${ scaleFactor.toFixed(3) } derived-scaleFactor ${ derivedScalefactor.toFixed(3) }`)
71682
-
71683
- };
71684
-
71685
- ZoomWidget.prototype.enable = function () {
71686
-
71687
- // this.zoomInButton.style.color = appleCrayonPalette[ 'steel' ];
71688
- // this.zoomInButton.style.pointerEvents = 'auto';
71689
- //
71690
- // this.zoomOutButton.style.color = appleCrayonPalette[ 'steel' ];
71691
- // this.zoomOutButton.style.pointerEvents = 'auto';
71692
-
71693
- this.slider.disabled = false;
71694
- };
71979
+ }
71695
71980
 
71696
- ZoomWidget.prototype.disable = function () {
71981
+ performClickWithState(browser, doShowSampleNamesOrUndefined) {
71697
71982
 
71698
- // this.zoomInButton.style.color = appleCrayonPalette[ 'silver' ];
71699
- // this.zoomInButton.style.pointerEvents = 'none';
71700
- //
71701
- // this.zoomOutButton.style.color = appleCrayonPalette[ 'silver' ];
71702
- // this.zoomOutButton.style.pointerEvents = 'none';
71983
+ browser.showSampleNames = undefined === doShowSampleNamesOrUndefined ? !browser.showSampleNames : doShowSampleNamesOrUndefined;
71703
71984
 
71704
- this.slider.disabled = true;
71705
- };
71985
+ const column = browser.columnContainer.querySelector('.igv-sample-name-column');
71986
+ column.style.display = false === browser.showSampleNames ? 'none' : 'flex';
71706
71987
 
71707
- ZoomWidget.prototype.hide = function () {
71708
- this.zoomContainer.style.display = 'none';
71709
- };
71988
+ this.setState(browser.showSampleNames);
71710
71989
 
71711
- ZoomWidget.prototype.show = function () {
71712
- this.zoomContainer.style.display = 'block';
71713
- };
71990
+ browser.layoutChange();
71714
71991
 
71715
- ZoomWidget.prototype.hideSlider = function () {
71716
- this.slider.style.display = 'none';
71717
- };
71992
+ }
71718
71993
 
71719
- ZoomWidget.prototype.showSlider = function () {
71720
- this.slider.style.display = 'block';
71721
- };
71994
+ }
71722
71995
 
71723
71996
  class Dropdown {
71724
71997
  constructor(parent, shim) {
@@ -71861,7 +72134,7 @@ const imageSaveImageHoverSVG =
71861
72134
  class SaveImageControl extends NavbarButton {
71862
72135
  constructor(parent, browser) {
71863
72136
 
71864
- super(browser, parent, 'Save Image', buttonLabel, imageSaveImageSVG, imageSaveImageHoverSVG, false);
72137
+ super(parent, browser, 'Save Image', buttonLabel, imageSaveImageSVG, imageSaveImageHoverSVG, false);
71865
72138
 
71866
72139
  this.button.addEventListener('mouseenter', () => this.setState(true));
71867
72140
 
@@ -71927,6 +72200,422 @@ class SaveImageControl extends NavbarButton {
71927
72200
 
71928
72201
  }
71929
72202
 
72203
+ /**
72204
+ * User supplied button for the navbar
72205
+ */
72206
+
72207
+ const CustomButton = function (parent, browser, b) {
72208
+
72209
+ const button = div({class: 'igv-navbar-button'});
72210
+ parent.append(button);
72211
+ button.textContent = b.label;
72212
+ button.addEventListener('click', () => b.callback(browser));
72213
+ };
72214
+
72215
+ const sliderMin = 0;
72216
+ let sliderMax = 23;
72217
+ let sliderValueRaw = 0;
72218
+
72219
+ class ZoomWidget {
72220
+ constructor(config, browser, parent) {
72221
+
72222
+ this.browser = browser;
72223
+
72224
+ this.zoomContainer = div({class: 'igv-zoom-widget'});
72225
+ parent.appendChild(this.zoomContainer);
72226
+
72227
+ // zoom out
72228
+ this.zoomOutButton = div();
72229
+ this.zoomContainer.appendChild(this.zoomOutButton);
72230
+ this.zoomOutButton.appendChild(createIcon('minus-circle'));
72231
+ this.zoomOutButton.addEventListener('click', () => {
72232
+ // browser.zoomWithScaleFactor(2.0)
72233
+ browser.zoomOut();
72234
+ });
72235
+
72236
+ // Range slider
72237
+ const el = div();
72238
+ this.zoomContainer.appendChild(el);
72239
+ this.slider = document.createElement('input');
72240
+ this.slider.type = 'range';
72241
+
72242
+ this.slider.min = `${sliderMin}`;
72243
+ this.slider.max = `${sliderMax}`;
72244
+
72245
+ el.appendChild(this.slider);
72246
+
72247
+ this.slider.addEventListener('change', e => {
72248
+
72249
+ e.preventDefault();
72250
+ e.stopPropagation();
72251
+
72252
+ const referenceFrame = browser.referenceFrameList[0];
72253
+ const {bpLength} = referenceFrame.genome.getChromosome(referenceFrame.chr);
72254
+ const {end, start} = referenceFrame;
72255
+
72256
+ const extent = end - start;
72257
+
72258
+ // bpLength/(end - start)
72259
+ const scaleFactor = Math.pow(2, e.target.valueAsNumber);
72260
+
72261
+ // (end - start) = bpLength/scaleFactor
72262
+ const zoomedExtent = bpLength / scaleFactor;
72263
+
72264
+ // console.log(`zoom-widget - slider ${ e.target.value } scaleFactor ${ scaleFactor } extent-zoomed ${ StringUtils.numberFormatter(Math.round(zoomedExtent)) }`)
72265
+
72266
+ browser.zoomWithScaleFactor(zoomedExtent / extent);
72267
+
72268
+ });
72269
+
72270
+ // zoom in
72271
+ this.zoomInButton = div();
72272
+ this.zoomContainer.appendChild(this.zoomInButton);
72273
+ this.zoomInButton.appendChild(createIcon('plus-circle'));
72274
+ this.zoomInButton.addEventListener('click', () => {
72275
+ // browser.zoomWithScaleFactor(0.5)
72276
+ browser.zoomIn();
72277
+ });
72278
+
72279
+ browser.on('locuschange', (referenceFrameList) => {
72280
+
72281
+ if (this.browser.isMultiLocusMode()) {
72282
+ this.disable();
72283
+ } else {
72284
+ this.enable();
72285
+ this.update(referenceFrameList);
72286
+ }
72287
+
72288
+ });
72289
+
72290
+ }
72291
+
72292
+ update(referenceFrameList) {
72293
+
72294
+ if (this.slider) {
72295
+ const referenceFrame = referenceFrameList[0];
72296
+ const {bpLength} = referenceFrame.genome.getChromosome(referenceFrame.chr);
72297
+ const {start, end} = referenceFrame;
72298
+
72299
+ sliderMax = Math.ceil(Math.log2(bpLength / this.browser.minimumBases()));
72300
+ this.slider.max = `${sliderMax}`;
72301
+
72302
+ const scaleFactor = bpLength / (end - start);
72303
+ sliderValueRaw = Math.log2(scaleFactor);
72304
+ this.slider.value = `${Math.round(sliderValueRaw)}`;
72305
+ }
72306
+ }
72307
+
72308
+
72309
+ enable() {
72310
+
72311
+ // this.zoomInButton.style.color = appleCrayonPalette[ 'steel' ];
72312
+ // this.zoomInButton.style.pointerEvents = 'auto';
72313
+ //
72314
+ // this.zoomOutButton.style.color = appleCrayonPalette[ 'steel' ];
72315
+ // this.zoomOutButton.style.pointerEvents = 'auto';
72316
+
72317
+ if (this.slider) this.slider.disabled = false;
72318
+ }
72319
+
72320
+ disable() {
72321
+
72322
+ // this.zoomInButton.style.color = appleCrayonPalette[ 'silver' ];
72323
+ // this.zoomInButton.style.pointerEvents = 'none';
72324
+ //
72325
+ // this.zoomOutButton.style.color = appleCrayonPalette[ 'silver' ];
72326
+ // this.zoomOutButton.style.pointerEvents = 'none';
72327
+
72328
+ if (this.slider) this.slider.disabled = true;
72329
+ }
72330
+
72331
+ hide() {
72332
+ this.zoomContainer.style.display = 'none';
72333
+ }
72334
+
72335
+ show() {
72336
+ this.zoomContainer.style.display = 'block';
72337
+ }
72338
+
72339
+ hideSlider() {
72340
+ if (this.slider) this.slider.style.display = 'none';
72341
+ }
72342
+
72343
+ showSlider() {
72344
+ if (this.slider) this.slider.style.display = 'block';
72345
+ }
72346
+ }
72347
+
72348
+ /*
72349
+ * The MIT License (MIT)
72350
+ *
72351
+ * Copyright (c) 2014 Broad Institute
72352
+ *
72353
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
72354
+ * of this software and associated documentation files (the "Software"), to deal
72355
+ * in the Software without restriction, including without limitation the rights
72356
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
72357
+ * copies of the Software, and to permit persons to whom the Software is
72358
+ * furnished to do so, subject to the following conditions:
72359
+ *
72360
+ * The above copyright notice and this permission notice shall be included in
72361
+ * all copies or substantial portions of the Software.
72362
+ *
72363
+ *
72364
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
72365
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
72366
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
72367
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
72368
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
72369
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
72370
+ * THE SOFTWARE.
72371
+ */
72372
+
72373
+ class ResponsiveNavbar {
72374
+ constructor(config, browser) {
72375
+
72376
+ this.browser = browser;
72377
+ this.config = config;
72378
+
72379
+ this.currentClass = 'igv-navbar-text-button';
72380
+
72381
+ // DOM element for
72382
+ const $navBar = $$1('<div>', {class: 'igv-navbar'});
72383
+ this.$navigation = $navBar;
72384
+
72385
+ const $navbarLeftContainer = $$1('<div>', {class: 'igv-navbar-left-container'});
72386
+ $navBar.append($navbarLeftContainer);
72387
+ this.navbarLeftContainer = $navbarLeftContainer.get(0);
72388
+
72389
+ // IGV logo
72390
+ const $logo = $$1('<div>', {class: 'igv-logo'});
72391
+ $navbarLeftContainer.append($logo);
72392
+
72393
+ const logoSvg = logo();
72394
+ logoSvg.css("width", "34px");
72395
+ logoSvg.css("height", "32px");
72396
+ $logo.append(logoSvg);
72397
+
72398
+ this.$current_genome = $$1('<div>', {class: 'igv-current-genome'});
72399
+ $navbarLeftContainer.append(this.$current_genome);
72400
+ this.$current_genome.text('');
72401
+
72402
+ const $genomicLocation = $$1('<div>', {class: 'igv-navbar-genomic-location'});
72403
+ $navbarLeftContainer.append($genomicLocation);
72404
+
72405
+ // chromosome select widget
72406
+ this.chromosomeSelectWidget = new ChromosomeSelectWidget(browser, $genomicLocation.get(0));
72407
+ if (config.showChromosomeWidget !== false) {
72408
+ this.chromosomeSelectWidget.show();
72409
+ } else {
72410
+ this.chromosomeSelectWidget.hide();
72411
+ }
72412
+
72413
+ const $locusSizeGroup = $$1('<div>', {class: 'igv-locus-size-group'});
72414
+ $genomicLocation.append($locusSizeGroup);
72415
+
72416
+ const $searchContainer = $$1('<div>', {class: 'igv-search-container'});
72417
+ $locusSizeGroup.append($searchContainer);
72418
+
72419
+ // browser.$searchInput = $('<input type="text" placeholder="Locus Search">');
72420
+ this.$searchInput = $$1('<input>', {class: 'igv-search-input', type: 'text', placeholder: 'Locus Search'});
72421
+ $searchContainer.append(this.$searchInput);
72422
+ // Stop event propagation to prevent feature track keyboard navigation
72423
+ this.$searchInput[0].addEventListener('keyup', (event) => {
72424
+ event.stopImmediatePropagation();
72425
+ });
72426
+
72427
+ this.$searchInput.change(() => browser.doSearch(this.$searchInput.val()));
72428
+
72429
+ const searchIconContainer = div({class: 'igv-search-icon-container'});
72430
+ $searchContainer.append($$1(searchIconContainer));
72431
+ searchIconContainer.appendChild(createIcon("search"));
72432
+ searchIconContainer.addEventListener('click', () => browser.doSearch(this.$searchInput.val()));
72433
+
72434
+ this.windowSizePanel = new WindowSizePanel($locusSizeGroup.get(0), browser);
72435
+
72436
+ const $navbarRightContainer = $$1('<div>', {class: 'igv-navbar-right-container'});
72437
+ $navBar.append($navbarRightContainer);
72438
+ this.navbarRightContainer = $navbarRightContainer.get(0);
72439
+
72440
+ const $toggle_button_container = $$1('<div class="igv-navbar-toggle-button-container">');
72441
+ $navbarRightContainer.append($toggle_button_container);
72442
+ const toggleButtonContainer = $toggle_button_container.get(0);
72443
+ this.toggle_button_container = toggleButtonContainer; // TODO -- for circular view , refactor this
72444
+
72445
+ this.overlayTrackButton = new OverlayTrackButton(toggleButtonContainer, browser);
72446
+ this.overlayTrackButton.setVisibility(false);
72447
+
72448
+ const showMultiSelect = config.showMultiSelectButton !== false;
72449
+ this.multiTrackSelectButton = new MultiTrackSelectButton(toggleButtonContainer, browser, this, showMultiSelect);
72450
+
72451
+ this.cursorGuideButton = new CursorGuideButton(toggleButtonContainer, browser);
72452
+
72453
+ this.centerLineButton = new CenterLineButton(toggleButtonContainer, browser);
72454
+
72455
+ this.trackLabelControl = new TrackLabelControl(toggleButtonContainer, browser);
72456
+
72457
+ // ROI Control
72458
+ this.roiTableControl = new ROITableControl(toggleButtonContainer, browser);
72459
+
72460
+ this.sampleInfoControl = new SampleInfoControl(toggleButtonContainer, browser);
72461
+
72462
+ this.sampleNameControl = new SampleNameControl(toggleButtonContainer, browser);
72463
+
72464
+ if (true === config.showSVGButton) {
72465
+ this.saveImageControl = new SaveImageControl(toggleButtonContainer, browser);
72466
+ }
72467
+
72468
+ if (config.customButtons) {
72469
+ for (let b of config.customButtons) {
72470
+ new CustomButton(toggleButtonContainer, browser, b);
72471
+ }
72472
+ }
72473
+
72474
+ this.zoomWidget = new ZoomWidget(config, browser, $navbarRightContainer.get(0));
72475
+
72476
+ if (false === config.showNavigation) {
72477
+ this.$navigation.hide();
72478
+ }
72479
+
72480
+
72481
+
72482
+ }
72483
+
72484
+ navbarDidResize() {
72485
+
72486
+
72487
+ const navbarWidth = this.$navigation.width();
72488
+ const currentClass = this.currentNavbarButtonClass();
72489
+ if ('igv-navbar-text-button' === currentClass) {
72490
+ this.textButtonContainerWidth = this.navbarRightContainer.getBoundingClientRect().width;
72491
+ }
72492
+ const browser = this.browser;
72493
+ const isWGV =
72494
+ (browser.isMultiLocusWholeGenomeView()) ||
72495
+ (browser.referenceFrameList && GenomeUtils.isWholeGenomeView(browser.referenceFrameList[0].chr));
72496
+
72497
+ isWGV ? this.windowSizePanel.hide() : this.windowSizePanel.show();
72498
+
72499
+ const {
72500
+ x: leftContainerX,
72501
+ width: leftContainerWidth
72502
+ } = this.navbarLeftContainer.getBoundingClientRect();
72503
+ const leftContainerExtent = leftContainerX + leftContainerWidth;
72504
+ const {x: rightContainerX} = this.navbarRightContainer.getBoundingClientRect();
72505
+
72506
+ const delta = rightContainerX - leftContainerExtent;
72507
+
72508
+ let navbarButtonClass;
72509
+ const threshold = 8;
72510
+ if ('igv-navbar-text-button' === currentClass && delta < threshold) {
72511
+ navbarButtonClass = 'igv-navbar-icon-button';
72512
+ } else if (this.textButtonContainerWidth && 'igv-navbar-icon-button' === currentClass) {
72513
+ const length = navbarWidth - leftContainerExtent;
72514
+ if (length - this.textButtonContainerWidth > threshold) {
72515
+ navbarButtonClass = 'igv-navbar-text-button';
72516
+ }
72517
+ }
72518
+ // Update all the buttons (buttons are listeners)
72519
+ if(navbarButtonClass && currentClass !== navbarButtonClass) {
72520
+ this.currentClass = navbarButtonClass;
72521
+ this.browser.fireEvent('navbar-resize', [navbarButtonClass]);
72522
+ }
72523
+
72524
+ let zoomContainerClass;
72525
+ if (isWGV) {
72526
+ zoomContainerClass = 'igv-zoom-widget-hidden';
72527
+ } else {
72528
+ zoomContainerClass = navbarWidth > 860 ? 'igv-zoom-widget' : 'igv-zoom-widget-900';
72529
+ }
72530
+ $$1(this.zoomWidget.zoomContainer).removeClass();
72531
+ $$1(this.zoomWidget.zoomContainer).addClass(zoomContainerClass);
72532
+ }
72533
+
72534
+
72535
+ setCenterLineButtonVisibility(isWholeGenomeView) {
72536
+ if (isWholeGenomeView) {
72537
+ this.centerLineButton.setVisibility(!isWholeGenomeView);
72538
+ } else {
72539
+ this.centerLineButton.setVisibility(this.config.showCenterGuideButton);
72540
+ }
72541
+ }
72542
+
72543
+ setCursorGuideVisibility(doShowCursorGuide) {
72544
+ if (doShowCursorGuide) {
72545
+ this.cursorGuide.show();
72546
+ } else {
72547
+ this.cursorGuide.hide();
72548
+ }
72549
+ }
72550
+
72551
+ updateGenome(genome) {
72552
+
72553
+ let genomeLabel = (genome.id && genome.id.length < 20 ? genome.id : `${genome.id.substring(0, 8)}...${genome.id.substring(genome.id.length - 8)}`);
72554
+ this.$current_genome.text(genomeLabel);
72555
+ this.$current_genome.attr('title', genome.description);
72556
+
72557
+ // chromosome select widget -- Show this IFF its not explicitly hidden AND the genome has pre-loaded chromosomes
72558
+ const showChromosomeWidget =
72559
+ this.config.showChromosomeWidget !== false &&
72560
+ genome.showChromosomeWidget !== false &&
72561
+ genome.chromosomeNames &&
72562
+ genome.chromosomeNames.length > 1;
72563
+
72564
+ if (showChromosomeWidget) {
72565
+ this.chromosomeSelectWidget.update(genome);
72566
+ this.chromosomeSelectWidget.show();
72567
+ } else {
72568
+ this.chromosomeSelectWidget.hide();
72569
+ }
72570
+ }
72571
+
72572
+ updateLocus(loc, chrName) {
72573
+ if(this.$searchInput) {
72574
+ this.$searchInput.val(loc);
72575
+ }
72576
+ if (this.chromosomeSelectWidget) {
72577
+ this.chromosomeSelectWidget.select.value = chrName;
72578
+ }
72579
+ }
72580
+
72581
+ currentNavbarButtonClass() {
72582
+ return this.currentClass
72583
+ //const el = this.$navigation.get(0).querySelector('.igv-navbar-text-button')
72584
+ //return el ? 'igv-navbar-text-button' : 'igv-navbar-icon-button'
72585
+ }
72586
+
72587
+ setEnableTrackSelection(b) {
72588
+ this.multiTrackSelectButton.setMultiTrackSelection(b);
72589
+ }
72590
+ getEnableTrackSelection() {
72591
+ return this.multiTrackSelectButton.enableMultiTrackSelection
72592
+ }
72593
+
72594
+ hide() {
72595
+ this.$navigation.hide();
72596
+ }
72597
+
72598
+ show() {
72599
+ this.$navigation.show();
72600
+ }
72601
+
72602
+ }
72603
+
72604
+
72605
+ function logo() {
72606
+
72607
+ return $$1(
72608
+ '<svg width="690px" height="324px" viewBox="0 0 690 324" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">' +
72609
+ '<title>IGV</title>' +
72610
+ '<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">' +
72611
+ '<g id="IGV" fill="#666666">' +
72612
+ '<polygon id="Path" points="379.54574 8.00169252 455.581247 8.00169252 515.564813 188.87244 532.884012 253.529506 537.108207 253.529506 554.849825 188.87244 614.833392 8.00169252 689.60164 8.00169252 582.729511 320.722144 486.840288 320.722144"></polygon>' +
72613
+ '<path d="M261.482414,323.793286 C207.975678,323.793286 168.339046,310.552102 142.571329,284.069337 C116.803612,257.586572 103.919946,217.158702 103.919946,162.784513 C103.919946,108.410325 117.437235,67.8415913 144.472217,41.0770945 C171.507199,14.3125977 212.903894,0.930550071 268.663545,0.930550071 C283.025879,0.930550071 298.232828,1.84616386 314.284849,3.6774189 C330.33687,5.50867394 344.839793,7.97378798 357.794056,11.072835 L357.794056,68.968378 C339.48912,65.869331 323.578145,63.5450806 310.060654,61.9955571 C296.543163,60.4460336 284.574731,59.6712835 274.154998,59.6712835 C255.850062,59.6712835 240.502308,61.4320792 228.111274,64.9537236 C215.720241,68.4753679 205.793482,74.2507779 198.330701,82.2801269 C190.867919,90.309476 185.587729,100.87425 182.48997,113.974767 C179.392212,127.075284 177.843356,143.345037 177.843356,162.784513 C177.843356,181.942258 179.251407,198.000716 182.067551,210.960367 C184.883695,223.920018 189.671068,234.41436 196.429813,242.443709 C203.188559,250.473058 212.059279,256.178037 223.042241,259.558815 C234.025202,262.939594 247.683295,264.629958 264.01693,264.629958 C268.241146,264.629958 273.098922,264.489094 278.590403,264.207362 C284.081883,263.925631 289.643684,263.50304 295.275972,262.939577 L295.275972,159.826347 L361.595831,159.826347 L361.595831,308.579859 C344.698967,313.087564 327.239137,316.750019 309.215815,319.567334 C291.192494,322.38465 275.281519,323.793286 261.482414,323.793286 L261.482414,323.793286 L261.482414,323.793286 Z" id="Path"></path>;' +
72614
+ '<polygon id="Path" points="0.81355666 5.00169252 73.0472883 5.00169252 73.0472883 317.722144 0.81355666 317.722144"></polygon>' +
72615
+ '</g> </g> </svg>'
72616
+ )
72617
+ }
72618
+
71930
72619
  const viewportColumnManager =
71931
72620
  {
71932
72621
  createColumns: (columnContainer, count) => {
@@ -72138,18 +72827,6 @@ CircularViewControl.prototype.hide = function () {
72138
72827
  this.button.style.display = 'none';
72139
72828
  };
72140
72829
 
72141
- /**
72142
- * User supplied button for the navbar
72143
- */
72144
-
72145
- const CustomButton = function (parent, browser, b) {
72146
-
72147
- const button = div({class: 'igv-navbar-button'});
72148
- parent.append(button);
72149
- button.textContent = b.label;
72150
- button.addEventListener('click', () => b.callback(browser));
72151
- };
72152
-
72153
72830
  const maxSequenceSize = 1000000;
72154
72831
  const maxBlatSize = 25000;
72155
72832
 
@@ -72914,88 +73591,6 @@ class TrackROISet {
72914
73591
  }
72915
73592
  }
72916
73593
 
72917
- const roiImage =
72918
- `<svg width="625px" height="625px" viewBox="0 0 625 625" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
72919
- <title>roi</title>
72920
- <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
72921
- <g id="roi">
72922
- <rect id="Rectangle-Copy-23" stroke="#737373" stroke-width="12" fill="#FFFFFF" x="6" y="6" width="613" height="613" rx="135"></rect>
72923
- <text id="ROI" font-family="HelveticaNeue-Bold, Helvetica Neue" font-size="258" font-weight="bold" fill="#737373">
72924
- <tspan x="81.445" y="389">ROI</tspan>
72925
- </text>
72926
- </g>
72927
- </g>
72928
- </svg>`;
72929
-
72930
- const roiImageHover =
72931
- `<svg width="625px" height="625px" viewBox="0 0 625 625" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
72932
- <title>roi hover</title>
72933
- <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
72934
- <g id="roi-hover">
72935
- <rect id="Rectangle-Copy-24" stroke="#737373" stroke-width="12" fill="#737373" x="6" y="6" width="613" height="613" rx="135"></rect>
72936
- <text id="ROI" font-family="HelveticaNeue-Bold, Helvetica Neue" font-size="258" font-weight="bold" fill="#FFFFFF">
72937
- <tspan x="81.445" y="389">ROI</tspan>
72938
- </text>
72939
- </g>
72940
- </g>
72941
- </svg>`;
72942
-
72943
- /*
72944
- * The MIT License (MIT)
72945
- *
72946
- * Copyright (c) 2016 University of California San Diego
72947
- * Author: Jim Robinson
72948
- *
72949
- * Permission is hereby granted, free of charge, to any person obtaining a copy
72950
- * of this software and associated documentation files (the "Software"), to deal
72951
- * in the Software without restriction, including without limitation the rights
72952
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
72953
- * copies of the Software, and to permit persons to whom the Software is
72954
- * furnished to do so, subject to the following conditions:
72955
- *
72956
- * The above copyright notice and this permission notice shall be included in
72957
- * all copies or substantial portions of the Software.
72958
- *
72959
- *
72960
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
72961
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
72962
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
72963
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
72964
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
72965
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
72966
- * THE SOFTWARE.
72967
- */
72968
-
72969
- class ROITableControl extends NavbarButton {
72970
-
72971
- constructor(parent, browser) {
72972
-
72973
- super(browser, parent, [ 'ROI', 'Regions of Interest Table' ], buttonLabel, roiImage, roiImageHover, false);
72974
-
72975
- this.button.addEventListener('mouseenter', () => {
72976
- if (false === browser.doShowROITable) {
72977
- this.setState(true);
72978
- }
72979
- });
72980
-
72981
- this.button.addEventListener('mouseleave', () => {
72982
- if (false === browser.doShowROITable) {
72983
- this.setState(false);
72984
- }
72985
- });
72986
-
72987
- this.button.addEventListener('click', () => this.buttonHandler(!browser.doShowROITable));
72988
-
72989
- this.setVisibility(false); // Hide initially, it will be un-hidden if ROIs are loaded
72990
-
72991
- }
72992
-
72993
- buttonHandler(status) {
72994
- this.setState(status);
72995
- this.browser.setROITableVisibility(status);
72996
- }
72997
- }
72998
-
72999
73594
  async function translateSession(juiceboxSession) {
73000
73595
 
73001
73596
  const jbBrowser = juiceboxSession.browsers[0];
@@ -73032,101 +73627,6 @@ async function translateSession(juiceboxSession) {
73032
73627
 
73033
73628
  }
73034
73629
 
73035
- const multiSelectImage =
73036
- `<svg width="625px" height="625px" viewBox="0 0 625 625" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
73037
- <title>multi select</title>
73038
- <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
73039
- <g id="multi-select">
73040
- <rect id="backdrop-copy-3" stroke="#737373" stroke-width="12" fill="#FFFFFF" x="6" y="6" width="613" height="613" rx="135"></rect>
73041
- <g id="row-copy-3" transform="translate(81, 427)" fill="#737373">
73042
- <rect id="Rectangle" x="134" y="0" width="329" height="70"></rect>
73043
- <rect id="Rectangle-Copy-16" stroke="#737373" stroke-width="12" x="6" y="6" width="58" height="58"></rect>
73044
- </g>
73045
- <g id="row-copy-2" transform="translate(82, 277)">
73046
- <rect id="Rectangle" fill-opacity="0.33" fill="#CFCECE" x="133" y="0" width="329" height="70"></rect>
73047
- <rect id="Rectangle-Copy-16" stroke-opacity="0.32659528" stroke="#CFCECE" stroke-width="12" x="6" y="6" width="58" height="58"></rect>
73048
- </g>
73049
- <g id="row-copy" transform="translate(81, 119)" fill="#737373">
73050
- <rect id="Rectangle" x="134" y="0" width="329" height="70"></rect>
73051
- <rect id="Rectangle-Copy-17" stroke="#737373" stroke-width="12" x="6" y="6" width="58" height="58"></rect>
73052
- </g>
73053
- </g>
73054
- </g>
73055
- </svg>`;
73056
-
73057
- const multiSelectImageHover =
73058
- `<svg width="625px" height="625px" viewBox="0 0 625 625" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
73059
- <title>multi select hover</title>
73060
- <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
73061
- <g id="multi-select-hover">
73062
- <rect id="backdrop-copy-4" stroke="#737373" stroke-width="12" fill="#737373" x="6" y="6" width="613" height="613" rx="135"></rect>
73063
- <g id="row-copy-3" transform="translate(81, 427)" fill="#FFFFFF">
73064
- <rect id="Rectangle" x="134" y="0" width="329" height="70"></rect>
73065
- <rect id="Rectangle-Copy-16" stroke="#FFFFFF" stroke-width="12" x="6" y="6" width="58" height="58"></rect>
73066
- </g>
73067
- <g id="row-copy-2" transform="translate(82, 277)">
73068
- <rect id="Rectangle" fill-opacity="0.33" fill="#CFCECE" x="133" y="0" width="329" height="70"></rect>
73069
- <rect id="Rectangle-Copy-16" stroke-opacity="0.33" stroke="#CFCECE" stroke-width="12" x="6" y="6" width="58" height="58"></rect>
73070
- </g>
73071
- <g id="row-copy" transform="translate(81, 119)" fill="#FFFFFF">
73072
- <rect id="Rectangle" x="134" y="0" width="329" height="70"></rect>
73073
- <rect id="Rectangle-Copy-17" stroke="#FFFFFF" stroke-width="12" x="6" y="6" width="58" height="58"></rect>
73074
- </g>
73075
- </g>
73076
- </g>
73077
- </svg>`;
73078
-
73079
- class MultiTrackSelectButton extends NavbarButton {
73080
-
73081
- constructor(browser, parent, enableMultiTrackSelection) {
73082
-
73083
- super(browser, parent, 'Select Tracks', buttonLabel, multiSelectImage, multiSelectImageHover, enableMultiTrackSelection = false);
73084
- this.enableMultiTrackSelection = enableMultiTrackSelection;
73085
- this.button.addEventListener('mouseenter', event => {
73086
- if (false === enableMultiTrackSelection) {
73087
- this.setState(true);
73088
- }
73089
- });
73090
-
73091
- this.button.addEventListener('mouseleave', event => {
73092
- if (false === enableMultiTrackSelection) {
73093
- this.setState(false);
73094
- }
73095
- });
73096
-
73097
- const mouseClickHandler = () => {
73098
- this.setMultiTrackSelection(!this.enableMultiTrackSelection);
73099
- };
73100
-
73101
- this.boundMouseClickHandler = mouseClickHandler.bind(this);
73102
-
73103
- this.button.addEventListener('click', this.boundMouseClickHandler);
73104
-
73105
- this.setVisibility(true);
73106
-
73107
- }
73108
-
73109
- setMultiTrackSelection(enableMultiTrackSelection) {
73110
- this.enableMultiTrackSelection = enableMultiTrackSelection;
73111
- for (const trackView of this.browser.trackViews) {
73112
- if (false === multiTrackSelectExclusionTypes.has(trackView.track.type)) {
73113
- trackView.setTrackSelectionState(trackView.axis, this.enableMultiTrackSelection);
73114
-
73115
- // If closing the selection boxes set track selected property to false
73116
- if (!this.enableMultiTrackSelection) {
73117
- trackView.track.selected = false;
73118
- }
73119
- }
73120
- }
73121
- this.setState(this.enableMultiTrackSelection);
73122
-
73123
- // If enableMultiTrackSelection is false hide Overlay button
73124
- if (false === this.enableMultiTrackSelection) {
73125
- this.browser.overlayTrackButton.setVisibility(false);
73126
- }
73127
- }
73128
- }
73129
-
73130
73630
  /**
73131
73631
  * Chromosome alias source backed by a UCSC bigbed file
73132
73632
  *
@@ -73146,6 +73646,13 @@ class ChromAliasBB {
73146
73646
  this.reader = new BWReader(config, genome);
73147
73647
  }
73148
73648
 
73649
+ async preload(chrNames) {
73650
+ await this.reader.preload();
73651
+ for(let nm of chrNames) {
73652
+ await this.search(nm);
73653
+ }
73654
+ }
73655
+
73149
73656
  /**
73150
73657
  * Return the cached canonical chromosome name for the alias. If none found return the alias.
73151
73658
  *
@@ -73221,6 +73728,10 @@ class ChromAliasFile {
73221
73728
  this.genome = genome;
73222
73729
  }
73223
73730
 
73731
+ async preload() {
73732
+ return this.loadAliases();
73733
+ }
73734
+
73224
73735
  /**
73225
73736
  * Return the canonical chromosome name for the alias. If none found return the alias
73226
73737
  *
@@ -73273,6 +73784,9 @@ class ChromAliasFile {
73273
73784
  aliasRecord[key] = tokens[i];
73274
73785
  this.aliasRecordCache.set(tokens[i], aliasRecord);
73275
73786
  }
73787
+
73788
+ this.aliasRecordCache.set(chr.toLowerCase(), aliasRecord);
73789
+ this.aliasRecordCache.set(chr.toUpperCase(), aliasRecord);
73276
73790
  }
73277
73791
  }
73278
73792
  }
@@ -73444,6 +73958,9 @@ class ChromAliasDefaults {
73444
73958
  this.update(id, chromosomeNames);
73445
73959
  }
73446
73960
 
73961
+ async preload() {
73962
+ // no-op
73963
+ }
73447
73964
 
73448
73965
  /**
73449
73966
  * Return the canonical chromosome name for the alias. If none found return the alias
@@ -73545,6 +74062,8 @@ class ChromAliasDefaults {
73545
74062
  } else if (Number.isInteger(Number(name))) {
73546
74063
  record["ucsc"] = "chr" + name;
73547
74064
  }
74065
+
74066
+
73548
74067
  }
73549
74068
  }
73550
74069
 
@@ -73552,7 +74071,10 @@ class ChromAliasDefaults {
73552
74071
  for (let a of Object.values(rec)) {
73553
74072
  this.aliasRecordCache.set(a, rec);
73554
74073
  }
74074
+ this.aliasRecordCache.set(rec.chr.toLowerCase(), rec);
74075
+ this.aliasRecordCache.set(rec.chr.toUpperCase(), rec);
73555
74076
  }
74077
+
73556
74078
  }
73557
74079
  }
73558
74080
 
@@ -73620,7 +74142,7 @@ class Genome {
73620
74142
 
73621
74143
  if (config.chromAliasBbURL) {
73622
74144
  this.chromAlias = new ChromAliasBB(config.chromAliasBbURL, Object.assign({}, config), this);
73623
- if(!this.chromosomeNames) {
74145
+ if (!this.chromosomeNames) {
73624
74146
  this.chromosomeNames = await this.chromAlias.getChromosomeNames();
73625
74147
  }
73626
74148
  } else if (config.aliasURL) {
@@ -73631,17 +74153,17 @@ class Genome {
73631
74153
 
73632
74154
  if (config.cytobandBbURL) {
73633
74155
  this.cytobandSource = new CytobandFileBB(config.cytobandBbURL, Object.assign({}, config), this);
73634
- if(!this.chromosomeNames) {
74156
+ if (!this.chromosomeNames) {
73635
74157
  this.chromosomeNames = await this.cytobandSource.getChromosomeNames();
73636
74158
  }
73637
74159
  } else if (config.cytobandURL) {
73638
74160
  this.cytobandSource = new CytobandFile(config.cytobandURL, Object.assign({}, config));
73639
- if(!this.chromosomeNames) {
74161
+ if (!this.chromosomeNames) {
73640
74162
  this.chromosomeNames = await this.cytobandSource.getChromosomeNames();
73641
74163
  }
73642
- if(this.chromosomes.size === 0) {
74164
+ if (this.chromosomes.size === 0) {
73643
74165
  const c = await this.cytobandSource.getChromosomes();
73644
- for(let chromosome of c) {
74166
+ for (let chromosome of c) {
73645
74167
  this.chromosomes.set(c.name, c);
73646
74168
  }
73647
74169
  }
@@ -73659,6 +74181,7 @@ class Genome {
73659
74181
  } else {
73660
74182
  this.#wgChromosomeNames = trimSmallChromosomes(this.chromosomes);
73661
74183
  }
74184
+ await this.chromAlias.preload(this.#wgChromosomeNames);
73662
74185
  }
73663
74186
 
73664
74187
  // Optionally create the psuedo chromosome "all" to support whole genome view
@@ -73725,15 +74248,15 @@ class Genome {
73725
74248
  async loadChromosome(chr) {
73726
74249
 
73727
74250
  if (this.chromAlias) {
73728
- const chromAliasRecord = await this.chromAlias.search(chr);
73729
- if(chromAliasRecord) {
74251
+ const chromAliasRecord = await this.chromAlias.search(chr);
74252
+ if (chromAliasRecord) {
73730
74253
  chr = chromAliasRecord.chr;
73731
74254
  }
73732
74255
  }
73733
74256
 
73734
74257
  if (!this.chromosomes.has(chr)) {
73735
74258
  let chromosome;
73736
- const sequenceRecord = await this.sequence.getSequenceRecord(chr);
74259
+ const sequenceRecord = await this.sequence.getSequenceRecord(chr);
73737
74260
  if (sequenceRecord) {
73738
74261
  chromosome = new Chromosome(chr, 0, sequenceRecord.bpLength);
73739
74262
  }
@@ -73743,6 +74266,7 @@ class Genome {
73743
74266
 
73744
74267
  return this.chromosomes.get(chr)
73745
74268
  }
74269
+
73746
74270
  async getAliasRecord(chr) {
73747
74271
  if (this.chromAlias) {
73748
74272
  return this.chromAlias.search(chr)
@@ -73762,7 +74286,7 @@ class Genome {
73762
74286
  }
73763
74287
 
73764
74288
  get wgChromosomeNames() {
73765
- return this.#wgChromosomeNames ? this.#wgChromosomeNames.slice() : undefined
74289
+ return this.#wgChromosomeNames ? this.#wgChromosomeNames.slice() : undefined
73766
74290
  }
73767
74291
 
73768
74292
  get showChromosomeWidget() {
@@ -73866,7 +74390,7 @@ class Genome {
73866
74390
  * @param end
73867
74391
  */
73868
74392
  getSequenceInterval(chr, start, end) {
73869
- if(typeof this.sequence.getSequenceInterval === 'function') {
74393
+ if (typeof this.sequence.getSequenceInterval === 'function') {
73870
74394
  return this.sequence.getSequenceInterval(chr, start, end)
73871
74395
  } else {
73872
74396
  return undefined
@@ -74035,6 +74559,125 @@ brewer.push("rgb(188, 128, 189)");
74035
74559
  brewer.push("rgb(204, 235, 197)");
74036
74560
  brewer.push("rgb(255, 237, 111)");
74037
74561
 
74562
+ class SliderDialog {
74563
+
74564
+ constructor(parent) {
74565
+
74566
+ this.parent = parent;
74567
+
74568
+ // dialog container
74569
+ this.container = div({class: 'igv-ui-generic-dialog-container'});
74570
+ parent.appendChild(this.container);
74571
+
74572
+ // dialog header
74573
+ const header = div({class: 'igv-ui-generic-dialog-header'});
74574
+ this.container.appendChild(header);
74575
+
74576
+ // dialog label
74577
+ this.label = div({class: 'igv-ui-generic-dialog-one-liner'});
74578
+ this.container.appendChild(this.label);
74579
+ this.label.text = 'Unlabeled';
74580
+
74581
+ // input container
74582
+ this.input_container = div({class: 'igv-ui-generic-dialog-input'});
74583
+ this.container.appendChild(this.input_container);
74584
+
74585
+ // input element
74586
+ let html = `<input type="range" id="igv-slider-dialog-input" name="igv-slider-dialog-input" />`;
74587
+ this._input = document.createRange().createContextualFragment(html).firstChild;
74588
+ this.input_container.appendChild(this._input);
74589
+
74590
+ // output element
74591
+ html = `<output id="igv-slider-dialog-output" name="igv-slider-dialog-output" for="igv-slider-dialog-input"></output>`;
74592
+ this._output = document.createRange().createContextualFragment(html).firstChild;
74593
+ this.input_container.appendChild(this._output);
74594
+
74595
+
74596
+ // ok | cancel
74597
+ const buttons = div({class: 'igv-ui-generic-dialog-ok-cancel'});
74598
+ this.container.appendChild(buttons);
74599
+
74600
+ // ok
74601
+ this.ok = div();
74602
+ buttons.appendChild(this.ok);
74603
+ this.ok.textContent = 'OK';
74604
+
74605
+ // cancel
74606
+ this.cancel = div();
74607
+ buttons.appendChild(this.cancel);
74608
+ this.cancel.textContent = 'Cancel';
74609
+
74610
+ hide(this.container);
74611
+
74612
+ this._input.addEventListener('input', () => {
74613
+ const number = parseFloat(this._input.value)/this._scaleFactor;
74614
+ this.callback(number);
74615
+ this._output.value = `${number.toFixed(2)}`;
74616
+ }, false);
74617
+
74618
+ this.ok.addEventListener('click', () => {
74619
+ if (typeof this.callback === 'function') {
74620
+ const number = parseFloat(this._input.value)/this._scaleFactor;
74621
+ this.callback(number);
74622
+ this.callback = undefined;
74623
+ }
74624
+ this._input.value = undefined;
74625
+ hide(this.container);
74626
+ });
74627
+
74628
+ const cancel = () => {
74629
+ this._input.value = undefined;
74630
+ hide(this.container);
74631
+ };
74632
+
74633
+ this.cancel.addEventListener('click', cancel);
74634
+
74635
+ attachDialogCloseHandlerWithParent(header, cancel);
74636
+ makeDraggable(this.container, header);
74637
+
74638
+ }
74639
+
74640
+ get value() {
74641
+ return purify.sanitize(this._input.value)
74642
+ }
74643
+
74644
+ present(options, e) {
74645
+
74646
+ this.label.textContent = options.label;
74647
+
74648
+ this._scaleFactor = options.scaleFactor;
74649
+ const [ minS, maxS, valueS ] = [ options.min, options.max, options.value ].map(number => (Math.floor(this._scaleFactor * number)).toString());
74650
+
74651
+ this._input.min = minS;
74652
+ this._input.max = maxS;
74653
+ this._input.value = valueS;
74654
+
74655
+ const numer = parseFloat(valueS);
74656
+ const denom = this._scaleFactor;
74657
+ const number = numer/denom;
74658
+ this._output.value = `${number.toFixed(2)}`;
74659
+
74660
+ this.callback = options.callback || options.click;
74661
+
74662
+ show(this.container);
74663
+ this.clampLocation(e.clientX, e.clientY);
74664
+
74665
+ }
74666
+
74667
+ clampLocation(clientX, clientY) {
74668
+
74669
+ const {width: w, height: h} = this.container.getBoundingClientRect();
74670
+ const wh = window.innerHeight;
74671
+ const ww = window.innerWidth;
74672
+
74673
+ const y = Math.min(wh - h, clientY);
74674
+ const x = Math.min(ww - w, clientX);
74675
+ this.container.style.left = `${x}px`;
74676
+ this.container.style.top = `${y}px`;
74677
+
74678
+ }
74679
+ }
74680
+
74038
74681
  // css - $igv-scrollbar-outer-width: 14px;
74039
74682
  const igv_scrollbar_outer_width = 14;
74040
74683
 
@@ -74124,7 +74767,6 @@ class Browser {
74124
74767
  this.sampleNameControl.setState(this.showSampleNames);
74125
74768
  this.sampleNameControl.hide();
74126
74769
 
74127
-
74128
74770
  this.layoutChange();
74129
74771
  }
74130
74772
  });
@@ -74143,7 +74785,7 @@ class Browser {
74143
74785
 
74144
74786
  this.sampleInfo = new SampleInfo(this);
74145
74787
 
74146
- this.setControls(config);
74788
+ this.createStandardControls(config);
74147
74789
 
74148
74790
  // Region of interest
74149
74791
  this.roiManager = new ROIManager(this);
@@ -74201,121 +74843,18 @@ class Browser {
74201
74843
  }
74202
74844
  }
74203
74845
 
74204
- setControls(config) {
74205
-
74206
- const $navBar = this.createStandardControls(config);
74207
- $navBar.insertBefore($$1(this.columnContainer));
74208
- this.$navigation = $navBar;
74209
-
74210
- if (false === config.showControls) {
74211
- $navBar.hide();
74212
- }
74213
-
74214
- }
74215
-
74216
74846
  createStandardControls(config) {
74217
74847
 
74218
- const $navBar = $$1('<div>', {class: 'igv-navbar'});
74219
- this.$navigation = $navBar;
74220
-
74221
- const $navbarLeftContainer = $$1('<div>', {class: 'igv-navbar-left-container'});
74222
- $navBar.append($navbarLeftContainer);
74223
-
74224
- // IGV logo
74225
- const $logo = $$1('<div>', {class: 'igv-logo'});
74226
- $navbarLeftContainer.append($logo);
74227
-
74228
- const logoSvg = logo();
74229
- logoSvg.css("width", "34px");
74230
- logoSvg.css("height", "32px");
74231
- $logo.append(logoSvg);
74232
-
74233
- this.$current_genome = $$1('<div>', {class: 'igv-current-genome'});
74234
- $navbarLeftContainer.append(this.$current_genome);
74235
- this.$current_genome.text('');
74236
-
74237
- const $genomicLocation = $$1('<div>', {class: 'igv-navbar-genomic-location'});
74238
- $navbarLeftContainer.append($genomicLocation);
74239
-
74240
- // chromosome select widget
74241
- this.chromosomeSelectWidget = new ChromosomeSelectWidget(this, $genomicLocation.get(0));
74242
- if (config.showChromosomeWidget !== false) {
74243
- this.chromosomeSelectWidget.show();
74244
- } else {
74245
- this.chromosomeSelectWidget.hide();
74246
- }
74247
-
74248
- const $locusSizeGroup = $$1('<div>', {class: 'igv-locus-size-group'});
74249
- $genomicLocation.append($locusSizeGroup);
74250
-
74251
- const $searchContainer = $$1('<div>', {class: 'igv-search-container'});
74252
- $locusSizeGroup.append($searchContainer);
74253
-
74254
- // browser.$searchInput = $('<input type="text" placeholder="Locus Search">');
74255
- this.$searchInput = $$1('<input>', {class: 'igv-search-input', type: 'text', placeholder: 'Locus Search'});
74256
- $searchContainer.append(this.$searchInput);
74257
- // Stop event propagation to prevent feature track keyboard navigation
74258
- this.$searchInput[0].addEventListener('keyup', (event) => {
74259
- event.stopImmediatePropagation();
74260
- });
74261
-
74262
- this.$searchInput.change(() => this.doSearch(this.$searchInput.val()));
74263
-
74264
- const searchIconContainer = div({class: 'igv-search-icon-container'});
74265
- $searchContainer.append($$1(searchIconContainer));
74266
-
74267
- searchIconContainer.appendChild(createIcon("search"));
74268
-
74269
- searchIconContainer.addEventListener('click', () => this.doSearch(this.$searchInput.val()));
74270
-
74271
- this.windowSizePanel = new WindowSizePanel($locusSizeGroup.get(0), this);
74272
-
74273
- const $navbarRightContainer = $$1('<div>', {class: 'igv-navbar-right-container'});
74274
- $navBar.append($navbarRightContainer);
74275
-
74276
- const $toggle_button_container = $$1('<div class="igv-navbar-toggle-button-container">');
74277
- $navbarRightContainer.append($toggle_button_container);
74278
- this.$toggle_button_container = $toggle_button_container;
74279
-
74280
- this.overlayTrackButton = new OverlayTrackButton(this, $toggle_button_container.get(0));
74281
- this.overlayTrackButton.setVisibility(false);
74282
-
74283
- this.multiTrackSelectButton = new MultiTrackSelectButton(this, $toggle_button_container.get(0));
74284
-
74285
- this.cursorGuide = new CursorGuide(this.columnContainer, this);
74286
-
74287
- this.cursorGuideButton = new CursorGuideButton(this, $toggle_button_container.get(0));
74288
-
74289
- this.centerLineButton = new CenterLineButton(this, $toggle_button_container.get(0));
74290
-
74291
74848
  this.setTrackLabelVisibility(config.showTrackLabels);
74292
- this.trackLabelControl = new TrackLabelControl($toggle_button_container.get(0), this);
74293
74849
 
74294
- // ROI Control
74295
- this.roiTableControl = new ROITableControl($toggle_button_container.get(0), this);
74296
-
74297
- this.sampleInfoControl = new SampleInfoControl($toggle_button_container.get(0), this);
74850
+ this.navbar = new ResponsiveNavbar(config, this);
74298
74851
 
74299
- this.sampleNameControl = new SampleNameControl($toggle_button_container.get(0), this);
74300
-
74301
- if (true === config.showSVGButton) {
74302
- this.saveImageControl = new SaveImageControl($toggle_button_container.get(0), this);
74303
- }
74852
+ this.navbar.$navigation.insertBefore($$1(this.columnContainer));
74304
74853
 
74305
- if (config.customButtons) {
74306
- for (let b of config.customButtons) {
74307
- new CustomButton($toggle_button_container.get(0), this, b);
74308
- }
74309
- }
74310
-
74311
- this.zoomWidget = new ZoomWidget(this, $navbarRightContainer.get(0));
74312
-
74313
- if (false === config.showNavigation) {
74314
- this.$navigation.hide();
74854
+ if (false === config.showControls) {
74855
+ this.navbar.hide();
74315
74856
  }
74316
-
74317
- this.sliderDialog = new SliderDialog(this.root);
74318
- this.sliderDialog.container.id = `igv-slider-dialog-${guid$2()}`;
74857
+ this.cursorGuide = new CursorGuide(this.columnContainer, this);
74319
74858
 
74320
74859
  this.inputDialog = new InputDialog(this.root);
74321
74860
  this.inputDialog.container.id = `igv-input-dialog-${guid$2()}`;
@@ -74326,7 +74865,8 @@ class Browser {
74326
74865
  this.genericColorPicker = new GenericColorPicker({parent: this.columnContainer, width: 432});
74327
74866
  this.genericColorPicker.container.id = `igv-track-color-picker-${guid$2()}`;
74328
74867
 
74329
- return $navBar
74868
+ this.sliderDialog = new SliderDialog(this.root);
74869
+ this.sliderDialog.container.id = `igv-slider-dialog-${guid$2()}`;
74330
74870
 
74331
74871
  }
74332
74872
 
@@ -74557,10 +75097,10 @@ class Browser {
74557
75097
  session = await translateSession(session);
74558
75098
  }
74559
75099
 
74560
- this.sampleInfoControl.setButtonVisibility(false);
75100
+ this.navbar.sampleInfoControl.setButtonVisibility(false);
74561
75101
 
74562
75102
  this.showSampleNames = session.showSampleNames || false;
74563
- this.sampleNameControl.setState(this.showSampleNames === true);
75103
+ this.navbar.sampleNameControl.setState(this.showSampleNames === true);
74564
75104
 
74565
75105
  if (session.sampleNameViewportWidth) {
74566
75106
  this.sampleNameViewportWidth = session.sampleNameViewportWidth;
@@ -74680,9 +75220,9 @@ class Browser {
74680
75220
  await rtv.updateViews();
74681
75221
  }
74682
75222
 
74683
- // If any tracks are selected show the selectino buttons
75223
+ // If any tracks are selected show the selection buttons
74684
75224
  if (this.trackViews.some(tv => tv.track.selected)) {
74685
- this.multiTrackSelectButton.setMultiTrackSelection(true);
75225
+ this.navbar.setEnableTrackSelection(true);
74686
75226
  }
74687
75227
 
74688
75228
  this.updateUIWithReferenceFrameList();
@@ -74723,7 +75263,8 @@ class Browser {
74723
75263
 
74724
75264
  this.removeAllTracks(); // Do this first, before new genome is set
74725
75265
  this.roiManager.clearROIs();
74726
- this.multiTrackSelectButton.setMultiTrackSelection(false);
75266
+
75267
+ this.navbar.setEnableTrackSelection(false);
74727
75268
 
74728
75269
  let genome;
74729
75270
  if (genomeConfig.gbkURL) {
@@ -74736,8 +75277,7 @@ class Browser {
74736
75277
 
74737
75278
  this.genome = genome;
74738
75279
 
74739
- this.updateNavbarDOMWithGenome(genome);
74740
-
75280
+ this.navbar.updateGenome(genome);
74741
75281
 
74742
75282
  let locus = initialLocus || genome.initialLocus;
74743
75283
  if (Array.isArray(locus)) {
@@ -74768,26 +75308,6 @@ class Browser {
74768
75308
  }
74769
75309
  }
74770
75310
 
74771
- updateNavbarDOMWithGenome(genome) {
74772
- let genomeLabel = (genome.id && genome.id.length < 20 ? genome.id : `${genome.id.substring(0, 8)}...${genome.id.substring(genome.id.length - 8)}`);
74773
- this.$current_genome.text(genomeLabel);
74774
- this.$current_genome.attr('title', genome.description);
74775
-
74776
- // chromosome select widget -- Show this IFF its not explicitly hidden AND the genome has pre-loaded chromosomes
74777
- const showChromosomeWidget =
74778
- this.config.showChromosomeWidget !== false &&
74779
- this.genome.showChromosomeWidget !== false &&
74780
- genome.chromosomeNames &&
74781
- genome.chromosomeNames.length > 1;
74782
-
74783
- if (showChromosomeWidget) {
74784
- this.chromosomeSelectWidget.update(genome);
74785
- this.chromosomeSelectWidget.show();
74786
- } else {
74787
- this.chromosomeSelectWidget.hide();
74788
- }
74789
- }
74790
-
74791
75311
  /**
74792
75312
  * Load a genome, defined by a string ID or a json-like configuration object. This includes a fasta reference
74793
75313
  * as well as optional cytoband and annotation tracks.
@@ -74872,16 +75392,16 @@ class Browser {
74872
75392
 
74873
75393
  (this.isMultiLocusWholeGenomeView() || GenomeUtils.isWholeGenomeView(referenceFrameList[0].chr));
74874
75394
 
74875
- navbarDidResize(this, this.$navigation.width());
75395
+ this.navbar.navbarDidResize();
74876
75396
 
74877
75397
  toggleTrackLabels(this.trackViews, this.doShowTrackLabels);
74878
75398
 
74879
75399
  if (this.doShowCenterLine && GenomeUtils.isWholeGenomeView(referenceFrameList[0].chr)) {
74880
- this.centerLineButton.boundMouseClickHandler();
75400
+ this.navbar.centerLineButton.boundMouseClickHandler();
74881
75401
  }
74882
75402
 
74883
75403
  if (this.doShowCursorGuide && GenomeUtils.isWholeGenomeView(referenceFrameList[0].chr)) {
74884
- this.cursorGuideButton.boundMouseClickHandler();
75404
+ this.navbar.cursorGuideButton.boundMouseClickHandler();
74885
75405
  }
74886
75406
 
74887
75407
  this.setCenterLineAndCenterLineButtonVisibility(GenomeUtils.isWholeGenomeView(referenceFrameList[0].chr));
@@ -74891,9 +75411,9 @@ class Browser {
74891
75411
  setCenterLineAndCenterLineButtonVisibility(isWholeGenomeView) {
74892
75412
 
74893
75413
  if (isWholeGenomeView) {
74894
- this.centerLineButton.setVisibility(!isWholeGenomeView);
75414
+ this.navbar.centerLineButton.setVisibility(false);
74895
75415
  } else {
74896
- this.centerLineButton.setVisibility(this.config.showCenterGuideButton);
75416
+ this.navbar.centerLineButton.setVisibility(this.config.showCenterGuideButton);
74897
75417
  }
74898
75418
 
74899
75419
  for (let centerLine of this.centerLineList) {
@@ -75099,7 +75619,7 @@ class Browser {
75099
75619
  this.reorderTracks();
75100
75620
  this.fireEvent('trackorderchanged', [this.getTrackOrder()]);
75101
75621
 
75102
- this.multiTrackSelectButton.setMultiTrackSelection(this.multiTrackSelectButton.enableMultiTrackSelection);
75622
+ newTrack.trackView.enableTrackSelection(this.navbar.getEnableTrackSelection());
75103
75623
 
75104
75624
  return newTrack
75105
75625
 
@@ -75467,8 +75987,7 @@ class Browser {
75467
75987
  }
75468
75988
 
75469
75989
  if (this.referenceFrameList) {
75470
- this.isMultiLocusWholeGenomeView() || GenomeUtils.isWholeGenomeView(this.referenceFrameList[0].chr);
75471
- navbarDidResize(this, this.$navigation.width());
75990
+ this.navbar.navbarDidResize();
75472
75991
  }
75473
75992
 
75474
75993
  resize.call(this);
@@ -75550,13 +76069,11 @@ class Browser {
75550
76069
  referenceFrame.end = referenceFrame.start + referenceFrame.bpPerPixel * width;
75551
76070
  }
75552
76071
 
75553
- if (this.chromosomeSelectWidget) {
75554
- this.chromosomeSelectWidget.select.value = referenceFrameList.length === 1 ? this.referenceFrameList[0].chr : '';
75555
- }
76072
+ const chrName = referenceFrameList.length === 1 ? this.referenceFrameList[0].chr : '';
75556
76073
 
75557
76074
  const loc = this.referenceFrameList.map(rf => rf.getLocusString()).join(' ');
75558
76075
 
75559
- this.$searchInput.val(loc);
76076
+ this.navbar.updateLocus(loc, chrName);
75560
76077
 
75561
76078
  this.fireEvent('locuschange', [this.referenceFrameList]);
75562
76079
  }
@@ -75831,7 +76348,7 @@ class Browser {
75831
76348
  async loadSampleInfo(config) {
75832
76349
 
75833
76350
  await this.sampleInfo.loadSampleInfoFile(config.url);
75834
-
76351
+
75835
76352
  for (const {sampleInfoViewport} of this.trackViews) {
75836
76353
  sampleInfoViewport.setWidth(this.getSampleInfoColumnWidth());
75837
76354
  }
@@ -76287,7 +76804,7 @@ class Browser {
76287
76804
  createCircularView(container, show) {
76288
76805
  show = show === true; // convert undefined to boolean
76289
76806
  this.circularView = createCircularView(container, this);
76290
- this.circularViewControl = new CircularViewControl(this.$toggle_button_container.get(0), this);
76807
+ this.circularViewControl = new CircularViewControl(this.navbar.toggle_button_container, this);
76291
76808
  this.circularView.setAssembly({
76292
76809
  name: this.genome.id,
76293
76810
  id: this.genome.id,
@@ -76307,6 +76824,30 @@ class Browser {
76307
76824
  this.circularViewControl.setState(isVisible);
76308
76825
  }
76309
76826
  }
76827
+
76828
+
76829
+
76830
+ // Navbar delegates
76831
+ get sampleInfoControl() {
76832
+ return this.navbar.sampleInfoControl
76833
+ }
76834
+
76835
+ get overlayTrackButton() {
76836
+ return this.navbar.overlayTrackButton
76837
+ }
76838
+
76839
+ get roiTableControl() {
76840
+ return this.navbar.roiTableControl
76841
+ }
76842
+
76843
+ get sampleInfoControl() {
76844
+ return this.navbar.sampleInfoControl
76845
+ }
76846
+
76847
+ get sampleNameControl() {
76848
+ return this.navbar.sampleNameControl
76849
+ }
76850
+
76310
76851
  }
76311
76852
 
76312
76853
  function getFileExtension(input) {
@@ -76516,21 +77057,6 @@ async function keyUpHandler(event) {
76516
77057
  }
76517
77058
  }
76518
77059
 
76519
-
76520
- function logo() {
76521
-
76522
- return $$1(
76523
- '<svg width="690px" height="324px" viewBox="0 0 690 324" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">' +
76524
- '<title>IGV</title>' +
76525
- '<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">' +
76526
- '<g id="IGV" fill="#666666">' +
76527
- '<polygon id="Path" points="379.54574 8.00169252 455.581247 8.00169252 515.564813 188.87244 532.884012 253.529506 537.108207 253.529506 554.849825 188.87244 614.833392 8.00169252 689.60164 8.00169252 582.729511 320.722144 486.840288 320.722144"></polygon>' +
76528
- '<path d="M261.482414,323.793286 C207.975678,323.793286 168.339046,310.552102 142.571329,284.069337 C116.803612,257.586572 103.919946,217.158702 103.919946,162.784513 C103.919946,108.410325 117.437235,67.8415913 144.472217,41.0770945 C171.507199,14.3125977 212.903894,0.930550071 268.663545,0.930550071 C283.025879,0.930550071 298.232828,1.84616386 314.284849,3.6774189 C330.33687,5.50867394 344.839793,7.97378798 357.794056,11.072835 L357.794056,68.968378 C339.48912,65.869331 323.578145,63.5450806 310.060654,61.9955571 C296.543163,60.4460336 284.574731,59.6712835 274.154998,59.6712835 C255.850062,59.6712835 240.502308,61.4320792 228.111274,64.9537236 C215.720241,68.4753679 205.793482,74.2507779 198.330701,82.2801269 C190.867919,90.309476 185.587729,100.87425 182.48997,113.974767 C179.392212,127.075284 177.843356,143.345037 177.843356,162.784513 C177.843356,181.942258 179.251407,198.000716 182.067551,210.960367 C184.883695,223.920018 189.671068,234.41436 196.429813,242.443709 C203.188559,250.473058 212.059279,256.178037 223.042241,259.558815 C234.025202,262.939594 247.683295,264.629958 264.01693,264.629958 C268.241146,264.629958 273.098922,264.489094 278.590403,264.207362 C284.081883,263.925631 289.643684,263.50304 295.275972,262.939577 L295.275972,159.826347 L361.595831,159.826347 L361.595831,308.579859 C344.698967,313.087564 327.239137,316.750019 309.215815,319.567334 C291.192494,322.38465 275.281519,323.793286 261.482414,323.793286 L261.482414,323.793286 L261.482414,323.793286 Z" id="Path"></path>;' +
76529
- '<polygon id="Path" points="0.81355666 5.00169252 73.0472883 5.00169252 73.0472883 317.722144 0.81355666 317.722144"></polygon>' +
76530
- '</g> </g> </svg>'
76531
- )
76532
- }
76533
-
76534
77060
  function toggleTrackLabels(trackViews, isVisible) {
76535
77061
 
76536
77062
  for (let {viewports} of trackViews) {
@@ -76625,7 +77151,7 @@ async function createBrowser(parentDiv, config) {
76625
77151
  }
76626
77152
 
76627
77153
  browser.stopSpinner();
76628
- navbarDidResize(browser, browser.$navigation.width());
77154
+ browser.navbar.navbarDidResize();
76629
77155
 
76630
77156
  return browser
76631
77157