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.js CHANGED
@@ -18599,7 +18599,7 @@
18599
18599
 
18600
18600
  }
18601
18601
 
18602
- const colorPickerTrackTypeSet = new Set(['bedtype', 'alignment', 'annotation', 'variant', 'wig', 'interact']);
18602
+ const colorPickerTrackTypeSet = new Set(['bedtype', 'alignment', 'annotation', 'variant', 'wig', 'interact', 'shoebox']);
18603
18603
 
18604
18604
  const vizWindowTypes = new Set(['alignment', 'annotation', 'variant', 'eqtl', 'qtl', 'snp', 'shoebox', 'wig']);
18605
18605
 
@@ -20942,125 +20942,6 @@
20942
20942
  }
20943
20943
  var purify = createDOMPurify();
20944
20944
 
20945
- class SliderDialog {
20946
-
20947
- constructor(parent) {
20948
-
20949
- this.parent = parent;
20950
-
20951
- // dialog container
20952
- this.container = div({class: 'igv-ui-generic-dialog-container'});
20953
- parent.appendChild(this.container);
20954
-
20955
- // dialog header
20956
- const header = div({class: 'igv-ui-generic-dialog-header'});
20957
- this.container.appendChild(header);
20958
-
20959
- // dialog label
20960
- this.label = div({class: 'igv-ui-generic-dialog-one-liner'});
20961
- this.container.appendChild(this.label);
20962
- this.label.text = 'Unlabeled';
20963
-
20964
- // input container
20965
- this.input_container = div({class: 'igv-ui-generic-dialog-input'});
20966
- this.container.appendChild(this.input_container);
20967
-
20968
- // input element
20969
- let html = `<input type="range" id="igv-slider-dialog-input" name="igv-slider-dialog-input" />`;
20970
- this._input = document.createRange().createContextualFragment(html).firstChild;
20971
- this.input_container.appendChild(this._input);
20972
-
20973
- // output element
20974
- html = `<output id="igv-slider-dialog-output" name="igv-slider-dialog-output" for="igv-slider-dialog-input"></output>`;
20975
- this._output = document.createRange().createContextualFragment(html).firstChild;
20976
- this.input_container.appendChild(this._output);
20977
-
20978
-
20979
- // ok | cancel
20980
- const buttons = div({class: 'igv-ui-generic-dialog-ok-cancel'});
20981
- this.container.appendChild(buttons);
20982
-
20983
- // ok
20984
- this.ok = div();
20985
- buttons.appendChild(this.ok);
20986
- this.ok.textContent = 'OK';
20987
-
20988
- // cancel
20989
- this.cancel = div();
20990
- buttons.appendChild(this.cancel);
20991
- this.cancel.textContent = 'Cancel';
20992
-
20993
- hide(this.container);
20994
-
20995
- this._input.addEventListener('input', () => {
20996
- const number = parseFloat(this._input.value)/this._scaleFactor;
20997
- this.callback(number);
20998
- this._output.value = `${number.toFixed(2)}`;
20999
- }, false);
21000
-
21001
- this.ok.addEventListener('click', () => {
21002
- if (typeof this.callback === 'function') {
21003
- const number = parseFloat(this._input.value)/this._scaleFactor;
21004
- this.callback(number);
21005
- this.callback = undefined;
21006
- }
21007
- this._input.value = undefined;
21008
- hide(this.container);
21009
- });
21010
-
21011
- const cancel = () => {
21012
- this._input.value = undefined;
21013
- hide(this.container);
21014
- };
21015
-
21016
- this.cancel.addEventListener('click', cancel);
21017
-
21018
- attachDialogCloseHandlerWithParent(header, cancel);
21019
- makeDraggable(this.container, header);
21020
-
21021
- }
21022
-
21023
- get value() {
21024
- return purify.sanitize(this._input.value)
21025
- }
21026
-
21027
- present(options, e) {
21028
-
21029
- this.label.textContent = options.label;
21030
-
21031
- this._scaleFactor = options.scaleFactor;
21032
- const [ minS, maxS, valueS ] = [ options.min, options.max, options.value ].map(number => (Math.floor(this._scaleFactor * number)).toString());
21033
-
21034
- this._input.min = minS;
21035
- this._input.max = maxS;
21036
- this._input.value = valueS;
21037
-
21038
- const numer = parseFloat(valueS);
21039
- const denom = this._scaleFactor;
21040
- const number = numer/denom;
21041
- this._output.value = `${number.toFixed(2)}`;
21042
-
21043
- this.callback = options.callback || options.click;
21044
-
21045
- show(this.container);
21046
- this.clampLocation(e.clientX, e.clientY);
21047
-
21048
- }
21049
-
21050
- clampLocation(clientX, clientY) {
21051
-
21052
- const {width: w, height: h} = this.container.getBoundingClientRect();
21053
- const wh = window.innerHeight;
21054
- const ww = window.innerWidth;
21055
-
21056
- const y = Math.min(wh - h, clientY);
21057
- const x = Math.min(ww - w, clientX);
21058
- this.container.style.left = `${x}px`;
21059
- this.container.style.top = `${y}px`;
21060
-
21061
- }
21062
- }
21063
-
21064
20945
  class InputDialog {
21065
20946
 
21066
20947
  constructor(parent) {
@@ -24514,6 +24395,29 @@
24514
24395
 
24515
24396
  }
24516
24397
 
24398
+ function decodeShoebox(tokens, header, maxColumnCount = Number.MAX_SAFE_INTEGER) {
24399
+
24400
+ if (tokens.length < 4) return undefined
24401
+
24402
+ const chr = tokens[0];
24403
+ const start = parseInt(tokens[1]);
24404
+ const end = tokens.length > 2 ? parseInt(tokens[2]) : start + 1;
24405
+ if (isNaN(start) || isNaN(end)) {
24406
+ return new DecodeError(`Unparsable bed record.`)
24407
+ }
24408
+ const feature = new UCSCBedFeature({chr: chr, start: start, end: end, score: 1000});
24409
+
24410
+ const values = [];
24411
+ for(let i = 3; i< tokens.length; i++) {
24412
+ values.push(Number.parseInt(tokens[i]));
24413
+ }
24414
+ feature.values = values;
24415
+
24416
+
24417
+ return feature
24418
+ }
24419
+
24420
+
24517
24421
  class UCSCBedFeature {
24518
24422
 
24519
24423
  constructor(properties) {
@@ -25878,6 +25782,10 @@
25878
25782
  this.decode = decodeGcnv;
25879
25783
  this.delimiter = "\t";
25880
25784
  break
25785
+ case "shoebox":
25786
+ this.decode = decodeShoebox;
25787
+ this.delimiter = "\t";
25788
+ break
25881
25789
  default:
25882
25790
  const customFormat = getFormat(format);
25883
25791
  if (customFormat !== undefined) {
@@ -31964,14 +31872,25 @@
31964
31872
  this.config = config;
31965
31873
  this.bufferSize = BUFFER_SIZE;
31966
31874
  this.loader = isDataURL(this.path) ?
31967
- new DataBuffer(this.path) : igvxhr;
31875
+ new DataBuffer(decodeDataURI$1(this.path).buffer) :
31876
+ igvxhr;
31968
31877
 
31969
- if (config.searchTrix) {
31970
- this._trix = new Trix(`${config.searchTrix}x`, config.searchTrix);
31878
+ const trixURL = config.trixURL || config.searchTrix;
31879
+ if (trixURL) {
31880
+ this._trix = new Trix(`${trixURL}x`, trixURL);
31971
31881
  }
31972
31882
 
31973
31883
  }
31974
31884
 
31885
+ /**
31886
+ * Preload all the data for this bb file
31887
+ * @returns {Promise<void>}
31888
+ */
31889
+ async preload() {
31890
+ const data = await igvxhr.loadArrayBuffer(this.path);
31891
+ this.loader = new DataBuffer(data);
31892
+ }
31893
+
31975
31894
  async readWGFeatures(bpPerPixel, windowFunction) {
31976
31895
  await this.loadHeader();
31977
31896
  const chrIdx1 = 0;
@@ -32637,8 +32556,8 @@
32637
32556
 
32638
32557
  class DataBuffer {
32639
32558
 
32640
- constructor(dataURI) {
32641
- this.data = decodeDataURI$1(dataURI).buffer;
32559
+ constructor(data) {
32560
+ this.data = data;
32642
32561
  }
32643
32562
 
32644
32563
  /**
@@ -38025,6 +37944,16 @@
38025
37944
  const groupsTxtURL = baseURL + genome.getProperty("groups");
38026
37945
  groups = await loadStanzas(groupsTxtURL);
38027
37946
  }
37947
+
37948
+ // If the genome has a chromSizes file, and it is not too large, set the chromSizesURL property. This will
37949
+ // enable whole genome view and the chromosome pulldown
37950
+ if (genome.hasProperty("chromSizes")) {
37951
+ const chromSizesURL = baseURL + genome.getProperty("chromSizes");
37952
+ const l = await getContentLength(chromSizesURL);
37953
+ if (l !== null && Number.parseInt(l) < 1000000) {
37954
+ genome.setProperty("chromSizesURL", chromSizesURL);
37955
+ }
37956
+ }
38028
37957
  }
38029
37958
 
38030
37959
  // TODO -- categorize extra "user" supplied and other tracks in some distinctive way before including them
@@ -38127,10 +38056,15 @@
38127
38056
  name: name,
38128
38057
  twoBitURL: this.baseURL + this.genomeStanza.getProperty("twoBitPath"),
38129
38058
  nameSet: "ucsc",
38130
- wholeGenomeView: false,
38131
- showChromosomeWidget: false
38132
38059
  };
38133
38060
 
38061
+ if (this.genomeStanza.hasProperty("chromSizesURL")) {
38062
+ config.chromSizesURL = this.genomeStanza.getProperty("chromSizesURL");
38063
+ } else {
38064
+ config.wholeGenomeView = false;
38065
+ config.showChromosomeWidget = false;
38066
+ }
38067
+
38134
38068
  if (this.genomeStanza.hasProperty("defaultPos")) {
38135
38069
  const hubLocus = this.genomeStanza.getProperty("defaultPos");
38136
38070
  // Strip out coordinates => whole chromosome view
@@ -38331,7 +38265,7 @@
38331
38265
  config.searchIndex = t.getProperty("searchIndex");
38332
38266
  }
38333
38267
  if (t.hasProperty("searchTrix")) {
38334
- config.searchTrix = this.baseURL + t.getProperty("searchTrix");
38268
+ config.trixURL = this.baseURL + t.getProperty("searchTrix");
38335
38269
  }
38336
38270
 
38337
38271
  if (t.hasProperty("group")) {
@@ -38418,6 +38352,26 @@
38418
38352
  }
38419
38353
  }
38420
38354
 
38355
+
38356
+ /**
38357
+ * Return the content length of the resource. If the content length cannot be determined return null;
38358
+ * @param url
38359
+ * @returns {Promise<number|string>}
38360
+ */
38361
+ async function getContentLength(url) {
38362
+ try {
38363
+ const response = await fetch(url, {method: 'HEAD'});
38364
+ const headers = response.headers;
38365
+ if (headers.has("content-length")) {
38366
+ return headers.get("content-length")
38367
+ } else {
38368
+ return null
38369
+ }
38370
+ } catch (e) {
38371
+ return null
38372
+ }
38373
+ }
38374
+
38421
38375
  /**
38422
38376
  * Parse a UCSC file
38423
38377
  * @param url
@@ -41966,7 +41920,7 @@
41966
41920
 
41967
41921
  class NavbarButton {
41968
41922
 
41969
- constructor(browser, parent, title, buttonLabel, imageSVG, imageHoverSVG, initialButtonState) {
41923
+ constructor(parent, browser, title, buttonLabel, imageSVG, imageHoverSVG, initialButtonState) {
41970
41924
 
41971
41925
  this.browser = browser;
41972
41926
 
@@ -42023,7 +41977,7 @@
42023
41977
  }
42024
41978
 
42025
41979
  configureTextButton(textContent) {
42026
-
41980
+ console.log(`text ${this.title}`);
42027
41981
  this.button.classList.add('igv-navbar-text-button');
42028
41982
 
42029
41983
  const tempDiv = document.createElement('div');
@@ -42038,6 +41992,7 @@
42038
41992
  }
42039
41993
 
42040
41994
  configureIconButton() {
41995
+ console.log(`icon ${this.title}`);
42041
41996
  this.button.classList.add('igv-navbar-icon-button');
42042
41997
  }
42043
41998
 
@@ -42077,11 +42032,6 @@
42077
42032
  this.hide();
42078
42033
  }
42079
42034
  }
42080
-
42081
- static currentNavbarButtonClass(browser) {
42082
- const el = browser.$navigation.get(0).querySelector('.igv-navbar-text-button');
42083
- return el ? 'igv-navbar-text-button' : 'igv-navbar-icon-button'
42084
- }
42085
42035
  }
42086
42036
 
42087
42037
  const overlayTrackImage =
@@ -42290,6 +42240,19 @@
42290
42240
  return this._autoscale
42291
42241
  }
42292
42242
 
42243
+ set autoscaleGroup(g) {
42244
+ if(this.tracks) {
42245
+ for(let t of this.tracks) t.autoscaleGroup = g;
42246
+ }
42247
+ }
42248
+
42249
+ get autoscaleGroup() {
42250
+ if(this.tracks && this.tracks.length > 0) {
42251
+ const g = this.tracks[0].autoscaleGroup;
42252
+ return (this.tracks.some(t => g !== t.autoscaleGroup)) ? undefined : g
42253
+ }
42254
+ }
42255
+
42293
42256
  /**
42294
42257
  * Set the data range of all constitutive numeric tracks. This method is called from the menu item, i.e. an explicit
42295
42258
  * setting, so it should disable autoscale as well.
@@ -42654,9 +42617,9 @@
42654
42617
  };
42655
42618
 
42656
42619
  class OverlayTrackButton extends NavbarButton {
42657
- constructor(browser, parent) {
42620
+ constructor(parent, browser) {
42658
42621
 
42659
- super(browser, parent, 'Overlay Tracks', buttonLabel, overlayTrackImage, overlayTrackImageHover, false);
42622
+ super(parent, browser, 'Overlay Tracks', buttonLabel, overlayTrackImage, overlayTrackImageHover, false);
42660
42623
 
42661
42624
  this.button.addEventListener('mouseenter', () => this.setState(true));
42662
42625
  this.button.addEventListener('mouseleave', () => this.setState(false));
@@ -42679,15 +42642,15 @@
42679
42642
 
42680
42643
  if (true === isOverlayTrackCriteriaMet(this.browser)) {
42681
42644
 
42682
- const tracks = this.browser.getSelectedTrackViews().map(({ track }) => track);
42645
+ const tracks = this.browser.getSelectedTrackViews().map(({track}) => track);
42683
42646
  for (const track of tracks) {
42684
42647
  track.selected = false;
42685
42648
  }
42686
42649
 
42687
- // Flatten any merged tracks. Must do this before there removal
42650
+ // Flatten any merged tracks. Must do this before their removal
42688
42651
  const flattenedTracks = [];
42689
- for(let t of tracks) {
42690
- if("merged" === t.type) {
42652
+ for (let t of tracks) {
42653
+ if ("merged" === t.type) {
42691
42654
  flattenedTracks.push(...t.tracks);
42692
42655
  } else {
42693
42656
  flattenedTracks.push(t);
@@ -42700,17 +42663,20 @@
42700
42663
  type: 'merged',
42701
42664
  autoscale: false,
42702
42665
  alpha: 0.5, //fudge * (1.0/tracks.length),
42703
- height: Math.max(...tracks.map(({ height }) => height)),
42704
- order: Math.min(...tracks.map(({ order }) => order)),
42666
+ height: Math.max(...tracks.map(({height}) => height)),
42667
+ order: Math.min(...tracks.map(({order}) => order)),
42705
42668
  };
42706
42669
 
42707
42670
  const mergedTrack = new MergedTrack(config, this.browser, flattenedTracks);
42708
42671
 
42709
42672
  for (const track of tracks) {
42710
- this.browser.removeTrack(track);
42673
+ const idx = this.browser.trackViews.indexOf(track.trackView);
42674
+ this.browser.trackViews.splice(idx, 1);
42675
+ track.trackView.dispose();
42711
42676
  }
42712
42677
 
42713
42678
  this.browser.addTrack(config, mergedTrack);
42679
+ mergedTrack.trackView.updateViews();
42714
42680
 
42715
42681
  }
42716
42682
 
@@ -42722,9 +42688,9 @@
42722
42688
 
42723
42689
  if (selected && selected.length > 1) {
42724
42690
 
42725
- const criteriaSet = new Set([ 'wig', 'merged' ]);
42691
+ const criteriaSet = new Set(['wig', 'merged']);
42726
42692
 
42727
- const list = selected.filter(({ track }) => criteriaSet.has(track.type));
42693
+ const list = selected.filter(({track}) => criteriaSet.has(track.type));
42728
42694
 
42729
42695
  return list.length > 1
42730
42696
 
@@ -42839,6 +42805,8 @@
42839
42805
  createAxis(browser, track) {
42840
42806
 
42841
42807
  const axis = div();
42808
+ this.axis = axis;
42809
+
42842
42810
  browser.columnContainer.querySelector('.igv-axis-column').appendChild(axis);
42843
42811
 
42844
42812
  axis.dataset.tracktype = track.type;
@@ -42856,12 +42824,12 @@
42856
42824
 
42857
42825
  if (false === multiTrackSelectExclusionTypes.has(this.track.type)) {
42858
42826
 
42859
- const trackSelectionContainer = div();
42860
- axis.appendChild(trackSelectionContainer);
42827
+ this.trackSelectionContainer = div();
42828
+ axis.appendChild(this.trackSelectionContainer);
42861
42829
 
42862
42830
  const html = `<input type="checkbox" name="track-select">`;
42863
42831
  const input = document.createRange().createContextualFragment(html).firstChild;
42864
- trackSelectionContainer.appendChild(input);
42832
+ this.trackSelectionContainer.appendChild(input);
42865
42833
  input.checked = this.track.selected || false;
42866
42834
 
42867
42835
  input.addEventListener('change', event => {
@@ -42869,10 +42837,10 @@
42869
42837
  event.stopPropagation();
42870
42838
  this.track.selected = event.target.checked;
42871
42839
  this.setDragHandleSelectionState(event.target.checked);
42872
- this.browser.overlayTrackButton.setVisibility( isOverlayTrackCriteriaMet(this.browser) );
42840
+ this.browser.overlayTrackButton.setVisibility(isOverlayTrackCriteriaMet(this.browser));
42873
42841
  });
42874
42842
 
42875
- this.setTrackSelectionState(axis, false);
42843
+ this.enableTrackSelection(false);
42876
42844
 
42877
42845
  }
42878
42846
 
@@ -43682,13 +43650,19 @@
43682
43650
  return Math.max(...this.viewports.map(viewport => viewport.getContentHeight()))
43683
43651
  }
43684
43652
 
43685
- setTrackSelectionState(axis, doEnableMultiSelection) {
43653
+ enableTrackSelection(doEnableMultiSelection) {
43686
43654
 
43687
- const container = axis.querySelector('div');
43655
+ const container = this.trackSelectionContainer;
43656
+
43657
+ if (!container || multiTrackSelectExclusionTypes.has(this.track.type)) {
43658
+ return
43659
+ }
43688
43660
 
43689
43661
  if (false !== doEnableMultiSelection) {
43690
43662
  container.style.display = 'grid';
43691
43663
  } else {
43664
+ // If disabling selection set track selection state to false
43665
+ this.track.selected = false;
43692
43666
 
43693
43667
  const trackSelectInput = container.querySelector('[name=track-select]');
43694
43668
  trackSelectInput.checked = this.track.selected;
@@ -43714,13 +43688,11 @@
43714
43688
  dragHandle.classList.remove('igv-track-drag-handle-selected-color');
43715
43689
  dragHandle.classList.add('igv-track-drag-handle-color');
43716
43690
  }
43717
-
43718
43691
  }
43719
43692
 
43720
43693
  }
43721
43694
 
43722
43695
 
43723
-
43724
43696
  function renderSVGAxis(context, track, axisCanvas, deltaX, deltaY) {
43725
43697
 
43726
43698
  if (typeof track.paintAxis === 'function') {
@@ -43834,13 +43806,13 @@
43834
43806
  *
43835
43807
  */
43836
43808
 
43837
- const defaultColorScaleConfig = {threshold: 2000, r: 0, g: 0, b: 255};
43809
+ const defaultColorScaleConfig$1 = {threshold: 2000, r: 0, g: 0, b: 255};
43838
43810
 
43839
43811
  class HicColorScale {
43840
43812
 
43841
43813
  constructor(scale) {
43842
43814
 
43843
- scale = scale || defaultColorScaleConfig;
43815
+ scale = scale || defaultColorScaleConfig$1;
43844
43816
  this.threshold = scale.threshold;
43845
43817
  this.r = scale.r;
43846
43818
  this.g = scale.g;
@@ -54043,6 +54015,10 @@
54043
54015
  if (firstLine.startsWith("##gff-version")) {
54044
54016
  return "gff"
54045
54017
  }
54018
+ if(firstLine.startsWith("##fileformat=")) {
54019
+ return firstLine.substring(13).toLowerCase(); // Non standard extension of VCF convention
54020
+ }
54021
+
54046
54022
 
54047
54023
  // QTL test must preceed GWAS test as GWAS files will also pass the QTL test
54048
54024
  if (QTLParser.isQTL(firstLine)) {
@@ -57279,10 +57255,6 @@
57279
57255
 
57280
57256
  function clickHandler() {
57281
57257
  this.alignmentTrack.colorBy = menuItem.key;
57282
- if ('strand' !== this.alignmentTrack.groupBy) {
57283
- this.alignmentTrack.groupBy = 'strand';
57284
- this.alignmentTrack.repackAlignments();
57285
- }
57286
57258
  this.trackView.repaintViews();
57287
57259
  }
57288
57260
 
@@ -67871,6 +67843,7 @@
67871
67843
  const searchConfig = browser.searchConfig || DEFAULT_SEARCH_CONFIG;
67872
67844
  let feature;
67873
67845
 
67846
+ name = name.toUpperCase();
67874
67847
  const searchableTracks = browser.tracks.filter(t => t.searchable);
67875
67848
  for (let track of searchableTracks) {
67876
67849
  const feature = await track.search(name);
@@ -70209,6 +70182,343 @@
70209
70182
  }
70210
70183
  }
70211
70184
 
70185
+ /*
70186
+ * The MIT License (MIT)
70187
+ *
70188
+ * Copyright (c) 2016-2017 The Regents of the University of California
70189
+ *
70190
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
70191
+ * associated documentation files (the "Software"), to deal in the Software without restriction, including
70192
+ * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
70193
+ * copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
70194
+ * following conditions:
70195
+ *
70196
+ * The above copyright notice and this permission notice shall be included in all copies or substantial
70197
+ * portions of the Software.
70198
+ *
70199
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
70200
+ * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
70201
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
70202
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
70203
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
70204
+ * THE SOFTWARE.
70205
+ *
70206
+ */
70207
+
70208
+ const defaultColorScaleConfig = {min: 0, max: 3000, color: "rgb(0,0,255)"};
70209
+
70210
+ class ShoeboxColorScale {
70211
+
70212
+ constructor(scale) {
70213
+
70214
+ scale = scale || defaultColorScaleConfig;
70215
+ this.max = scale.max;
70216
+ this.min = scale.min || 0;
70217
+ this.cache = [];
70218
+ this.nbins = 1000;
70219
+ this.binsize = (this.max - this.min) / this.nbins;
70220
+ this.updateColor(scale.color || "rgb(0,0,255)");
70221
+
70222
+ }
70223
+
70224
+ updateColor(color) {
70225
+ const comps = color.substring(4).replace(")", "").split(",");
70226
+ if (comps.length === 3) {
70227
+ this.r = Number.parseInt(comps[0].trim());
70228
+ this.g = Number.parseInt(comps[1].trim());
70229
+ this.b = Number.parseInt(comps[2].trim());
70230
+ }
70231
+ this.cache = [];
70232
+ }
70233
+
70234
+ setMinMax(min, max) {
70235
+ this.min = min;
70236
+ this.max = max;
70237
+ this.cache = [];
70238
+ this.binsize = (this.max - this.min) / this.nbins;
70239
+ }
70240
+
70241
+ getColor(value) {
70242
+ const low = 0;
70243
+ if (value < this.min) return "white"
70244
+
70245
+ const bin = Math.floor((Math.min(this.max, value) - this.min) / this.binsize);
70246
+ if (undefined === this.cache[bin]) {
70247
+ const alpha = (IGVMath.clamp(value, low, this.max) - low) / (this.max - low);
70248
+ this.cache[bin] = `rgba(${this.r},${this.g},${this.b}, ${alpha})`;
70249
+ }
70250
+ return this.cache[bin]
70251
+ }
70252
+
70253
+ /**
70254
+ *
70255
+ * @returns {{min: (*|number), color: string, max}}
70256
+ */
70257
+ toJson() {
70258
+ return {
70259
+ min: this.min,
70260
+ max: this.max,
70261
+ color: `rgb(${this.r},${this.g},${this.b})`
70262
+ }
70263
+ }
70264
+
70265
+ // For short-term backward compatibility
70266
+ static parse(str) {
70267
+
70268
+ const tokens = str.split(",");
70269
+
70270
+ const cs = {
70271
+ min: Number.parseFloat(tokens[0]),
70272
+ max: Number.parseFloat(tokens[1]),
70273
+ color: `${tokens[2]},${tokens[3]},${tokens[4]}`
70274
+ };
70275
+ return new ShoeboxColorScale(cs)
70276
+ }
70277
+
70278
+ }
70279
+
70280
+ class ShoeboxTrack extends TrackBase {
70281
+
70282
+ static defaults = {
70283
+ height: 300,
70284
+ rowHeight: 3,
70285
+ max: 3000,
70286
+ visibilityWindow: 10000
70287
+ }
70288
+
70289
+ constructor(config, browser) {
70290
+ super(config, browser);
70291
+ }
70292
+
70293
+ init(config) {
70294
+ super.init(config);
70295
+
70296
+ this.type = "shoebox";
70297
+ this.height = config.height || 300;
70298
+ this.rowHeight = config.rowHeight || 3;
70299
+ this.max = config.max || 3000;
70300
+
70301
+ // Hardcoded -- todo get from track line
70302
+ this.sampleKeys = [];
70303
+ for (let i = 1; i <= 100; i++) {
70304
+ this.sampleKeys.push(i);
70305
+ }
70306
+
70307
+ // Create featureSource
70308
+ const configCopy = Object.assign({}, this.config);
70309
+ configCopy.format = 'shoebox'; // bit of a hack
70310
+ this.featureSource = FeatureSource(configCopy, this.browser.genome);
70311
+ }
70312
+
70313
+ async postInit() {
70314
+ if (typeof this.featureSource.getHeader === "function") {
70315
+ this.header = await this.featureSource.getHeader();
70316
+ if (this.disposed) return // This track was removed during async load
70317
+ }
70318
+ // Set properties from track line
70319
+ if (this.header) {
70320
+ this.setTrackProperties(this.header);
70321
+ }
70322
+
70323
+ // Must do the following after setting track properties as they can be overriden via a track line
70324
+
70325
+ // Color settings
70326
+ if (this.config.colorScale && this.config.colorScale.max && this.config.colorScale.color) { // Minimal validation
70327
+ this.colorScale = new ShoeboxColorScale(this.config.colorScale);
70328
+
70329
+ } else {
70330
+ const min = this.dataRange.min;
70331
+ const max = this.dataRange.max;
70332
+ this.colorScale = new ShoeboxColorScale({min, max, color: this.color});
70333
+ }
70334
+ }
70335
+
70336
+ get color() {
70337
+ return this._color || "rgb(0,0,255)"
70338
+ }
70339
+
70340
+ set color(color) {
70341
+ this._color = color;
70342
+ if (this.colorScale) {
70343
+ this.colorScale.updateColor(color);
70344
+ }
70345
+ }
70346
+
70347
+ menuItemList() {
70348
+
70349
+ const menuItems = [];
70350
+
70351
+
70352
+ menuItems.push('<hr/>');
70353
+
70354
+ // Data range
70355
+ let object = $$1('<div>');
70356
+ object.text('Set data range');
70357
+
70358
+ function dialogPresentationHandler() {
70359
+
70360
+ if (this.trackView.track.selected) {
70361
+ this.browser.dataRangeDialog.configure(this.trackView.browser.getSelectedTrackViews());
70362
+ } else {
70363
+ this.browser.dataRangeDialog.configure(this.trackView);
70364
+ }
70365
+ this.browser.dataRangeDialog.present($$1(this.browser.columnContainer));
70366
+ }
70367
+
70368
+ menuItems.push({object, dialog: dialogPresentationHandler});
70369
+
70370
+ return menuItems
70371
+ }
70372
+
70373
+ setDataRange({min, max}) {
70374
+ this.dataRange.min = min;
70375
+ this.dataRange.max = max;
70376
+ this.colorScale.min = min;
70377
+ this.colorScale.max = max;
70378
+ this.trackView.repaintViews();
70379
+ }
70380
+
70381
+ hasSamples() {
70382
+ return true // by definition
70383
+ }
70384
+
70385
+ getSamples() {
70386
+ return {
70387
+ names: this.sampleKeys,
70388
+ height: this.rowHeight,
70389
+ yOffset: 0
70390
+ }
70391
+ }
70392
+
70393
+ async getFeatures(chr, start, end, bpPerPixel) {
70394
+ const visibilityWindow = this.visibilityWindow;
70395
+ return this.featureSource.getFeatures({chr, start, end, bpPerPixel, visibilityWindow})
70396
+ }
70397
+
70398
+
70399
+ draw({context, pixelTop, pixelWidth, pixelHeight, features, bpPerPixel, bpStart}) {
70400
+
70401
+
70402
+ IGVGraphics.fillRect(context, 0, pixelTop, pixelWidth, pixelHeight, {'fillStyle': "rgb(255, 255, 255)"});
70403
+
70404
+ if (features && features.length > 0) {
70405
+
70406
+ const rowHeight = this.rowHeight;
70407
+ const pixelBottom = pixelTop + pixelHeight;
70408
+ const bpEnd = bpStart + pixelWidth * bpPerPixel + 1;
70409
+
70410
+ const h = rowHeight;
70411
+
70412
+ for (let f of features) {
70413
+
70414
+ // Test for overlap with in-view region
70415
+ if (f.end < bpStart || f.start > bpEnd) continue
70416
+
70417
+ // Pixel x values
70418
+ const xLeft = Math.round((f.start - bpStart) / bpPerPixel);
70419
+ const xRight = Math.round((f.end - bpStart) / bpPerPixel);
70420
+ const w = Math.max(1, xRight - xLeft);
70421
+
70422
+ // Loop through value array
70423
+ let row = 0;
70424
+
70425
+ for (let i = f.values.length - 1; i >= 0; i--) {
70426
+
70427
+ const v = f.values[i];
70428
+
70429
+ const y = row * rowHeight;
70430
+
70431
+
70432
+ const bottom = y + rowHeight;
70433
+
70434
+ if (bottom < pixelTop || y > pixelBottom) {
70435
+ continue
70436
+ }
70437
+
70438
+ const color = this.colorScale.getColor(v);
70439
+
70440
+ context.fillStyle = color;
70441
+
70442
+ context.fillRect(xLeft, y, w, h);
70443
+
70444
+ row++;
70445
+ }
70446
+ }
70447
+ }
70448
+
70449
+ }
70450
+
70451
+ /**
70452
+ * Optional method to compute pixel height to accomodate the list of features.
70453
+ *
70454
+ * @param features
70455
+ * @returns {number}
70456
+ */
70457
+ computePixelHeight(features) {
70458
+ if (!features || features.length === 0) return 0
70459
+ return features[0].values.length * this.rowHeight
70460
+ }
70461
+
70462
+
70463
+ clickedFeatures(clickState) {
70464
+
70465
+ const allFeatures = super.clickedFeatures(clickState);
70466
+ const y = clickState.y;
70467
+ return allFeatures.filter(function (feature) {
70468
+ const rect = feature.pixelRect;
70469
+ return rect && y >= rect.y && y <= (rect.y + rect.h)
70470
+ })
70471
+
70472
+ }
70473
+
70474
+ hoverText(clickState) {
70475
+ const features = this.clickedFeatures(clickState);
70476
+ if (features && features.length > 0) {
70477
+ return `${features[0].sample}: ${features[0].value}`
70478
+ }
70479
+ }
70480
+
70481
+ popupData(clickState, featureList) {
70482
+
70483
+ if (featureList === undefined) featureList = this.clickedFeatures(clickState);
70484
+
70485
+ const items = [];
70486
+
70487
+ for (let feature of featureList) {
70488
+
70489
+ // Double line divider between features
70490
+ if (items.length > 0) {
70491
+ items.push('<hr/>');
70492
+ items.push('<hr/>');
70493
+ }
70494
+
70495
+ // hack for whole genome features, which save the original feature as "_f"
70496
+ const f = feature._f || feature;
70497
+
70498
+ const data = (typeof f.popupData === 'function') ?
70499
+ f.popupData(this.type, this.browser.genome.id) :
70500
+ this.extractPopupData(f);
70501
+ Array.prototype.push.apply(items, data);
70502
+
70503
+ }
70504
+
70505
+ return items
70506
+ }
70507
+
70508
+ get supportsWholeGenome() {
70509
+ return false
70510
+ }
70511
+
70512
+ getState() {
70513
+
70514
+ const config = super.getState();
70515
+ config.colorScale = this.colorScale.toJson();
70516
+ return config
70517
+
70518
+ }
70519
+
70520
+ }
70521
+
70212
70522
  //import CNVPytorTrack from "./CNVpytor/cnvpytorTrack.js"
70213
70523
 
70214
70524
 
@@ -70220,7 +70530,7 @@
70220
70530
  ['seg', (config, browser) => new SegTrack(config, browser)],
70221
70531
  ['mut', (config, browser) => new SegTrack(config, browser)],
70222
70532
  ['maf', (config, browser) => new SegTrack(config, browser)],
70223
- ['shoebox', (config, browser) => new SegTrack(config, browser)],
70533
+ ['shoebox', (config, browser) => new ShoeboxTrack(config, browser)],
70224
70534
  ['wig', (config, browser) => new WigTrack(config, browser)],
70225
70535
  ['merged', (config, browser) => new MergedTrack(config, browser)],
70226
70536
  ['alignment', (config, browser) => new BAMTrack(config, browser)],
@@ -70712,94 +71022,11 @@
70712
71022
  })
70713
71023
  }
70714
71024
 
70715
- const _version = "3.0.8";
71025
+ const _version = "3.0.9";
70716
71026
  function version() {
70717
71027
  return _version
70718
71028
  }
70719
71029
 
70720
- /*
70721
- * The MIT License (MIT)
70722
- *
70723
- * Copyright (c) 2014 Broad Institute
70724
- *
70725
- * Permission is hereby granted, free of charge, to any person obtaining a copy
70726
- * of this software and associated documentation files (the "Software"), to deal
70727
- * in the Software without restriction, including without limitation the rights
70728
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
70729
- * copies of the Software, and to permit persons to whom the Software is
70730
- * furnished to do so, subject to the following conditions:
70731
- *
70732
- * The above copyright notice and this permission notice shall be included in
70733
- * all copies or substantial portions of the Software.
70734
- *
70735
- *
70736
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
70737
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
70738
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
70739
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
70740
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
70741
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
70742
- * THE SOFTWARE.
70743
- */
70744
-
70745
- const navbarResponsiveClasses = {};
70746
-
70747
- const responsiveThreshold = 8;
70748
- let textButtonContainerWidth = undefined;
70749
-
70750
- function navbarDidResize(browser, width) {
70751
-
70752
- const currentClass = NavbarButton.currentNavbarButtonClass(browser);
70753
- if ('igv-navbar-text-button' === currentClass) {
70754
- textButtonContainerWidth = browser.$navigation.get(0).querySelector('.igv-navbar-right-container').getBoundingClientRect().width;
70755
- }
70756
-
70757
- const responsiveClasses = getResponsiveClasses(browser, width);
70758
-
70759
- $$1(browser.zoomWidget.zoomContainer).removeClass();
70760
- $$1(browser.zoomWidget.zoomContainer).addClass(responsiveClasses.zoomContainer);
70761
-
70762
- browser.fireEvent('navbar-resize', [ responsiveClasses.navbarButton ]);
70763
- }
70764
-
70765
- function getResponsiveClasses(browser, navbarWidth) {
70766
-
70767
- const isWGV =
70768
- (browser.isMultiLocusWholeGenomeView()) ||
70769
- (browser.referenceFrameList && GenomeUtils.isWholeGenomeView(browser.referenceFrameList[0].chr));
70770
-
70771
- isWGV ? browser.windowSizePanel.hide() : browser.windowSizePanel.show();
70772
-
70773
- const { x: leftContainerX, width: leftContainerWidth } = browser.$navigation.get(0).querySelector('.igv-navbar-left-container').getBoundingClientRect();
70774
- const leftContainerExtent = leftContainerX + leftContainerWidth;
70775
- const { x:rightContainerX} = browser.$navigation.get(0).querySelector('.igv-navbar-right-container').getBoundingClientRect();
70776
-
70777
- const delta = rightContainerX - leftContainerExtent;
70778
-
70779
- const currentClass = NavbarButton.currentNavbarButtonClass(browser);
70780
-
70781
- // console.log(`Current class ${ currentClass } Delta: ${ StringUtils.numberFormatter(Math.floor(delta))}`)
70782
-
70783
- if ('igv-navbar-text-button' === currentClass && delta < responsiveThreshold) {
70784
- navbarResponsiveClasses.navbarButton = 'igv-navbar-icon-button';
70785
- } else if (textButtonContainerWidth && 'igv-navbar-icon-button' === currentClass) {
70786
- const length = navbarWidth - leftContainerExtent;
70787
- if (length - textButtonContainerWidth > responsiveThreshold) {
70788
- navbarResponsiveClasses.navbarButton = 'igv-navbar-text-button';
70789
- }
70790
-
70791
- }
70792
-
70793
-
70794
- if (isWGV) {
70795
- navbarResponsiveClasses.zoomContainer = 'igv-zoom-widget-hidden';
70796
- } else {
70797
- navbarResponsiveClasses.zoomContainer = navbarWidth > 860 ? 'igv-zoom-widget' : 'igv-zoom-widget-900';
70798
- }
70799
-
70800
- return navbarResponsiveClasses
70801
- }
70802
-
70803
71030
  /*
70804
71031
  * The MIT License (MIT)
70805
71032
  *
@@ -70847,7 +71074,7 @@
70847
71074
  });
70848
71075
 
70849
71076
  this.showAllChromosomes = browser.config.showAllChromosomes !== false; // i.e. default to true
70850
-
71077
+ this.genome = browser.genome;
70851
71078
  }
70852
71079
 
70853
71080
  show() {
@@ -70858,10 +71085,16 @@
70858
71085
  this.container.style.display = 'none';
70859
71086
  }
70860
71087
 
71088
+ setValue(chrName) {
71089
+ this.select.value = this.genome.getChromosomeDisplayName(chrName);
71090
+ }
71091
+
70861
71092
  update(genome) {
70862
71093
 
71094
+ this.genome = genome;
71095
+
70863
71096
  // Start with explicit chromosome name list
70864
- const list = genome.wgChromosomeNames || [];
71097
+ const list = genome.wgChromosomeNames.map(nm => genome.getChromosomeDisplayName(nm)) || [];
70865
71098
 
70866
71099
  if (this.showAllChromosomes && genome.chromosomeNames.length > 1) {
70867
71100
  const exclude = new Set(list);
@@ -70872,6 +71105,7 @@
70872
71105
  break
70873
71106
  }
70874
71107
  if (!exclude.has(nm)) {
71108
+ nm = genome.getChromosomeDisplayName(nm);
70875
71109
  list.push(nm);
70876
71110
  }
70877
71111
  }
@@ -70952,6 +71186,99 @@
70952
71186
  }
70953
71187
  }
70954
71188
 
71189
+ const multiSelectImage =
71190
+ `<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">
71191
+ <title>multi select</title>
71192
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
71193
+ <g id="multi-select">
71194
+ <rect id="backdrop-copy-3" stroke="#737373" stroke-width="12" fill="#FFFFFF" x="6" y="6" width="613" height="613" rx="135"></rect>
71195
+ <g id="row-copy-3" transform="translate(81, 427)" fill="#737373">
71196
+ <rect id="Rectangle" x="134" y="0" width="329" height="70"></rect>
71197
+ <rect id="Rectangle-Copy-16" stroke="#737373" stroke-width="12" x="6" y="6" width="58" height="58"></rect>
71198
+ </g>
71199
+ <g id="row-copy-2" transform="translate(82, 277)">
71200
+ <rect id="Rectangle" fill-opacity="0.33" fill="#CFCECE" x="133" y="0" width="329" height="70"></rect>
71201
+ <rect id="Rectangle-Copy-16" stroke-opacity="0.32659528" stroke="#CFCECE" stroke-width="12" x="6" y="6" width="58" height="58"></rect>
71202
+ </g>
71203
+ <g id="row-copy" transform="translate(81, 119)" fill="#737373">
71204
+ <rect id="Rectangle" x="134" y="0" width="329" height="70"></rect>
71205
+ <rect id="Rectangle-Copy-17" stroke="#737373" stroke-width="12" x="6" y="6" width="58" height="58"></rect>
71206
+ </g>
71207
+ </g>
71208
+ </g>
71209
+ </svg>`;
71210
+
71211
+ const multiSelectImageHover =
71212
+ `<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">
71213
+ <title>multi select hover</title>
71214
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
71215
+ <g id="multi-select-hover">
71216
+ <rect id="backdrop-copy-4" stroke="#737373" stroke-width="12" fill="#737373" x="6" y="6" width="613" height="613" rx="135"></rect>
71217
+ <g id="row-copy-3" transform="translate(81, 427)" fill="#FFFFFF">
71218
+ <rect id="Rectangle" x="134" y="0" width="329" height="70"></rect>
71219
+ <rect id="Rectangle-Copy-16" stroke="#FFFFFF" stroke-width="12" x="6" y="6" width="58" height="58"></rect>
71220
+ </g>
71221
+ <g id="row-copy-2" transform="translate(82, 277)">
71222
+ <rect id="Rectangle" fill-opacity="0.33" fill="#CFCECE" x="133" y="0" width="329" height="70"></rect>
71223
+ <rect id="Rectangle-Copy-16" stroke-opacity="0.33" stroke="#CFCECE" stroke-width="12" x="6" y="6" width="58" height="58"></rect>
71224
+ </g>
71225
+ <g id="row-copy" transform="translate(81, 119)" fill="#FFFFFF">
71226
+ <rect id="Rectangle" x="134" y="0" width="329" height="70"></rect>
71227
+ <rect id="Rectangle-Copy-17" stroke="#FFFFFF" stroke-width="12" x="6" y="6" width="58" height="58"></rect>
71228
+ </g>
71229
+ </g>
71230
+ </g>
71231
+ </svg>`;
71232
+
71233
+ class MultiTrackSelectButton extends NavbarButton {
71234
+
71235
+ constructor(parent, browser, navbar, enableMultiTrackSelection) {
71236
+
71237
+ super(parent, browser, 'Select Tracks', buttonLabel, multiSelectImage, multiSelectImageHover, false);
71238
+
71239
+ this.navbar = navbar;
71240
+ this.enableMultiTrackSelection = false; // Initial state
71241
+ this.button.addEventListener('mouseenter', event => {
71242
+ if (false === enableMultiTrackSelection) {
71243
+ this.setState(true);
71244
+ }
71245
+ });
71246
+
71247
+ this.button.addEventListener('mouseleave', event => {
71248
+ if (false === enableMultiTrackSelection) {
71249
+ this.setState(false);
71250
+ }
71251
+ });
71252
+
71253
+ const mouseClickHandler = () => {
71254
+ // Toggle the selection state
71255
+ this.setMultiTrackSelection(!this.enableMultiTrackSelection);
71256
+ };
71257
+
71258
+ this.boundMouseClickHandler = mouseClickHandler.bind(this);
71259
+
71260
+ this.button.addEventListener('click', this.boundMouseClickHandler);
71261
+
71262
+ }
71263
+
71264
+ setMultiTrackSelection(enableMultiTrackSelection) {
71265
+
71266
+ this.enableMultiTrackSelection = enableMultiTrackSelection;
71267
+ this.setState(this.enableMultiTrackSelection);
71268
+
71269
+ // If enableMultiTrackSelection is false hide the Overly button
71270
+ if (false === this.enableMultiTrackSelection) {
71271
+ this.navbar.overlayTrackButton.setVisibility(false);
71272
+ }
71273
+
71274
+ for (const trackView of this.browser.trackViews) {
71275
+ trackView.enableTrackSelection(enableMultiTrackSelection);
71276
+ }
71277
+
71278
+ }
71279
+
71280
+ }
71281
+
70955
71282
  class CursorGuide {
70956
71283
 
70957
71284
  constructor(columnContainer, browser) {
@@ -71133,9 +71460,9 @@
71133
71460
 
71134
71461
  class CursorGuideButton extends NavbarButton {
71135
71462
 
71136
- constructor(browser, parent) {
71463
+ constructor(parent, browser) {
71137
71464
 
71138
- super(browser, parent, 'Crosshairs', buttonLabel, cursorImage, cursorImageHover, browser.doShowCursorGuide);
71465
+ super(parent, browser, 'Crosshairs', buttonLabel, cursorImage, cursorImageHover, browser.doShowCursorGuide);
71139
71466
 
71140
71467
  this.button.addEventListener('mouseenter', () => {
71141
71468
  if (false === browser.doShowCursorGuide) {
@@ -71221,9 +71548,9 @@
71221
71548
 
71222
71549
  class CenterLineButton extends NavbarButton {
71223
71550
 
71224
- constructor(browser, parent) {
71551
+ constructor(parent, browser) {
71225
71552
 
71226
- super(browser, parent, 'Center Line', buttonLabel, centerlineImage, centerlineImageHover, browser.config.showCenterGuide);
71553
+ super(parent, browser, 'Center Line', buttonLabel, centerlineImage, centerlineImageHover, browser.config.showCenterGuide);
71227
71554
 
71228
71555
  this.button.addEventListener('mouseenter', () => {
71229
71556
  if (false === browser.doShowCenterLine) {
@@ -71312,7 +71639,7 @@
71312
71639
 
71313
71640
  constructor(parent, browser) {
71314
71641
 
71315
- super(browser, parent, 'Track Labels', buttonLabel, trackLabelsImage, trackLabelsImageHover, browser.config.showTrackLabels);
71642
+ super(parent, browser, 'Track Labels', buttonLabel, trackLabelsImage, trackLabelsImageHover, browser.config.showTrackLabels);
71316
71643
 
71317
71644
  this.button.addEventListener('mouseenter', () => {
71318
71645
  if (false === browser.doShowTrackLabels) {
@@ -71342,28 +71669,28 @@
71342
71669
 
71343
71670
  }
71344
71671
 
71345
- const sampleNameImage =
71672
+ const roiImage =
71346
71673
  `<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">
71347
- <title>sample names</title>
71674
+ <title>roi</title>
71348
71675
  <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
71349
- <g id="sample-names" stroke="#737373">
71350
- <rect id="Rectangle-Copy-13" stroke-width="12" fill="#FFFFFF" x="6" y="6" width="613" height="613" rx="135"></rect>
71351
- <line x1="80" y1="465" x2="541" y2="464.5" id="Line-3-Copy-3" stroke-width="32"></line>
71352
- <line x1="80" y1="312.5" x2="542" y2="313" id="Line-3" stroke-width="32"></line>
71353
- <line x1="80" y1="158" x2="541" y2="158" id="Line-3-Copy" stroke-width="32"></line>
71676
+ <g id="roi">
71677
+ <rect id="Rectangle-Copy-23" stroke="#737373" stroke-width="12" fill="#FFFFFF" x="6" y="6" width="613" height="613" rx="135"></rect>
71678
+ <text id="ROI" font-family="HelveticaNeue-Bold, Helvetica Neue" font-size="258" font-weight="bold" fill="#737373">
71679
+ <tspan x="81.445" y="389">ROI</tspan>
71680
+ </text>
71354
71681
  </g>
71355
71682
  </g>
71356
71683
  </svg>`;
71357
71684
 
71358
- const sampleNameImageHover =
71685
+ const roiImageHover =
71359
71686
  `<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">
71360
- <title>sample names hover</title>
71687
+ <title>roi hover</title>
71361
71688
  <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
71362
- <g id="sample-names-hover">
71363
- <rect id="Rectangle-Copy-18" stroke="#737373" stroke-width="12" fill="#737373" x="6" y="6" width="613" height="613" rx="135"></rect>
71364
- <line x1="80" y1="465" x2="541" y2="464.5" id="Line-3-Copy-3" stroke="#FFFFFF" stroke-width="32" fill="#FFFFFF"></line>
71365
- <line x1="80" y1="312.5" x2="542" y2="313" id="Line-3" stroke="#FFFFFF" stroke-width="32" fill="#FFFFFF"></line>
71366
- <line x1="80" y1="158" x2="541" y2="158" id="Line-3-Copy" stroke="#FFFFFF" stroke-width="32" fill="#FFFFFF"></line>
71689
+ <g id="roi-hover">
71690
+ <rect id="Rectangle-Copy-24" stroke="#737373" stroke-width="12" fill="#737373" x="6" y="6" width="613" height="613" rx="135"></rect>
71691
+ <text id="ROI" font-family="HelveticaNeue-Bold, Helvetica Neue" font-size="258" font-weight="bold" fill="#FFFFFF">
71692
+ <tspan x="81.445" y="389">ROI</tspan>
71693
+ </text>
71367
71694
  </g>
71368
71695
  </g>
71369
71696
  </svg>`;
@@ -71394,49 +71721,34 @@
71394
71721
  * THE SOFTWARE.
71395
71722
  */
71396
71723
 
71397
- class SampleNameControl extends NavbarButton {
71724
+ class ROITableControl extends NavbarButton {
71398
71725
 
71399
- constructor(parent, browser) {
71726
+ constructor(parent, browser) {
71400
71727
 
71401
- super(browser, parent, 'Sample Names', sampleNameButtonLabel, sampleNameImage, sampleNameImageHover, browser.config.showSampleNames);
71728
+ super(parent, browser, ['ROI', 'Regions of Interest Table'], buttonLabel, roiImage, roiImageHover, false);
71402
71729
 
71403
71730
  this.button.addEventListener('mouseenter', () => {
71404
- if (false === browser.showSampleNames) {
71731
+ if (false === browser.doShowROITable) {
71405
71732
  this.setState(true);
71406
71733
  }
71407
71734
  });
71408
71735
 
71409
71736
  this.button.addEventListener('mouseleave', () => {
71410
- if (false === browser.showSampleNames) {
71737
+ if (false === browser.doShowROITable) {
71411
71738
  this.setState(false);
71412
71739
  }
71413
71740
  });
71414
71741
 
71415
- this.button.addEventListener('click', () => {
71416
- this.performClickWithState(browser, undefined);
71417
- });
71742
+ this.button.addEventListener('click', () => this.buttonHandler(!browser.doShowROITable));
71418
71743
 
71419
- if (true === browser.config.showSampleNameButton) {
71420
- this.show();
71421
- } else {
71422
- this.hide();
71423
- }
71744
+ this.setVisibility(false); // Hide initially, it will be un-hidden if ROIs are loaded
71424
71745
 
71425
71746
  }
71426
71747
 
71427
- performClickWithState(browser, doShowSampleNamesOrUndefined) {
71428
-
71429
- browser.showSampleNames = undefined === doShowSampleNamesOrUndefined ? !browser.showSampleNames : doShowSampleNamesOrUndefined;
71430
-
71431
- const column = browser.columnContainer.querySelector('.igv-sample-name-column');
71432
- column.style.display = false === browser.showSampleNames ? 'none' : 'flex';
71433
-
71434
- this.setState(browser.showSampleNames);
71435
-
71436
- browser.layoutChange();
71437
-
71748
+ buttonHandler(status) {
71749
+ this.setState(status);
71750
+ this.browser.setROITableVisibility(status);
71438
71751
  }
71439
-
71440
71752
  }
71441
71753
 
71442
71754
  const sampleInfoImage =
@@ -71540,7 +71852,7 @@
71540
71852
 
71541
71853
  constructor(parent, browser) {
71542
71854
 
71543
- super(browser, parent, 'Sample Info', buttonLabel, sampleInfoImage, sampleInfoImageHover, false);
71855
+ super(parent, browser, 'Sample Info', buttonLabel, sampleInfoImage, sampleInfoImageHover, false);
71544
71856
 
71545
71857
  this.showSampleInfo = false;
71546
71858
 
@@ -71590,141 +71902,102 @@
71590
71902
 
71591
71903
  }
71592
71904
 
71593
- const sliderMin = 0;
71594
- let sliderMax = 23;
71595
- let sliderValueRaw = 0;
71596
-
71597
- const ZoomWidget = function (browser, parent) {
71598
-
71599
- this.browser = browser;
71600
-
71601
- this.zoomContainer = div({class: 'igv-zoom-widget'});
71602
- parent.appendChild(this.zoomContainer);
71603
-
71604
- // zoom out
71605
- this.zoomOutButton = div();
71606
- this.zoomContainer.appendChild(this.zoomOutButton);
71607
- this.zoomOutButton.appendChild(createIcon('minus-circle'));
71608
- this.zoomOutButton.addEventListener('click', () => {
71609
- // browser.zoomWithScaleFactor(2.0)
71610
- browser.zoomOut();
71611
- });
71612
-
71613
- // Range slider
71614
- const el = div();
71615
- this.zoomContainer.appendChild(el);
71616
- this.slider = document.createElement('input');
71617
- this.slider.type = 'range';
71618
-
71619
- this.slider.min = `${sliderMin}`;
71620
- this.slider.max = `${sliderMax}`;
71621
-
71622
- el.appendChild(this.slider);
71623
-
71624
- this.slider.addEventListener('change', e => {
71625
-
71626
- e.preventDefault();
71627
- e.stopPropagation();
71628
-
71629
- const referenceFrame = browser.referenceFrameList[0];
71630
- const {bpLength} = referenceFrame.genome.getChromosome(referenceFrame.chr);
71631
- const {end, start} = referenceFrame;
71905
+ const sampleNameImage =
71906
+ `<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">
71907
+ <title>sample names</title>
71908
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
71909
+ <g id="sample-names" stroke="#737373">
71910
+ <rect id="Rectangle-Copy-13" stroke-width="12" fill="#FFFFFF" x="6" y="6" width="613" height="613" rx="135"></rect>
71911
+ <line x1="80" y1="465" x2="541" y2="464.5" id="Line-3-Copy-3" stroke-width="32"></line>
71912
+ <line x1="80" y1="312.5" x2="542" y2="313" id="Line-3" stroke-width="32"></line>
71913
+ <line x1="80" y1="158" x2="541" y2="158" id="Line-3-Copy" stroke-width="32"></line>
71914
+ </g>
71915
+ </g>
71916
+ </svg>`;
71632
71917
 
71633
- const extent = end - start;
71918
+ const sampleNameImageHover =
71919
+ `<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">
71920
+ <title>sample names hover</title>
71921
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
71922
+ <g id="sample-names-hover">
71923
+ <rect id="Rectangle-Copy-18" stroke="#737373" stroke-width="12" fill="#737373" x="6" y="6" width="613" height="613" rx="135"></rect>
71924
+ <line x1="80" y1="465" x2="541" y2="464.5" id="Line-3-Copy-3" stroke="#FFFFFF" stroke-width="32" fill="#FFFFFF"></line>
71925
+ <line x1="80" y1="312.5" x2="542" y2="313" id="Line-3" stroke="#FFFFFF" stroke-width="32" fill="#FFFFFF"></line>
71926
+ <line x1="80" y1="158" x2="541" y2="158" id="Line-3-Copy" stroke="#FFFFFF" stroke-width="32" fill="#FFFFFF"></line>
71927
+ </g>
71928
+ </g>
71929
+ </svg>`;
71634
71930
 
71635
- // bpLength/(end - start)
71636
- const scaleFactor = Math.pow(2, e.target.valueAsNumber);
71931
+ /*
71932
+ * The MIT License (MIT)
71933
+ *
71934
+ * Copyright (c) 2016 University of California San Diego
71935
+ * Author: Jim Robinson
71936
+ *
71937
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
71938
+ * of this software and associated documentation files (the "Software"), to deal
71939
+ * in the Software without restriction, including without limitation the rights
71940
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
71941
+ * copies of the Software, and to permit persons to whom the Software is
71942
+ * furnished to do so, subject to the following conditions:
71943
+ *
71944
+ * The above copyright notice and this permission notice shall be included in
71945
+ * all copies or substantial portions of the Software.
71946
+ *
71947
+ *
71948
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
71949
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
71950
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
71951
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
71952
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
71953
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
71954
+ * THE SOFTWARE.
71955
+ */
71637
71956
 
71638
- // (end - start) = bpLength/scaleFactor
71639
- const zoomedExtent = bpLength / scaleFactor;
71957
+ class SampleNameControl extends NavbarButton {
71640
71958
 
71641
- // console.log(`zoom-widget - slider ${ e.target.value } scaleFactor ${ scaleFactor } extent-zoomed ${ StringUtils.numberFormatter(Math.round(zoomedExtent)) }`)
71959
+ constructor(parent, browser) {
71642
71960
 
71643
- browser.zoomWithScaleFactor(zoomedExtent / extent);
71961
+ super(parent, browser, 'Sample Names', sampleNameButtonLabel, sampleNameImage, sampleNameImageHover, browser.config.showSampleNames);
71644
71962
 
71645
- });
71963
+ this.button.addEventListener('mouseenter', () => {
71964
+ if (false === browser.showSampleNames) {
71965
+ this.setState(true);
71966
+ }
71967
+ });
71646
71968
 
71647
- // zoom in
71648
- this.zoomInButton = div();
71649
- this.zoomContainer.appendChild(this.zoomInButton);
71650
- this.zoomInButton.appendChild(createIcon('plus-circle'));
71651
- this.zoomInButton.addEventListener('click', () => {
71652
- // browser.zoomWithScaleFactor(0.5)
71653
- browser.zoomIn();
71654
- });
71969
+ this.button.addEventListener('mouseleave', () => {
71970
+ if (false === browser.showSampleNames) {
71971
+ this.setState(false);
71972
+ }
71973
+ });
71655
71974
 
71656
- browser.on('locuschange', (referenceFrameList) => {
71975
+ this.button.addEventListener('click', () => {
71976
+ this.performClickWithState(browser, undefined);
71977
+ });
71657
71978
 
71658
- if (this.browser.isMultiLocusMode()) {
71659
- this.disable();
71979
+ if (true === browser.config.showSampleNameButton) {
71980
+ this.show();
71660
71981
  } else {
71661
- this.enable();
71662
- this.update(referenceFrameList);
71982
+ this.hide();
71663
71983
  }
71664
71984
 
71665
- });
71666
-
71667
- };
71668
-
71669
- ZoomWidget.prototype.update = function (referenceFrameList) {
71670
-
71671
- const referenceFrame = referenceFrameList[0];
71672
- const {bpLength} = referenceFrame.genome.getChromosome(referenceFrame.chr);
71673
- const {start, end} = referenceFrame;
71674
-
71675
- sliderMax = Math.ceil(Math.log2(bpLength / this.browser.minimumBases()));
71676
-
71677
- this.slider.max = `${sliderMax}`;
71678
-
71679
- const scaleFactor = bpLength / (end - start);
71680
- sliderValueRaw = Math.log2(scaleFactor);
71681
- this.slider.value = `${Math.round(sliderValueRaw)}`;
71682
-
71683
- // referenceFrame.description('zoom.update')
71684
-
71685
- // console.log(`${ Date.now() } update - slider ${ this.slider.value } scaleFactor ${ Math.round(scaleFactor) } extent ${ StringUtils.numberFormatter(Math.round(extent)) }`)
71686
-
71687
- // console.log(`update - sliderMin ${ sliderMin } sliderValue ${ this.slider.value } sliderMax ${ sliderMax } scaleFactor ${ scaleFactor.toFixed(3) } derived-scaleFactor ${ derivedScalefactor.toFixed(3) }`)
71688
-
71689
- };
71690
-
71691
- ZoomWidget.prototype.enable = function () {
71692
-
71693
- // this.zoomInButton.style.color = appleCrayonPalette[ 'steel' ];
71694
- // this.zoomInButton.style.pointerEvents = 'auto';
71695
- //
71696
- // this.zoomOutButton.style.color = appleCrayonPalette[ 'steel' ];
71697
- // this.zoomOutButton.style.pointerEvents = 'auto';
71698
-
71699
- this.slider.disabled = false;
71700
- };
71985
+ }
71701
71986
 
71702
- ZoomWidget.prototype.disable = function () {
71987
+ performClickWithState(browser, doShowSampleNamesOrUndefined) {
71703
71988
 
71704
- // this.zoomInButton.style.color = appleCrayonPalette[ 'silver' ];
71705
- // this.zoomInButton.style.pointerEvents = 'none';
71706
- //
71707
- // this.zoomOutButton.style.color = appleCrayonPalette[ 'silver' ];
71708
- // this.zoomOutButton.style.pointerEvents = 'none';
71989
+ browser.showSampleNames = undefined === doShowSampleNamesOrUndefined ? !browser.showSampleNames : doShowSampleNamesOrUndefined;
71709
71990
 
71710
- this.slider.disabled = true;
71711
- };
71991
+ const column = browser.columnContainer.querySelector('.igv-sample-name-column');
71992
+ column.style.display = false === browser.showSampleNames ? 'none' : 'flex';
71712
71993
 
71713
- ZoomWidget.prototype.hide = function () {
71714
- this.zoomContainer.style.display = 'none';
71715
- };
71994
+ this.setState(browser.showSampleNames);
71716
71995
 
71717
- ZoomWidget.prototype.show = function () {
71718
- this.zoomContainer.style.display = 'block';
71719
- };
71996
+ browser.layoutChange();
71720
71997
 
71721
- ZoomWidget.prototype.hideSlider = function () {
71722
- this.slider.style.display = 'none';
71723
- };
71998
+ }
71724
71999
 
71725
- ZoomWidget.prototype.showSlider = function () {
71726
- this.slider.style.display = 'block';
71727
- };
72000
+ }
71728
72001
 
71729
72002
  class Dropdown {
71730
72003
  constructor(parent, shim) {
@@ -71867,7 +72140,7 @@
71867
72140
  class SaveImageControl extends NavbarButton {
71868
72141
  constructor(parent, browser) {
71869
72142
 
71870
- super(browser, parent, 'Save Image', buttonLabel, imageSaveImageSVG, imageSaveImageHoverSVG, false);
72143
+ super(parent, browser, 'Save Image', buttonLabel, imageSaveImageSVG, imageSaveImageHoverSVG, false);
71871
72144
 
71872
72145
  this.button.addEventListener('mouseenter', () => this.setState(true));
71873
72146
 
@@ -71933,6 +72206,422 @@
71933
72206
 
71934
72207
  }
71935
72208
 
72209
+ /**
72210
+ * User supplied button for the navbar
72211
+ */
72212
+
72213
+ const CustomButton = function (parent, browser, b) {
72214
+
72215
+ const button = div({class: 'igv-navbar-button'});
72216
+ parent.append(button);
72217
+ button.textContent = b.label;
72218
+ button.addEventListener('click', () => b.callback(browser));
72219
+ };
72220
+
72221
+ const sliderMin = 0;
72222
+ let sliderMax = 23;
72223
+ let sliderValueRaw = 0;
72224
+
72225
+ class ZoomWidget {
72226
+ constructor(config, browser, parent) {
72227
+
72228
+ this.browser = browser;
72229
+
72230
+ this.zoomContainer = div({class: 'igv-zoom-widget'});
72231
+ parent.appendChild(this.zoomContainer);
72232
+
72233
+ // zoom out
72234
+ this.zoomOutButton = div();
72235
+ this.zoomContainer.appendChild(this.zoomOutButton);
72236
+ this.zoomOutButton.appendChild(createIcon('minus-circle'));
72237
+ this.zoomOutButton.addEventListener('click', () => {
72238
+ // browser.zoomWithScaleFactor(2.0)
72239
+ browser.zoomOut();
72240
+ });
72241
+
72242
+ // Range slider
72243
+ const el = div();
72244
+ this.zoomContainer.appendChild(el);
72245
+ this.slider = document.createElement('input');
72246
+ this.slider.type = 'range';
72247
+
72248
+ this.slider.min = `${sliderMin}`;
72249
+ this.slider.max = `${sliderMax}`;
72250
+
72251
+ el.appendChild(this.slider);
72252
+
72253
+ this.slider.addEventListener('change', e => {
72254
+
72255
+ e.preventDefault();
72256
+ e.stopPropagation();
72257
+
72258
+ const referenceFrame = browser.referenceFrameList[0];
72259
+ const {bpLength} = referenceFrame.genome.getChromosome(referenceFrame.chr);
72260
+ const {end, start} = referenceFrame;
72261
+
72262
+ const extent = end - start;
72263
+
72264
+ // bpLength/(end - start)
72265
+ const scaleFactor = Math.pow(2, e.target.valueAsNumber);
72266
+
72267
+ // (end - start) = bpLength/scaleFactor
72268
+ const zoomedExtent = bpLength / scaleFactor;
72269
+
72270
+ // console.log(`zoom-widget - slider ${ e.target.value } scaleFactor ${ scaleFactor } extent-zoomed ${ StringUtils.numberFormatter(Math.round(zoomedExtent)) }`)
72271
+
72272
+ browser.zoomWithScaleFactor(zoomedExtent / extent);
72273
+
72274
+ });
72275
+
72276
+ // zoom in
72277
+ this.zoomInButton = div();
72278
+ this.zoomContainer.appendChild(this.zoomInButton);
72279
+ this.zoomInButton.appendChild(createIcon('plus-circle'));
72280
+ this.zoomInButton.addEventListener('click', () => {
72281
+ // browser.zoomWithScaleFactor(0.5)
72282
+ browser.zoomIn();
72283
+ });
72284
+
72285
+ browser.on('locuschange', (referenceFrameList) => {
72286
+
72287
+ if (this.browser.isMultiLocusMode()) {
72288
+ this.disable();
72289
+ } else {
72290
+ this.enable();
72291
+ this.update(referenceFrameList);
72292
+ }
72293
+
72294
+ });
72295
+
72296
+ }
72297
+
72298
+ update(referenceFrameList) {
72299
+
72300
+ if (this.slider) {
72301
+ const referenceFrame = referenceFrameList[0];
72302
+ const {bpLength} = referenceFrame.genome.getChromosome(referenceFrame.chr);
72303
+ const {start, end} = referenceFrame;
72304
+
72305
+ sliderMax = Math.ceil(Math.log2(bpLength / this.browser.minimumBases()));
72306
+ this.slider.max = `${sliderMax}`;
72307
+
72308
+ const scaleFactor = bpLength / (end - start);
72309
+ sliderValueRaw = Math.log2(scaleFactor);
72310
+ this.slider.value = `${Math.round(sliderValueRaw)}`;
72311
+ }
72312
+ }
72313
+
72314
+
72315
+ enable() {
72316
+
72317
+ // this.zoomInButton.style.color = appleCrayonPalette[ 'steel' ];
72318
+ // this.zoomInButton.style.pointerEvents = 'auto';
72319
+ //
72320
+ // this.zoomOutButton.style.color = appleCrayonPalette[ 'steel' ];
72321
+ // this.zoomOutButton.style.pointerEvents = 'auto';
72322
+
72323
+ if (this.slider) this.slider.disabled = false;
72324
+ }
72325
+
72326
+ disable() {
72327
+
72328
+ // this.zoomInButton.style.color = appleCrayonPalette[ 'silver' ];
72329
+ // this.zoomInButton.style.pointerEvents = 'none';
72330
+ //
72331
+ // this.zoomOutButton.style.color = appleCrayonPalette[ 'silver' ];
72332
+ // this.zoomOutButton.style.pointerEvents = 'none';
72333
+
72334
+ if (this.slider) this.slider.disabled = true;
72335
+ }
72336
+
72337
+ hide() {
72338
+ this.zoomContainer.style.display = 'none';
72339
+ }
72340
+
72341
+ show() {
72342
+ this.zoomContainer.style.display = 'block';
72343
+ }
72344
+
72345
+ hideSlider() {
72346
+ if (this.slider) this.slider.style.display = 'none';
72347
+ }
72348
+
72349
+ showSlider() {
72350
+ if (this.slider) this.slider.style.display = 'block';
72351
+ }
72352
+ }
72353
+
72354
+ /*
72355
+ * The MIT License (MIT)
72356
+ *
72357
+ * Copyright (c) 2014 Broad Institute
72358
+ *
72359
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
72360
+ * of this software and associated documentation files (the "Software"), to deal
72361
+ * in the Software without restriction, including without limitation the rights
72362
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
72363
+ * copies of the Software, and to permit persons to whom the Software is
72364
+ * furnished to do so, subject to the following conditions:
72365
+ *
72366
+ * The above copyright notice and this permission notice shall be included in
72367
+ * all copies or substantial portions of the Software.
72368
+ *
72369
+ *
72370
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
72371
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
72372
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
72373
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
72374
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
72375
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
72376
+ * THE SOFTWARE.
72377
+ */
72378
+
72379
+ class ResponsiveNavbar {
72380
+ constructor(config, browser) {
72381
+
72382
+ this.browser = browser;
72383
+ this.config = config;
72384
+
72385
+ this.currentClass = 'igv-navbar-text-button';
72386
+
72387
+ // DOM element for
72388
+ const $navBar = $$1('<div>', {class: 'igv-navbar'});
72389
+ this.$navigation = $navBar;
72390
+
72391
+ const $navbarLeftContainer = $$1('<div>', {class: 'igv-navbar-left-container'});
72392
+ $navBar.append($navbarLeftContainer);
72393
+ this.navbarLeftContainer = $navbarLeftContainer.get(0);
72394
+
72395
+ // IGV logo
72396
+ const $logo = $$1('<div>', {class: 'igv-logo'});
72397
+ $navbarLeftContainer.append($logo);
72398
+
72399
+ const logoSvg = logo();
72400
+ logoSvg.css("width", "34px");
72401
+ logoSvg.css("height", "32px");
72402
+ $logo.append(logoSvg);
72403
+
72404
+ this.$current_genome = $$1('<div>', {class: 'igv-current-genome'});
72405
+ $navbarLeftContainer.append(this.$current_genome);
72406
+ this.$current_genome.text('');
72407
+
72408
+ const $genomicLocation = $$1('<div>', {class: 'igv-navbar-genomic-location'});
72409
+ $navbarLeftContainer.append($genomicLocation);
72410
+
72411
+ // chromosome select widget
72412
+ this.chromosomeSelectWidget = new ChromosomeSelectWidget(browser, $genomicLocation.get(0));
72413
+ if (config.showChromosomeWidget !== false) {
72414
+ this.chromosomeSelectWidget.show();
72415
+ } else {
72416
+ this.chromosomeSelectWidget.hide();
72417
+ }
72418
+
72419
+ const $locusSizeGroup = $$1('<div>', {class: 'igv-locus-size-group'});
72420
+ $genomicLocation.append($locusSizeGroup);
72421
+
72422
+ const $searchContainer = $$1('<div>', {class: 'igv-search-container'});
72423
+ $locusSizeGroup.append($searchContainer);
72424
+
72425
+ // browser.$searchInput = $('<input type="text" placeholder="Locus Search">');
72426
+ this.$searchInput = $$1('<input>', {class: 'igv-search-input', type: 'text', placeholder: 'Locus Search'});
72427
+ $searchContainer.append(this.$searchInput);
72428
+ // Stop event propagation to prevent feature track keyboard navigation
72429
+ this.$searchInput[0].addEventListener('keyup', (event) => {
72430
+ event.stopImmediatePropagation();
72431
+ });
72432
+
72433
+ this.$searchInput.change(() => browser.doSearch(this.$searchInput.val()));
72434
+
72435
+ const searchIconContainer = div({class: 'igv-search-icon-container'});
72436
+ $searchContainer.append($$1(searchIconContainer));
72437
+ searchIconContainer.appendChild(createIcon("search"));
72438
+ searchIconContainer.addEventListener('click', () => browser.doSearch(this.$searchInput.val()));
72439
+
72440
+ this.windowSizePanel = new WindowSizePanel($locusSizeGroup.get(0), browser);
72441
+
72442
+ const $navbarRightContainer = $$1('<div>', {class: 'igv-navbar-right-container'});
72443
+ $navBar.append($navbarRightContainer);
72444
+ this.navbarRightContainer = $navbarRightContainer.get(0);
72445
+
72446
+ const $toggle_button_container = $$1('<div class="igv-navbar-toggle-button-container">');
72447
+ $navbarRightContainer.append($toggle_button_container);
72448
+ const toggleButtonContainer = $toggle_button_container.get(0);
72449
+ this.toggle_button_container = toggleButtonContainer; // TODO -- for circular view , refactor this
72450
+
72451
+ this.overlayTrackButton = new OverlayTrackButton(toggleButtonContainer, browser);
72452
+ this.overlayTrackButton.setVisibility(false);
72453
+
72454
+ const showMultiSelect = config.showMultiSelectButton !== false;
72455
+ this.multiTrackSelectButton = new MultiTrackSelectButton(toggleButtonContainer, browser, this, showMultiSelect);
72456
+
72457
+ this.cursorGuideButton = new CursorGuideButton(toggleButtonContainer, browser);
72458
+
72459
+ this.centerLineButton = new CenterLineButton(toggleButtonContainer, browser);
72460
+
72461
+ this.trackLabelControl = new TrackLabelControl(toggleButtonContainer, browser);
72462
+
72463
+ // ROI Control
72464
+ this.roiTableControl = new ROITableControl(toggleButtonContainer, browser);
72465
+
72466
+ this.sampleInfoControl = new SampleInfoControl(toggleButtonContainer, browser);
72467
+
72468
+ this.sampleNameControl = new SampleNameControl(toggleButtonContainer, browser);
72469
+
72470
+ if (true === config.showSVGButton) {
72471
+ this.saveImageControl = new SaveImageControl(toggleButtonContainer, browser);
72472
+ }
72473
+
72474
+ if (config.customButtons) {
72475
+ for (let b of config.customButtons) {
72476
+ new CustomButton(toggleButtonContainer, browser, b);
72477
+ }
72478
+ }
72479
+
72480
+ this.zoomWidget = new ZoomWidget(config, browser, $navbarRightContainer.get(0));
72481
+
72482
+ if (false === config.showNavigation) {
72483
+ this.$navigation.hide();
72484
+ }
72485
+
72486
+
72487
+
72488
+ }
72489
+
72490
+ navbarDidResize() {
72491
+
72492
+
72493
+ const navbarWidth = this.$navigation.width();
72494
+ const currentClass = this.currentNavbarButtonClass();
72495
+ if ('igv-navbar-text-button' === currentClass) {
72496
+ this.textButtonContainerWidth = this.navbarRightContainer.getBoundingClientRect().width;
72497
+ }
72498
+ const browser = this.browser;
72499
+ const isWGV =
72500
+ (browser.isMultiLocusWholeGenomeView()) ||
72501
+ (browser.referenceFrameList && GenomeUtils.isWholeGenomeView(browser.referenceFrameList[0].chr));
72502
+
72503
+ isWGV ? this.windowSizePanel.hide() : this.windowSizePanel.show();
72504
+
72505
+ const {
72506
+ x: leftContainerX,
72507
+ width: leftContainerWidth
72508
+ } = this.navbarLeftContainer.getBoundingClientRect();
72509
+ const leftContainerExtent = leftContainerX + leftContainerWidth;
72510
+ const {x: rightContainerX} = this.navbarRightContainer.getBoundingClientRect();
72511
+
72512
+ const delta = rightContainerX - leftContainerExtent;
72513
+
72514
+ let navbarButtonClass;
72515
+ const threshold = 8;
72516
+ if ('igv-navbar-text-button' === currentClass && delta < threshold) {
72517
+ navbarButtonClass = 'igv-navbar-icon-button';
72518
+ } else if (this.textButtonContainerWidth && 'igv-navbar-icon-button' === currentClass) {
72519
+ const length = navbarWidth - leftContainerExtent;
72520
+ if (length - this.textButtonContainerWidth > threshold) {
72521
+ navbarButtonClass = 'igv-navbar-text-button';
72522
+ }
72523
+ }
72524
+ // Update all the buttons (buttons are listeners)
72525
+ if(navbarButtonClass && currentClass !== navbarButtonClass) {
72526
+ this.currentClass = navbarButtonClass;
72527
+ this.browser.fireEvent('navbar-resize', [navbarButtonClass]);
72528
+ }
72529
+
72530
+ let zoomContainerClass;
72531
+ if (isWGV) {
72532
+ zoomContainerClass = 'igv-zoom-widget-hidden';
72533
+ } else {
72534
+ zoomContainerClass = navbarWidth > 860 ? 'igv-zoom-widget' : 'igv-zoom-widget-900';
72535
+ }
72536
+ $$1(this.zoomWidget.zoomContainer).removeClass();
72537
+ $$1(this.zoomWidget.zoomContainer).addClass(zoomContainerClass);
72538
+ }
72539
+
72540
+
72541
+ setCenterLineButtonVisibility(isWholeGenomeView) {
72542
+ if (isWholeGenomeView) {
72543
+ this.centerLineButton.setVisibility(!isWholeGenomeView);
72544
+ } else {
72545
+ this.centerLineButton.setVisibility(this.config.showCenterGuideButton);
72546
+ }
72547
+ }
72548
+
72549
+ setCursorGuideVisibility(doShowCursorGuide) {
72550
+ if (doShowCursorGuide) {
72551
+ this.cursorGuide.show();
72552
+ } else {
72553
+ this.cursorGuide.hide();
72554
+ }
72555
+ }
72556
+
72557
+ updateGenome(genome) {
72558
+
72559
+ let genomeLabel = (genome.id && genome.id.length < 20 ? genome.id : `${genome.id.substring(0, 8)}...${genome.id.substring(genome.id.length - 8)}`);
72560
+ this.$current_genome.text(genomeLabel);
72561
+ this.$current_genome.attr('title', genome.description);
72562
+
72563
+ // chromosome select widget -- Show this IFF its not explicitly hidden AND the genome has pre-loaded chromosomes
72564
+ const showChromosomeWidget =
72565
+ this.config.showChromosomeWidget !== false &&
72566
+ genome.showChromosomeWidget !== false &&
72567
+ genome.chromosomeNames &&
72568
+ genome.chromosomeNames.length > 1;
72569
+
72570
+ if (showChromosomeWidget) {
72571
+ this.chromosomeSelectWidget.update(genome);
72572
+ this.chromosomeSelectWidget.show();
72573
+ } else {
72574
+ this.chromosomeSelectWidget.hide();
72575
+ }
72576
+ }
72577
+
72578
+ updateLocus(loc, chrName) {
72579
+ if(this.$searchInput) {
72580
+ this.$searchInput.val(loc);
72581
+ }
72582
+ if (this.chromosomeSelectWidget) {
72583
+ this.chromosomeSelectWidget.select.value = chrName;
72584
+ }
72585
+ }
72586
+
72587
+ currentNavbarButtonClass() {
72588
+ return this.currentClass
72589
+ //const el = this.$navigation.get(0).querySelector('.igv-navbar-text-button')
72590
+ //return el ? 'igv-navbar-text-button' : 'igv-navbar-icon-button'
72591
+ }
72592
+
72593
+ setEnableTrackSelection(b) {
72594
+ this.multiTrackSelectButton.setMultiTrackSelection(b);
72595
+ }
72596
+ getEnableTrackSelection() {
72597
+ return this.multiTrackSelectButton.enableMultiTrackSelection
72598
+ }
72599
+
72600
+ hide() {
72601
+ this.$navigation.hide();
72602
+ }
72603
+
72604
+ show() {
72605
+ this.$navigation.show();
72606
+ }
72607
+
72608
+ }
72609
+
72610
+
72611
+ function logo() {
72612
+
72613
+ return $$1(
72614
+ '<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">' +
72615
+ '<title>IGV</title>' +
72616
+ '<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">' +
72617
+ '<g id="IGV" fill="#666666">' +
72618
+ '<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>' +
72619
+ '<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>;' +
72620
+ '<polygon id="Path" points="0.81355666 5.00169252 73.0472883 5.00169252 73.0472883 317.722144 0.81355666 317.722144"></polygon>' +
72621
+ '</g> </g> </svg>'
72622
+ )
72623
+ }
72624
+
71936
72625
  const viewportColumnManager =
71937
72626
  {
71938
72627
  createColumns: (columnContainer, count) => {
@@ -72144,18 +72833,6 @@
72144
72833
  this.button.style.display = 'none';
72145
72834
  };
72146
72835
 
72147
- /**
72148
- * User supplied button for the navbar
72149
- */
72150
-
72151
- const CustomButton = function (parent, browser, b) {
72152
-
72153
- const button = div({class: 'igv-navbar-button'});
72154
- parent.append(button);
72155
- button.textContent = b.label;
72156
- button.addEventListener('click', () => b.callback(browser));
72157
- };
72158
-
72159
72836
  const maxSequenceSize = 1000000;
72160
72837
  const maxBlatSize = 25000;
72161
72838
 
@@ -72920,88 +73597,6 @@
72920
73597
  }
72921
73598
  }
72922
73599
 
72923
- const roiImage =
72924
- `<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">
72925
- <title>roi</title>
72926
- <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
72927
- <g id="roi">
72928
- <rect id="Rectangle-Copy-23" stroke="#737373" stroke-width="12" fill="#FFFFFF" x="6" y="6" width="613" height="613" rx="135"></rect>
72929
- <text id="ROI" font-family="HelveticaNeue-Bold, Helvetica Neue" font-size="258" font-weight="bold" fill="#737373">
72930
- <tspan x="81.445" y="389">ROI</tspan>
72931
- </text>
72932
- </g>
72933
- </g>
72934
- </svg>`;
72935
-
72936
- const roiImageHover =
72937
- `<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">
72938
- <title>roi hover</title>
72939
- <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
72940
- <g id="roi-hover">
72941
- <rect id="Rectangle-Copy-24" stroke="#737373" stroke-width="12" fill="#737373" x="6" y="6" width="613" height="613" rx="135"></rect>
72942
- <text id="ROI" font-family="HelveticaNeue-Bold, Helvetica Neue" font-size="258" font-weight="bold" fill="#FFFFFF">
72943
- <tspan x="81.445" y="389">ROI</tspan>
72944
- </text>
72945
- </g>
72946
- </g>
72947
- </svg>`;
72948
-
72949
- /*
72950
- * The MIT License (MIT)
72951
- *
72952
- * Copyright (c) 2016 University of California San Diego
72953
- * Author: Jim Robinson
72954
- *
72955
- * Permission is hereby granted, free of charge, to any person obtaining a copy
72956
- * of this software and associated documentation files (the "Software"), to deal
72957
- * in the Software without restriction, including without limitation the rights
72958
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
72959
- * copies of the Software, and to permit persons to whom the Software is
72960
- * furnished to do so, subject to the following conditions:
72961
- *
72962
- * The above copyright notice and this permission notice shall be included in
72963
- * all copies or substantial portions of the Software.
72964
- *
72965
- *
72966
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
72967
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
72968
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
72969
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
72970
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
72971
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
72972
- * THE SOFTWARE.
72973
- */
72974
-
72975
- class ROITableControl extends NavbarButton {
72976
-
72977
- constructor(parent, browser) {
72978
-
72979
- super(browser, parent, [ 'ROI', 'Regions of Interest Table' ], buttonLabel, roiImage, roiImageHover, false);
72980
-
72981
- this.button.addEventListener('mouseenter', () => {
72982
- if (false === browser.doShowROITable) {
72983
- this.setState(true);
72984
- }
72985
- });
72986
-
72987
- this.button.addEventListener('mouseleave', () => {
72988
- if (false === browser.doShowROITable) {
72989
- this.setState(false);
72990
- }
72991
- });
72992
-
72993
- this.button.addEventListener('click', () => this.buttonHandler(!browser.doShowROITable));
72994
-
72995
- this.setVisibility(false); // Hide initially, it will be un-hidden if ROIs are loaded
72996
-
72997
- }
72998
-
72999
- buttonHandler(status) {
73000
- this.setState(status);
73001
- this.browser.setROITableVisibility(status);
73002
- }
73003
- }
73004
-
73005
73600
  async function translateSession(juiceboxSession) {
73006
73601
 
73007
73602
  const jbBrowser = juiceboxSession.browsers[0];
@@ -73038,101 +73633,6 @@
73038
73633
 
73039
73634
  }
73040
73635
 
73041
- const multiSelectImage =
73042
- `<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">
73043
- <title>multi select</title>
73044
- <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
73045
- <g id="multi-select">
73046
- <rect id="backdrop-copy-3" stroke="#737373" stroke-width="12" fill="#FFFFFF" x="6" y="6" width="613" height="613" rx="135"></rect>
73047
- <g id="row-copy-3" transform="translate(81, 427)" fill="#737373">
73048
- <rect id="Rectangle" x="134" y="0" width="329" height="70"></rect>
73049
- <rect id="Rectangle-Copy-16" stroke="#737373" stroke-width="12" x="6" y="6" width="58" height="58"></rect>
73050
- </g>
73051
- <g id="row-copy-2" transform="translate(82, 277)">
73052
- <rect id="Rectangle" fill-opacity="0.33" fill="#CFCECE" x="133" y="0" width="329" height="70"></rect>
73053
- <rect id="Rectangle-Copy-16" stroke-opacity="0.32659528" stroke="#CFCECE" stroke-width="12" x="6" y="6" width="58" height="58"></rect>
73054
- </g>
73055
- <g id="row-copy" transform="translate(81, 119)" fill="#737373">
73056
- <rect id="Rectangle" x="134" y="0" width="329" height="70"></rect>
73057
- <rect id="Rectangle-Copy-17" stroke="#737373" stroke-width="12" x="6" y="6" width="58" height="58"></rect>
73058
- </g>
73059
- </g>
73060
- </g>
73061
- </svg>`;
73062
-
73063
- const multiSelectImageHover =
73064
- `<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">
73065
- <title>multi select hover</title>
73066
- <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
73067
- <g id="multi-select-hover">
73068
- <rect id="backdrop-copy-4" stroke="#737373" stroke-width="12" fill="#737373" x="6" y="6" width="613" height="613" rx="135"></rect>
73069
- <g id="row-copy-3" transform="translate(81, 427)" fill="#FFFFFF">
73070
- <rect id="Rectangle" x="134" y="0" width="329" height="70"></rect>
73071
- <rect id="Rectangle-Copy-16" stroke="#FFFFFF" stroke-width="12" x="6" y="6" width="58" height="58"></rect>
73072
- </g>
73073
- <g id="row-copy-2" transform="translate(82, 277)">
73074
- <rect id="Rectangle" fill-opacity="0.33" fill="#CFCECE" x="133" y="0" width="329" height="70"></rect>
73075
- <rect id="Rectangle-Copy-16" stroke-opacity="0.33" stroke="#CFCECE" stroke-width="12" x="6" y="6" width="58" height="58"></rect>
73076
- </g>
73077
- <g id="row-copy" transform="translate(81, 119)" fill="#FFFFFF">
73078
- <rect id="Rectangle" x="134" y="0" width="329" height="70"></rect>
73079
- <rect id="Rectangle-Copy-17" stroke="#FFFFFF" stroke-width="12" x="6" y="6" width="58" height="58"></rect>
73080
- </g>
73081
- </g>
73082
- </g>
73083
- </svg>`;
73084
-
73085
- class MultiTrackSelectButton extends NavbarButton {
73086
-
73087
- constructor(browser, parent, enableMultiTrackSelection) {
73088
-
73089
- super(browser, parent, 'Select Tracks', buttonLabel, multiSelectImage, multiSelectImageHover, enableMultiTrackSelection = false);
73090
- this.enableMultiTrackSelection = enableMultiTrackSelection;
73091
- this.button.addEventListener('mouseenter', event => {
73092
- if (false === enableMultiTrackSelection) {
73093
- this.setState(true);
73094
- }
73095
- });
73096
-
73097
- this.button.addEventListener('mouseleave', event => {
73098
- if (false === enableMultiTrackSelection) {
73099
- this.setState(false);
73100
- }
73101
- });
73102
-
73103
- const mouseClickHandler = () => {
73104
- this.setMultiTrackSelection(!this.enableMultiTrackSelection);
73105
- };
73106
-
73107
- this.boundMouseClickHandler = mouseClickHandler.bind(this);
73108
-
73109
- this.button.addEventListener('click', this.boundMouseClickHandler);
73110
-
73111
- this.setVisibility(true);
73112
-
73113
- }
73114
-
73115
- setMultiTrackSelection(enableMultiTrackSelection) {
73116
- this.enableMultiTrackSelection = enableMultiTrackSelection;
73117
- for (const trackView of this.browser.trackViews) {
73118
- if (false === multiTrackSelectExclusionTypes.has(trackView.track.type)) {
73119
- trackView.setTrackSelectionState(trackView.axis, this.enableMultiTrackSelection);
73120
-
73121
- // If closing the selection boxes set track selected property to false
73122
- if (!this.enableMultiTrackSelection) {
73123
- trackView.track.selected = false;
73124
- }
73125
- }
73126
- }
73127
- this.setState(this.enableMultiTrackSelection);
73128
-
73129
- // If enableMultiTrackSelection is false hide Overlay button
73130
- if (false === this.enableMultiTrackSelection) {
73131
- this.browser.overlayTrackButton.setVisibility(false);
73132
- }
73133
- }
73134
- }
73135
-
73136
73636
  /**
73137
73637
  * Chromosome alias source backed by a UCSC bigbed file
73138
73638
  *
@@ -73152,6 +73652,13 @@
73152
73652
  this.reader = new BWReader(config, genome);
73153
73653
  }
73154
73654
 
73655
+ async preload(chrNames) {
73656
+ await this.reader.preload();
73657
+ for(let nm of chrNames) {
73658
+ await this.search(nm);
73659
+ }
73660
+ }
73661
+
73155
73662
  /**
73156
73663
  * Return the cached canonical chromosome name for the alias. If none found return the alias.
73157
73664
  *
@@ -73227,6 +73734,10 @@
73227
73734
  this.genome = genome;
73228
73735
  }
73229
73736
 
73737
+ async preload() {
73738
+ return this.loadAliases();
73739
+ }
73740
+
73230
73741
  /**
73231
73742
  * Return the canonical chromosome name for the alias. If none found return the alias
73232
73743
  *
@@ -73279,6 +73790,9 @@
73279
73790
  aliasRecord[key] = tokens[i];
73280
73791
  this.aliasRecordCache.set(tokens[i], aliasRecord);
73281
73792
  }
73793
+
73794
+ this.aliasRecordCache.set(chr.toLowerCase(), aliasRecord);
73795
+ this.aliasRecordCache.set(chr.toUpperCase(), aliasRecord);
73282
73796
  }
73283
73797
  }
73284
73798
  }
@@ -73450,6 +73964,9 @@
73450
73964
  this.update(id, chromosomeNames);
73451
73965
  }
73452
73966
 
73967
+ async preload() {
73968
+ // no-op
73969
+ }
73453
73970
 
73454
73971
  /**
73455
73972
  * Return the canonical chromosome name for the alias. If none found return the alias
@@ -73551,6 +74068,8 @@
73551
74068
  } else if (Number.isInteger(Number(name))) {
73552
74069
  record["ucsc"] = "chr" + name;
73553
74070
  }
74071
+
74072
+
73554
74073
  }
73555
74074
  }
73556
74075
 
@@ -73558,7 +74077,10 @@
73558
74077
  for (let a of Object.values(rec)) {
73559
74078
  this.aliasRecordCache.set(a, rec);
73560
74079
  }
74080
+ this.aliasRecordCache.set(rec.chr.toLowerCase(), rec);
74081
+ this.aliasRecordCache.set(rec.chr.toUpperCase(), rec);
73561
74082
  }
74083
+
73562
74084
  }
73563
74085
  }
73564
74086
 
@@ -73626,7 +74148,7 @@
73626
74148
 
73627
74149
  if (config.chromAliasBbURL) {
73628
74150
  this.chromAlias = new ChromAliasBB(config.chromAliasBbURL, Object.assign({}, config), this);
73629
- if(!this.chromosomeNames) {
74151
+ if (!this.chromosomeNames) {
73630
74152
  this.chromosomeNames = await this.chromAlias.getChromosomeNames();
73631
74153
  }
73632
74154
  } else if (config.aliasURL) {
@@ -73637,17 +74159,17 @@
73637
74159
 
73638
74160
  if (config.cytobandBbURL) {
73639
74161
  this.cytobandSource = new CytobandFileBB(config.cytobandBbURL, Object.assign({}, config), this);
73640
- if(!this.chromosomeNames) {
74162
+ if (!this.chromosomeNames) {
73641
74163
  this.chromosomeNames = await this.cytobandSource.getChromosomeNames();
73642
74164
  }
73643
74165
  } else if (config.cytobandURL) {
73644
74166
  this.cytobandSource = new CytobandFile(config.cytobandURL, Object.assign({}, config));
73645
- if(!this.chromosomeNames) {
74167
+ if (!this.chromosomeNames) {
73646
74168
  this.chromosomeNames = await this.cytobandSource.getChromosomeNames();
73647
74169
  }
73648
- if(this.chromosomes.size === 0) {
74170
+ if (this.chromosomes.size === 0) {
73649
74171
  const c = await this.cytobandSource.getChromosomes();
73650
- for(let chromosome of c) {
74172
+ for (let chromosome of c) {
73651
74173
  this.chromosomes.set(c.name, c);
73652
74174
  }
73653
74175
  }
@@ -73665,6 +74187,7 @@
73665
74187
  } else {
73666
74188
  this.#wgChromosomeNames = trimSmallChromosomes(this.chromosomes);
73667
74189
  }
74190
+ await this.chromAlias.preload(this.#wgChromosomeNames);
73668
74191
  }
73669
74192
 
73670
74193
  // Optionally create the psuedo chromosome "all" to support whole genome view
@@ -73731,15 +74254,15 @@
73731
74254
  async loadChromosome(chr) {
73732
74255
 
73733
74256
  if (this.chromAlias) {
73734
- const chromAliasRecord = await this.chromAlias.search(chr);
73735
- if(chromAliasRecord) {
74257
+ const chromAliasRecord = await this.chromAlias.search(chr);
74258
+ if (chromAliasRecord) {
73736
74259
  chr = chromAliasRecord.chr;
73737
74260
  }
73738
74261
  }
73739
74262
 
73740
74263
  if (!this.chromosomes.has(chr)) {
73741
74264
  let chromosome;
73742
- const sequenceRecord = await this.sequence.getSequenceRecord(chr);
74265
+ const sequenceRecord = await this.sequence.getSequenceRecord(chr);
73743
74266
  if (sequenceRecord) {
73744
74267
  chromosome = new Chromosome(chr, 0, sequenceRecord.bpLength);
73745
74268
  }
@@ -73749,6 +74272,7 @@
73749
74272
 
73750
74273
  return this.chromosomes.get(chr)
73751
74274
  }
74275
+
73752
74276
  async getAliasRecord(chr) {
73753
74277
  if (this.chromAlias) {
73754
74278
  return this.chromAlias.search(chr)
@@ -73768,7 +74292,7 @@
73768
74292
  }
73769
74293
 
73770
74294
  get wgChromosomeNames() {
73771
- return this.#wgChromosomeNames ? this.#wgChromosomeNames.slice() : undefined
74295
+ return this.#wgChromosomeNames ? this.#wgChromosomeNames.slice() : undefined
73772
74296
  }
73773
74297
 
73774
74298
  get showChromosomeWidget() {
@@ -73872,7 +74396,7 @@
73872
74396
  * @param end
73873
74397
  */
73874
74398
  getSequenceInterval(chr, start, end) {
73875
- if(typeof this.sequence.getSequenceInterval === 'function') {
74399
+ if (typeof this.sequence.getSequenceInterval === 'function') {
73876
74400
  return this.sequence.getSequenceInterval(chr, start, end)
73877
74401
  } else {
73878
74402
  return undefined
@@ -74041,6 +74565,125 @@
74041
74565
  brewer.push("rgb(204, 235, 197)");
74042
74566
  brewer.push("rgb(255, 237, 111)");
74043
74567
 
74568
+ class SliderDialog {
74569
+
74570
+ constructor(parent) {
74571
+
74572
+ this.parent = parent;
74573
+
74574
+ // dialog container
74575
+ this.container = div({class: 'igv-ui-generic-dialog-container'});
74576
+ parent.appendChild(this.container);
74577
+
74578
+ // dialog header
74579
+ const header = div({class: 'igv-ui-generic-dialog-header'});
74580
+ this.container.appendChild(header);
74581
+
74582
+ // dialog label
74583
+ this.label = div({class: 'igv-ui-generic-dialog-one-liner'});
74584
+ this.container.appendChild(this.label);
74585
+ this.label.text = 'Unlabeled';
74586
+
74587
+ // input container
74588
+ this.input_container = div({class: 'igv-ui-generic-dialog-input'});
74589
+ this.container.appendChild(this.input_container);
74590
+
74591
+ // input element
74592
+ let html = `<input type="range" id="igv-slider-dialog-input" name="igv-slider-dialog-input" />`;
74593
+ this._input = document.createRange().createContextualFragment(html).firstChild;
74594
+ this.input_container.appendChild(this._input);
74595
+
74596
+ // output element
74597
+ html = `<output id="igv-slider-dialog-output" name="igv-slider-dialog-output" for="igv-slider-dialog-input"></output>`;
74598
+ this._output = document.createRange().createContextualFragment(html).firstChild;
74599
+ this.input_container.appendChild(this._output);
74600
+
74601
+
74602
+ // ok | cancel
74603
+ const buttons = div({class: 'igv-ui-generic-dialog-ok-cancel'});
74604
+ this.container.appendChild(buttons);
74605
+
74606
+ // ok
74607
+ this.ok = div();
74608
+ buttons.appendChild(this.ok);
74609
+ this.ok.textContent = 'OK';
74610
+
74611
+ // cancel
74612
+ this.cancel = div();
74613
+ buttons.appendChild(this.cancel);
74614
+ this.cancel.textContent = 'Cancel';
74615
+
74616
+ hide(this.container);
74617
+
74618
+ this._input.addEventListener('input', () => {
74619
+ const number = parseFloat(this._input.value)/this._scaleFactor;
74620
+ this.callback(number);
74621
+ this._output.value = `${number.toFixed(2)}`;
74622
+ }, false);
74623
+
74624
+ this.ok.addEventListener('click', () => {
74625
+ if (typeof this.callback === 'function') {
74626
+ const number = parseFloat(this._input.value)/this._scaleFactor;
74627
+ this.callback(number);
74628
+ this.callback = undefined;
74629
+ }
74630
+ this._input.value = undefined;
74631
+ hide(this.container);
74632
+ });
74633
+
74634
+ const cancel = () => {
74635
+ this._input.value = undefined;
74636
+ hide(this.container);
74637
+ };
74638
+
74639
+ this.cancel.addEventListener('click', cancel);
74640
+
74641
+ attachDialogCloseHandlerWithParent(header, cancel);
74642
+ makeDraggable(this.container, header);
74643
+
74644
+ }
74645
+
74646
+ get value() {
74647
+ return purify.sanitize(this._input.value)
74648
+ }
74649
+
74650
+ present(options, e) {
74651
+
74652
+ this.label.textContent = options.label;
74653
+
74654
+ this._scaleFactor = options.scaleFactor;
74655
+ const [ minS, maxS, valueS ] = [ options.min, options.max, options.value ].map(number => (Math.floor(this._scaleFactor * number)).toString());
74656
+
74657
+ this._input.min = minS;
74658
+ this._input.max = maxS;
74659
+ this._input.value = valueS;
74660
+
74661
+ const numer = parseFloat(valueS);
74662
+ const denom = this._scaleFactor;
74663
+ const number = numer/denom;
74664
+ this._output.value = `${number.toFixed(2)}`;
74665
+
74666
+ this.callback = options.callback || options.click;
74667
+
74668
+ show(this.container);
74669
+ this.clampLocation(e.clientX, e.clientY);
74670
+
74671
+ }
74672
+
74673
+ clampLocation(clientX, clientY) {
74674
+
74675
+ const {width: w, height: h} = this.container.getBoundingClientRect();
74676
+ const wh = window.innerHeight;
74677
+ const ww = window.innerWidth;
74678
+
74679
+ const y = Math.min(wh - h, clientY);
74680
+ const x = Math.min(ww - w, clientX);
74681
+ this.container.style.left = `${x}px`;
74682
+ this.container.style.top = `${y}px`;
74683
+
74684
+ }
74685
+ }
74686
+
74044
74687
  // css - $igv-scrollbar-outer-width: 14px;
74045
74688
  const igv_scrollbar_outer_width = 14;
74046
74689
 
@@ -74130,7 +74773,6 @@
74130
74773
  this.sampleNameControl.setState(this.showSampleNames);
74131
74774
  this.sampleNameControl.hide();
74132
74775
 
74133
-
74134
74776
  this.layoutChange();
74135
74777
  }
74136
74778
  });
@@ -74149,7 +74791,7 @@
74149
74791
 
74150
74792
  this.sampleInfo = new SampleInfo(this);
74151
74793
 
74152
- this.setControls(config);
74794
+ this.createStandardControls(config);
74153
74795
 
74154
74796
  // Region of interest
74155
74797
  this.roiManager = new ROIManager(this);
@@ -74207,121 +74849,18 @@
74207
74849
  }
74208
74850
  }
74209
74851
 
74210
- setControls(config) {
74211
-
74212
- const $navBar = this.createStandardControls(config);
74213
- $navBar.insertBefore($$1(this.columnContainer));
74214
- this.$navigation = $navBar;
74215
-
74216
- if (false === config.showControls) {
74217
- $navBar.hide();
74218
- }
74219
-
74220
- }
74221
-
74222
74852
  createStandardControls(config) {
74223
74853
 
74224
- const $navBar = $$1('<div>', {class: 'igv-navbar'});
74225
- this.$navigation = $navBar;
74226
-
74227
- const $navbarLeftContainer = $$1('<div>', {class: 'igv-navbar-left-container'});
74228
- $navBar.append($navbarLeftContainer);
74229
-
74230
- // IGV logo
74231
- const $logo = $$1('<div>', {class: 'igv-logo'});
74232
- $navbarLeftContainer.append($logo);
74233
-
74234
- const logoSvg = logo();
74235
- logoSvg.css("width", "34px");
74236
- logoSvg.css("height", "32px");
74237
- $logo.append(logoSvg);
74238
-
74239
- this.$current_genome = $$1('<div>', {class: 'igv-current-genome'});
74240
- $navbarLeftContainer.append(this.$current_genome);
74241
- this.$current_genome.text('');
74242
-
74243
- const $genomicLocation = $$1('<div>', {class: 'igv-navbar-genomic-location'});
74244
- $navbarLeftContainer.append($genomicLocation);
74245
-
74246
- // chromosome select widget
74247
- this.chromosomeSelectWidget = new ChromosomeSelectWidget(this, $genomicLocation.get(0));
74248
- if (config.showChromosomeWidget !== false) {
74249
- this.chromosomeSelectWidget.show();
74250
- } else {
74251
- this.chromosomeSelectWidget.hide();
74252
- }
74253
-
74254
- const $locusSizeGroup = $$1('<div>', {class: 'igv-locus-size-group'});
74255
- $genomicLocation.append($locusSizeGroup);
74256
-
74257
- const $searchContainer = $$1('<div>', {class: 'igv-search-container'});
74258
- $locusSizeGroup.append($searchContainer);
74259
-
74260
- // browser.$searchInput = $('<input type="text" placeholder="Locus Search">');
74261
- this.$searchInput = $$1('<input>', {class: 'igv-search-input', type: 'text', placeholder: 'Locus Search'});
74262
- $searchContainer.append(this.$searchInput);
74263
- // Stop event propagation to prevent feature track keyboard navigation
74264
- this.$searchInput[0].addEventListener('keyup', (event) => {
74265
- event.stopImmediatePropagation();
74266
- });
74267
-
74268
- this.$searchInput.change(() => this.doSearch(this.$searchInput.val()));
74269
-
74270
- const searchIconContainer = div({class: 'igv-search-icon-container'});
74271
- $searchContainer.append($$1(searchIconContainer));
74272
-
74273
- searchIconContainer.appendChild(createIcon("search"));
74274
-
74275
- searchIconContainer.addEventListener('click', () => this.doSearch(this.$searchInput.val()));
74276
-
74277
- this.windowSizePanel = new WindowSizePanel($locusSizeGroup.get(0), this);
74278
-
74279
- const $navbarRightContainer = $$1('<div>', {class: 'igv-navbar-right-container'});
74280
- $navBar.append($navbarRightContainer);
74281
-
74282
- const $toggle_button_container = $$1('<div class="igv-navbar-toggle-button-container">');
74283
- $navbarRightContainer.append($toggle_button_container);
74284
- this.$toggle_button_container = $toggle_button_container;
74285
-
74286
- this.overlayTrackButton = new OverlayTrackButton(this, $toggle_button_container.get(0));
74287
- this.overlayTrackButton.setVisibility(false);
74288
-
74289
- this.multiTrackSelectButton = new MultiTrackSelectButton(this, $toggle_button_container.get(0));
74290
-
74291
- this.cursorGuide = new CursorGuide(this.columnContainer, this);
74292
-
74293
- this.cursorGuideButton = new CursorGuideButton(this, $toggle_button_container.get(0));
74294
-
74295
- this.centerLineButton = new CenterLineButton(this, $toggle_button_container.get(0));
74296
-
74297
74854
  this.setTrackLabelVisibility(config.showTrackLabels);
74298
- this.trackLabelControl = new TrackLabelControl($toggle_button_container.get(0), this);
74299
74855
 
74300
- // ROI Control
74301
- this.roiTableControl = new ROITableControl($toggle_button_container.get(0), this);
74302
-
74303
- this.sampleInfoControl = new SampleInfoControl($toggle_button_container.get(0), this);
74856
+ this.navbar = new ResponsiveNavbar(config, this);
74304
74857
 
74305
- this.sampleNameControl = new SampleNameControl($toggle_button_container.get(0), this);
74306
-
74307
- if (true === config.showSVGButton) {
74308
- this.saveImageControl = new SaveImageControl($toggle_button_container.get(0), this);
74309
- }
74858
+ this.navbar.$navigation.insertBefore($$1(this.columnContainer));
74310
74859
 
74311
- if (config.customButtons) {
74312
- for (let b of config.customButtons) {
74313
- new CustomButton($toggle_button_container.get(0), this, b);
74314
- }
74315
- }
74316
-
74317
- this.zoomWidget = new ZoomWidget(this, $navbarRightContainer.get(0));
74318
-
74319
- if (false === config.showNavigation) {
74320
- this.$navigation.hide();
74860
+ if (false === config.showControls) {
74861
+ this.navbar.hide();
74321
74862
  }
74322
-
74323
- this.sliderDialog = new SliderDialog(this.root);
74324
- this.sliderDialog.container.id = `igv-slider-dialog-${guid$2()}`;
74863
+ this.cursorGuide = new CursorGuide(this.columnContainer, this);
74325
74864
 
74326
74865
  this.inputDialog = new InputDialog(this.root);
74327
74866
  this.inputDialog.container.id = `igv-input-dialog-${guid$2()}`;
@@ -74332,7 +74871,8 @@
74332
74871
  this.genericColorPicker = new GenericColorPicker({parent: this.columnContainer, width: 432});
74333
74872
  this.genericColorPicker.container.id = `igv-track-color-picker-${guid$2()}`;
74334
74873
 
74335
- return $navBar
74874
+ this.sliderDialog = new SliderDialog(this.root);
74875
+ this.sliderDialog.container.id = `igv-slider-dialog-${guid$2()}`;
74336
74876
 
74337
74877
  }
74338
74878
 
@@ -74563,10 +75103,10 @@
74563
75103
  session = await translateSession(session);
74564
75104
  }
74565
75105
 
74566
- this.sampleInfoControl.setButtonVisibility(false);
75106
+ this.navbar.sampleInfoControl.setButtonVisibility(false);
74567
75107
 
74568
75108
  this.showSampleNames = session.showSampleNames || false;
74569
- this.sampleNameControl.setState(this.showSampleNames === true);
75109
+ this.navbar.sampleNameControl.setState(this.showSampleNames === true);
74570
75110
 
74571
75111
  if (session.sampleNameViewportWidth) {
74572
75112
  this.sampleNameViewportWidth = session.sampleNameViewportWidth;
@@ -74686,9 +75226,9 @@
74686
75226
  await rtv.updateViews();
74687
75227
  }
74688
75228
 
74689
- // If any tracks are selected show the selectino buttons
75229
+ // If any tracks are selected show the selection buttons
74690
75230
  if (this.trackViews.some(tv => tv.track.selected)) {
74691
- this.multiTrackSelectButton.setMultiTrackSelection(true);
75231
+ this.navbar.setEnableTrackSelection(true);
74692
75232
  }
74693
75233
 
74694
75234
  this.updateUIWithReferenceFrameList();
@@ -74729,7 +75269,8 @@
74729
75269
 
74730
75270
  this.removeAllTracks(); // Do this first, before new genome is set
74731
75271
  this.roiManager.clearROIs();
74732
- this.multiTrackSelectButton.setMultiTrackSelection(false);
75272
+
75273
+ this.navbar.setEnableTrackSelection(false);
74733
75274
 
74734
75275
  let genome;
74735
75276
  if (genomeConfig.gbkURL) {
@@ -74742,8 +75283,7 @@
74742
75283
 
74743
75284
  this.genome = genome;
74744
75285
 
74745
- this.updateNavbarDOMWithGenome(genome);
74746
-
75286
+ this.navbar.updateGenome(genome);
74747
75287
 
74748
75288
  let locus = initialLocus || genome.initialLocus;
74749
75289
  if (Array.isArray(locus)) {
@@ -74774,26 +75314,6 @@
74774
75314
  }
74775
75315
  }
74776
75316
 
74777
- updateNavbarDOMWithGenome(genome) {
74778
- let genomeLabel = (genome.id && genome.id.length < 20 ? genome.id : `${genome.id.substring(0, 8)}...${genome.id.substring(genome.id.length - 8)}`);
74779
- this.$current_genome.text(genomeLabel);
74780
- this.$current_genome.attr('title', genome.description);
74781
-
74782
- // chromosome select widget -- Show this IFF its not explicitly hidden AND the genome has pre-loaded chromosomes
74783
- const showChromosomeWidget =
74784
- this.config.showChromosomeWidget !== false &&
74785
- this.genome.showChromosomeWidget !== false &&
74786
- genome.chromosomeNames &&
74787
- genome.chromosomeNames.length > 1;
74788
-
74789
- if (showChromosomeWidget) {
74790
- this.chromosomeSelectWidget.update(genome);
74791
- this.chromosomeSelectWidget.show();
74792
- } else {
74793
- this.chromosomeSelectWidget.hide();
74794
- }
74795
- }
74796
-
74797
75317
  /**
74798
75318
  * Load a genome, defined by a string ID or a json-like configuration object. This includes a fasta reference
74799
75319
  * as well as optional cytoband and annotation tracks.
@@ -74878,16 +75398,16 @@
74878
75398
 
74879
75399
  (this.isMultiLocusWholeGenomeView() || GenomeUtils.isWholeGenomeView(referenceFrameList[0].chr));
74880
75400
 
74881
- navbarDidResize(this, this.$navigation.width());
75401
+ this.navbar.navbarDidResize();
74882
75402
 
74883
75403
  toggleTrackLabels(this.trackViews, this.doShowTrackLabels);
74884
75404
 
74885
75405
  if (this.doShowCenterLine && GenomeUtils.isWholeGenomeView(referenceFrameList[0].chr)) {
74886
- this.centerLineButton.boundMouseClickHandler();
75406
+ this.navbar.centerLineButton.boundMouseClickHandler();
74887
75407
  }
74888
75408
 
74889
75409
  if (this.doShowCursorGuide && GenomeUtils.isWholeGenomeView(referenceFrameList[0].chr)) {
74890
- this.cursorGuideButton.boundMouseClickHandler();
75410
+ this.navbar.cursorGuideButton.boundMouseClickHandler();
74891
75411
  }
74892
75412
 
74893
75413
  this.setCenterLineAndCenterLineButtonVisibility(GenomeUtils.isWholeGenomeView(referenceFrameList[0].chr));
@@ -74897,9 +75417,9 @@
74897
75417
  setCenterLineAndCenterLineButtonVisibility(isWholeGenomeView) {
74898
75418
 
74899
75419
  if (isWholeGenomeView) {
74900
- this.centerLineButton.setVisibility(!isWholeGenomeView);
75420
+ this.navbar.centerLineButton.setVisibility(false);
74901
75421
  } else {
74902
- this.centerLineButton.setVisibility(this.config.showCenterGuideButton);
75422
+ this.navbar.centerLineButton.setVisibility(this.config.showCenterGuideButton);
74903
75423
  }
74904
75424
 
74905
75425
  for (let centerLine of this.centerLineList) {
@@ -75105,7 +75625,7 @@
75105
75625
  this.reorderTracks();
75106
75626
  this.fireEvent('trackorderchanged', [this.getTrackOrder()]);
75107
75627
 
75108
- this.multiTrackSelectButton.setMultiTrackSelection(this.multiTrackSelectButton.enableMultiTrackSelection);
75628
+ newTrack.trackView.enableTrackSelection(this.navbar.getEnableTrackSelection());
75109
75629
 
75110
75630
  return newTrack
75111
75631
 
@@ -75473,8 +75993,7 @@
75473
75993
  }
75474
75994
 
75475
75995
  if (this.referenceFrameList) {
75476
- this.isMultiLocusWholeGenomeView() || GenomeUtils.isWholeGenomeView(this.referenceFrameList[0].chr);
75477
- navbarDidResize(this, this.$navigation.width());
75996
+ this.navbar.navbarDidResize();
75478
75997
  }
75479
75998
 
75480
75999
  resize.call(this);
@@ -75556,13 +76075,11 @@
75556
76075
  referenceFrame.end = referenceFrame.start + referenceFrame.bpPerPixel * width;
75557
76076
  }
75558
76077
 
75559
- if (this.chromosomeSelectWidget) {
75560
- this.chromosomeSelectWidget.select.value = referenceFrameList.length === 1 ? this.referenceFrameList[0].chr : '';
75561
- }
76078
+ const chrName = referenceFrameList.length === 1 ? this.referenceFrameList[0].chr : '';
75562
76079
 
75563
76080
  const loc = this.referenceFrameList.map(rf => rf.getLocusString()).join(' ');
75564
76081
 
75565
- this.$searchInput.val(loc);
76082
+ this.navbar.updateLocus(loc, chrName);
75566
76083
 
75567
76084
  this.fireEvent('locuschange', [this.referenceFrameList]);
75568
76085
  }
@@ -75837,7 +76354,7 @@
75837
76354
  async loadSampleInfo(config) {
75838
76355
 
75839
76356
  await this.sampleInfo.loadSampleInfoFile(config.url);
75840
-
76357
+
75841
76358
  for (const {sampleInfoViewport} of this.trackViews) {
75842
76359
  sampleInfoViewport.setWidth(this.getSampleInfoColumnWidth());
75843
76360
  }
@@ -76293,7 +76810,7 @@
76293
76810
  createCircularView(container, show) {
76294
76811
  show = show === true; // convert undefined to boolean
76295
76812
  this.circularView = createCircularView(container, this);
76296
- this.circularViewControl = new CircularViewControl(this.$toggle_button_container.get(0), this);
76813
+ this.circularViewControl = new CircularViewControl(this.navbar.toggle_button_container, this);
76297
76814
  this.circularView.setAssembly({
76298
76815
  name: this.genome.id,
76299
76816
  id: this.genome.id,
@@ -76313,6 +76830,30 @@
76313
76830
  this.circularViewControl.setState(isVisible);
76314
76831
  }
76315
76832
  }
76833
+
76834
+
76835
+
76836
+ // Navbar delegates
76837
+ get sampleInfoControl() {
76838
+ return this.navbar.sampleInfoControl
76839
+ }
76840
+
76841
+ get overlayTrackButton() {
76842
+ return this.navbar.overlayTrackButton
76843
+ }
76844
+
76845
+ get roiTableControl() {
76846
+ return this.navbar.roiTableControl
76847
+ }
76848
+
76849
+ get sampleInfoControl() {
76850
+ return this.navbar.sampleInfoControl
76851
+ }
76852
+
76853
+ get sampleNameControl() {
76854
+ return this.navbar.sampleNameControl
76855
+ }
76856
+
76316
76857
  }
76317
76858
 
76318
76859
  function getFileExtension(input) {
@@ -76522,21 +77063,6 @@
76522
77063
  }
76523
77064
  }
76524
77065
 
76525
-
76526
- function logo() {
76527
-
76528
- return $$1(
76529
- '<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">' +
76530
- '<title>IGV</title>' +
76531
- '<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">' +
76532
- '<g id="IGV" fill="#666666">' +
76533
- '<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>' +
76534
- '<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>;' +
76535
- '<polygon id="Path" points="0.81355666 5.00169252 73.0472883 5.00169252 73.0472883 317.722144 0.81355666 317.722144"></polygon>' +
76536
- '</g> </g> </svg>'
76537
- )
76538
- }
76539
-
76540
77066
  function toggleTrackLabels(trackViews, isVisible) {
76541
77067
 
76542
77068
  for (let {viewports} of trackViews) {
@@ -76631,7 +77157,7 @@
76631
77157
  }
76632
77158
 
76633
77159
  browser.stopSpinner();
76634
- navbarDidResize(browser, browser.$navigation.width());
77160
+ browser.navbar.navbarDidResize();
76635
77161
 
76636
77162
  return browser
76637
77163