igv 2.10.5 → 2.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/igv.esm.js CHANGED
@@ -8167,7 +8167,7 @@ function applyStyle$1(elem, style) {
8167
8167
  }
8168
8168
  }
8169
8169
 
8170
- function guid$1 () {
8170
+ function guid$2 () {
8171
8171
  return ("0000" + (Math.random() * Math.pow(36, 4) << 0).toString(36)).slice(-4);
8172
8172
  }
8173
8173
 
@@ -8760,8 +8760,8 @@ async function resolveURL(url) {
8760
8760
 
8761
8761
  function getFilename$1(urlOrFile) {
8762
8762
 
8763
- if (isFile(urlOrFile)) {
8764
- return urlOrFile.name;
8763
+ if (urlOrFile.name !== undefined) {
8764
+ return urlOrFile.name
8765
8765
  } else if (isString$3(urlOrFile)) {
8766
8766
 
8767
8767
  let index = urlOrFile.lastIndexOf("/");
@@ -8772,21 +8772,24 @@ function getFilename$1(urlOrFile) {
8772
8772
  if (index > 0) {
8773
8773
  filename = filename.substr(0, index);
8774
8774
  }
8775
- return filename;
8775
+ return filename
8776
8776
  } else {
8777
- throw Error(`Expected File or string, got ${typeof urlOrFile}`);
8777
+ throw Error(`Expected File or string, got ${typeof urlOrFile}`)
8778
8778
  }
8779
8779
  }
8780
8780
 
8781
8781
  /**
8782
- * Test if object is a File or File-like object by testing for the "name" property. This is not a robust test,
8783
- * but the purpose is to distinguish the object from url strings
8782
+ * Test if object is a File or File-like object.
8784
8783
  *
8785
8784
  * @param object
8786
8785
  */
8787
8786
  function isFile(object) {
8788
- return object !== undefined && (object instanceof File || (typeof object !== "function" &&
8789
- object.hasOwnProperty("name") && object.hasOwnProperty("size") && object.hasOwnProperty("type")));
8787
+ if(!object) {
8788
+ return false;
8789
+ }
8790
+ return typeof object !== 'function' &&
8791
+ (object instanceof File ||
8792
+ (object.hasOwnProperty("name") && typeof object.slice === 'function' && typeof object.arrayBuffer === 'function'))
8790
8793
  }
8791
8794
 
8792
8795
  const isFilePath = isFile; // deprecated
@@ -17121,7 +17124,6 @@ function getOauthToken(url) {
17121
17124
  * @returns the oauth token
17122
17125
  */
17123
17126
  async function fetchGoogleAccessToken(url) {
17124
- console.log("Fetch token for " + url);
17125
17127
  if (isInitialized()) {
17126
17128
  const scope = getScopeForURL(url);
17127
17129
  const googleToken = await getAccessToken(scope);
@@ -18564,11 +18566,11 @@ class AlertDialog {
18564
18566
  this.errorHeadline.textContent = '';
18565
18567
 
18566
18568
  // body container
18567
- let bodyContainer = div({id: 'igv-ui-alert-dialog-body'});
18569
+ let bodyContainer = div({class: 'igv-ui-alert-dialog-body'});
18568
18570
  this.container.appendChild(bodyContainer);
18569
18571
 
18570
18572
  // body copy
18571
- this.body = div({id: 'igv-ui-alert-dialog-body-copy'});
18573
+ this.body = div({class: 'igv-ui-alert-dialog-body-copy'});
18572
18574
  bodyContainer.appendChild(this.body);
18573
18575
 
18574
18576
  // ok container
@@ -19089,7 +19091,7 @@ function createMenuElements$1(itemList, popover) {
19089
19091
 
19090
19092
  function embedCSS$2() {
19091
19093
 
19092
- var css = '.igv-ui-popover {\n cursor: default;\n position: absolute;\n z-index: 2048;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: 1px;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n background-color: white; }\n .igv-ui-popover > div:first-child {\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee; }\n .igv-ui-popover > div:first-child > div:first-child {\n margin-left: 4px; }\n .igv-ui-popover > div:first-child > div:last-child {\n margin-right: 4px;\n height: 12px;\n width: 12px;\n color: #7F7F7F; }\n .igv-ui-popover > div:first-child > div:last-child:hover {\n cursor: pointer;\n color: #444; }\n .igv-ui-popover > div:last-child {\n overflow-y: auto;\n overflow-x: hidden;\n max-height: 400px;\n max-width: 800px;\n background-color: white; }\n .igv-ui-popover > div:last-child > div {\n -webkit-user-select: all;\n /* Chrome/Safari */\n -moz-user-select: all;\n /* Firefox */\n margin-left: 4px;\n margin-right: 4px;\n min-width: 220px;\n overflow-x: hidden;\n text-overflow: ellipsis;\n white-space: nowrap; }\n .igv-ui-popover > div:last-child > div > span {\n font-weight: bolder; }\n .igv-ui-popover > div:last-child hr {\n width: 100%; }\n\n.igv-ui-alert-dialog-container {\n box-sizing: content-box;\n position: absolute;\n z-index: 2048;\n top: 50%;\n left: 50%;\n width: 400px;\n height: 200px;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n outline: none;\n font-family: \"Open Sans\", sans-serif;\n font-size: 15px;\n font-weight: 400;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center; }\n .igv-ui-alert-dialog-container > div:first-child {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee; }\n .igv-ui-alert-dialog-container > div:first-child div:first-child {\n padding-left: 8px; }\n .igv-ui-alert-dialog-container #igv-ui-alert-dialog-body {\n color: #373737;\n width: 100%;\n height: calc(100% - 24px - 64px);\n overflow-y: scroll; }\n .igv-ui-alert-dialog-container #igv-ui-alert-dialog-body #igv-ui-alert-dialog-body-copy {\n cursor: pointer;\n margin: 16px;\n width: auto;\n height: auto;\n overflow-wrap: break-word;\n word-break: break-word;\n background-color: white;\n border: unset; }\n .igv-ui-alert-dialog-container > div:last-child {\n width: 100%;\n margin-bottom: 10px;\n background-color: white;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: center;\n align-items: center; }\n .igv-ui-alert-dialog-container > div:last-child div {\n margin: unset;\n width: 40px;\n height: 30px;\n line-height: 30px;\n text-align: center;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n font-weight: 400;\n border-color: #2B81AF;\n border-style: solid;\n border-width: thin;\n border-radius: 4px;\n background-color: #2B81AF; }\n .igv-ui-alert-dialog-container > div:last-child div:hover {\n cursor: pointer;\n border-color: #25597f;\n background-color: #25597f; }\n\n.igv-ui-color-swatch {\n position: relative;\n box-sizing: content-box;\n display: flex;\n flex-flow: row;\n flex-wrap: wrap;\n justify-content: center;\n align-items: center;\n width: 32px;\n height: 32px;\n border-style: solid;\n border-width: 2px;\n border-color: white;\n border-radius: 4px; }\n\n.igv-ui-color-swatch:hover {\n border-color: dimgray; }\n\n.igv-ui-colorpicker-menu-close-button {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 32px;\n margin-top: 4px;\n margin-bottom: 4px;\n padding-right: 8px; }\n .igv-ui-colorpicker-menu-close-button i.fa {\n display: block;\n margin-left: 4px;\n margin-right: 4px;\n color: #5f5f5f; }\n .igv-ui-colorpicker-menu-close-button i.fa:hover,\n .igv-ui-colorpicker-menu-close-button i.fa:focus,\n .igv-ui-colorpicker-menu-close-button i.fa:active {\n cursor: pointer;\n color: #0f0f0f; }\n\n.igv-ui-generic-dialog-container {\n box-sizing: content-box;\n position: fixed;\n top: 0;\n left: 0;\n width: 300px;\n height: 200px;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n z-index: 2048;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-header {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-header div {\n margin-right: 4px;\n margin-bottom: 2px;\n height: 12px;\n width: 12px;\n color: #7F7F7F; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-header div:hover {\n cursor: pointer;\n color: #444; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-one-liner {\n color: #373737;\n width: 95%;\n height: 24px;\n line-height: 24px;\n text-align: left;\n margin-top: 8px;\n padding-left: 8px;\n overflow-wrap: break-word;\n background-color: white; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input {\n margin-top: 8px;\n width: 95%;\n height: 24px;\n color: #373737;\n line-height: 24px;\n padding-left: 8px;\n background-color: white;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input div {\n width: 30%;\n height: 100%;\n font-size: 16px;\n text-align: right;\n padding-right: 8px;\n background-color: white; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input input {\n display: block;\n height: 100%;\n width: 100%;\n padding-left: 4px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n color: #373737;\n text-align: left;\n outline: none;\n border-style: solid;\n border-width: thin;\n border-color: #7F7F7F;\n background-color: white; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input input {\n width: 50%;\n font-size: 16px; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-input {\n margin-top: 8px;\n width: calc(100% - 16px);\n height: 24px;\n color: #373737;\n line-height: 24px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-input input {\n display: block;\n height: 100%;\n width: 100%;\n padding-left: 4px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n color: #373737;\n text-align: left;\n outline: none;\n border-style: solid;\n border-width: thin;\n border-color: #7F7F7F;\n background-color: white; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-input input {\n font-size: 16px; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel {\n width: 100%;\n height: 28px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div {\n margin-top: 32px;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n width: 75px;\n height: 28px;\n line-height: 28px;\n text-align: center;\n border-color: transparent;\n border-style: solid;\n border-width: thin;\n border-radius: 2px; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div:first-child {\n margin-left: 32px;\n margin-right: 0;\n background-color: #5ea4e0; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div:last-child {\n margin-left: 0;\n margin-right: 32px;\n background-color: #c4c4c4; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div:first-child:hover {\n cursor: pointer;\n background-color: #3b5c7f; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div:last-child:hover {\n cursor: pointer;\n background-color: #7f7f7f; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok {\n width: 100%;\n height: 36px;\n margin-top: 32px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok div {\n width: 98px;\n height: 36px;\n line-height: 36px;\n text-align: center;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n border-color: white;\n border-style: solid;\n border-width: thin;\n border-radius: 4px;\n background-color: #2B81AF; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok div:hover {\n cursor: pointer;\n background-color: #25597f; }\n\n.igv-ui-generic-container {\n box-sizing: content-box;\n position: absolute;\n z-index: 2048;\n background-color: white;\n cursor: pointer;\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n justify-content: flex-start;\n align-items: center; }\n .igv-ui-generic-container div:first-child {\n cursor: move;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n height: 24px;\n width: 100%;\n background-color: #dddddd; }\n .igv-ui-generic-container div:first-child div {\n display: block;\n color: #5f5f5f;\n cursor: pointer;\n width: 14px;\n height: 14px;\n margin-right: 8px;\n margin-bottom: 4px; }\n\n.igv-ui-dialog {\n z-index: 2048;\n position: fixed;\n width: fit-content;\n height: fit-content;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n background-color: white;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400; }\n .igv-ui-dialog .igv-ui-dialog-header {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee; }\n .igv-ui-dialog .igv-ui-dialog-header div {\n margin-right: 4px;\n margin-bottom: 2px;\n height: 12px;\n width: 12px;\n color: #7F7F7F; }\n .igv-ui-dialog .igv-ui-dialog-header div:hover {\n cursor: pointer;\n color: #444; }\n .igv-ui-dialog .igv-ui-dialog-one-liner {\n width: 95%;\n height: 24px;\n line-height: 24px;\n text-align: left;\n margin: 8px;\n overflow-wrap: break-word;\n background-color: white;\n font-weight: bold; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel {\n width: 100%;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel div {\n margin: 16px;\n margin-top: 32px;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n width: 75px;\n height: 28px;\n line-height: 28px;\n text-align: center;\n border-color: transparent;\n border-style: solid;\n border-width: thin;\n border-radius: 2px; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel div:first-child {\n background-color: #5ea4e0; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel div:last-child {\n background-color: #c4c4c4; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel div:first-child:hover {\n cursor: pointer;\n background-color: #3b5c7f; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel div:last-child:hover {\n cursor: pointer;\n background-color: #7f7f7f; }\n .igv-ui-dialog .igv-ui-dialog-ok {\n width: 100%;\n height: 36px;\n margin-top: 32px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-ui-dialog .igv-ui-dialog-ok div {\n width: 98px;\n height: 36px;\n line-height: 36px;\n text-align: center;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n border-color: white;\n border-style: solid;\n border-width: thin;\n border-radius: 4px;\n background-color: #2B81AF; }\n .igv-ui-dialog .igv-ui-dialog-ok div:hover {\n cursor: pointer;\n background-color: #25597f; }\n\n.igv-ui-panel, .igv-ui-panel-column, .igv-ui-panel-row {\n z-index: 2048;\n background-color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n display: flex;\n justify-content: flex-start;\n align-items: flex-start; }\n\n.igv-ui-panel-column {\n display: flex;\n flex-direction: column; }\n\n.igv-ui-panel-row {\n display: flex;\n flex-direction: row; }\n\n.igv-ui-textbox {\n background-color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n display: flex;\n justify-content: flex-start;\n align-items: flex-start; }\n\n/*# sourceMappingURL=igv-ui.css.map */\n';
19094
+ var css = '.igv-ui-popover {\n cursor: default;\n position: absolute;\n z-index: 2048;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: 1px;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n background-color: white; }\n .igv-ui-popover > div:first-child {\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee; }\n .igv-ui-popover > div:first-child > div:first-child {\n margin-left: 4px; }\n .igv-ui-popover > div:first-child > div:last-child {\n margin-right: 4px;\n height: 12px;\n width: 12px;\n color: #7F7F7F; }\n .igv-ui-popover > div:first-child > div:last-child:hover {\n cursor: pointer;\n color: #444; }\n .igv-ui-popover > div:last-child {\n overflow-y: auto;\n overflow-x: hidden;\n max-height: 400px;\n max-width: 800px;\n background-color: white; }\n .igv-ui-popover > div:last-child > div {\n -webkit-user-select: text;\n -moz-user-select: text;\n -ms-user-select: text;\n user-select: text;\n margin-left: 4px;\n margin-right: 4px;\n min-width: 220px;\n overflow-x: hidden;\n text-overflow: ellipsis;\n white-space: nowrap; }\n .igv-ui-popover > div:last-child > div > span {\n font-weight: bolder; }\n .igv-ui-popover > div:last-child hr {\n width: 100%; }\n\n.igv-ui-alert-dialog-container {\n box-sizing: content-box;\n position: absolute;\n z-index: 2048;\n top: 50%;\n left: 50%;\n width: 400px;\n height: 200px;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n outline: none;\n font-family: \"Open Sans\", sans-serif;\n font-size: 15px;\n font-weight: 400;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center; }\n .igv-ui-alert-dialog-container > div:first-child {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee; }\n .igv-ui-alert-dialog-container > div:first-child div:first-child {\n padding-left: 8px; }\n .igv-ui-alert-dialog-container .igv-ui-alert-dialog-body {\n -webkit-user-select: text;\n -moz-user-select: text;\n -ms-user-select: text;\n user-select: text;\n color: #373737;\n width: 100%;\n height: calc(100% - 24px - 64px);\n overflow-y: scroll; }\n .igv-ui-alert-dialog-container .igv-ui-alert-dialog-body .igv-ui-alert-dialog-body-copy {\n margin: 16px;\n width: auto;\n height: auto;\n overflow-wrap: break-word;\n word-break: break-word;\n background-color: white;\n border: unset; }\n .igv-ui-alert-dialog-container > div:last-child {\n width: 100%;\n margin-bottom: 10px;\n background-color: white;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: center;\n align-items: center; }\n .igv-ui-alert-dialog-container > div:last-child div {\n margin: unset;\n width: 40px;\n height: 30px;\n line-height: 30px;\n text-align: center;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n font-weight: 400;\n border-color: #2B81AF;\n border-style: solid;\n border-width: thin;\n border-radius: 4px;\n background-color: #2B81AF; }\n .igv-ui-alert-dialog-container > div:last-child div:hover {\n cursor: pointer;\n border-color: #25597f;\n background-color: #25597f; }\n\n.igv-ui-color-swatch {\n position: relative;\n box-sizing: content-box;\n display: flex;\n flex-flow: row;\n flex-wrap: wrap;\n justify-content: center;\n align-items: center;\n width: 32px;\n height: 32px;\n border-style: solid;\n border-width: 2px;\n border-color: white;\n border-radius: 4px; }\n\n.igv-ui-color-swatch:hover {\n border-color: dimgray; }\n\n.igv-ui-colorpicker-menu-close-button {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 32px;\n margin-top: 4px;\n margin-bottom: 4px;\n padding-right: 8px; }\n .igv-ui-colorpicker-menu-close-button i.fa {\n display: block;\n margin-left: 4px;\n margin-right: 4px;\n color: #5f5f5f; }\n .igv-ui-colorpicker-menu-close-button i.fa:hover,\n .igv-ui-colorpicker-menu-close-button i.fa:focus,\n .igv-ui-colorpicker-menu-close-button i.fa:active {\n cursor: pointer;\n color: #0f0f0f; }\n\n.igv-ui-generic-dialog-container {\n box-sizing: content-box;\n position: fixed;\n top: 0;\n left: 0;\n width: 300px;\n height: 200px;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n z-index: 2048;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-header {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-header div {\n margin-right: 4px;\n margin-bottom: 2px;\n height: 12px;\n width: 12px;\n color: #7F7F7F; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-header div:hover {\n cursor: pointer;\n color: #444; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-one-liner {\n color: #373737;\n width: 95%;\n height: 24px;\n line-height: 24px;\n text-align: left;\n margin-top: 8px;\n padding-left: 8px;\n overflow-wrap: break-word;\n background-color: white; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input {\n margin-top: 8px;\n width: 95%;\n height: 24px;\n color: #373737;\n line-height: 24px;\n padding-left: 8px;\n background-color: white;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input div {\n width: 30%;\n height: 100%;\n font-size: 16px;\n text-align: right;\n padding-right: 8px;\n background-color: white; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input input {\n display: block;\n height: 100%;\n width: 100%;\n padding-left: 4px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n color: #373737;\n text-align: left;\n outline: none;\n border-style: solid;\n border-width: thin;\n border-color: #7F7F7F;\n background-color: white; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input input {\n width: 50%;\n font-size: 16px; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-input {\n margin-top: 8px;\n width: calc(100% - 16px);\n height: 24px;\n color: #373737;\n line-height: 24px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-input input {\n display: block;\n height: 100%;\n width: 100%;\n padding-left: 4px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n color: #373737;\n text-align: left;\n outline: none;\n border-style: solid;\n border-width: thin;\n border-color: #7F7F7F;\n background-color: white; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-input input {\n font-size: 16px; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel {\n width: 100%;\n height: 28px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div {\n margin-top: 32px;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n width: 75px;\n height: 28px;\n line-height: 28px;\n text-align: center;\n border-color: transparent;\n border-style: solid;\n border-width: thin;\n border-radius: 2px; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div:first-child {\n margin-left: 32px;\n margin-right: 0;\n background-color: #5ea4e0; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div:last-child {\n margin-left: 0;\n margin-right: 32px;\n background-color: #c4c4c4; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div:first-child:hover {\n cursor: pointer;\n background-color: #3b5c7f; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div:last-child:hover {\n cursor: pointer;\n background-color: #7f7f7f; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok {\n width: 100%;\n height: 36px;\n margin-top: 32px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok div {\n width: 98px;\n height: 36px;\n line-height: 36px;\n text-align: center;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n border-color: white;\n border-style: solid;\n border-width: thin;\n border-radius: 4px;\n background-color: #2B81AF; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok div:hover {\n cursor: pointer;\n background-color: #25597f; }\n\n.igv-ui-generic-container {\n box-sizing: content-box;\n position: absolute;\n z-index: 2048;\n background-color: white;\n cursor: pointer;\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n justify-content: flex-start;\n align-items: center; }\n .igv-ui-generic-container > div:first-child {\n cursor: move;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n height: 24px;\n width: 100%;\n background-color: #dddddd; }\n .igv-ui-generic-container > div:first-child > div {\n display: block;\n color: #5f5f5f;\n cursor: pointer;\n width: 14px;\n height: 14px;\n margin-right: 8px;\n margin-bottom: 4px; }\n\n.igv-ui-dialog {\n z-index: 2048;\n position: fixed;\n width: fit-content;\n height: fit-content;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n background-color: white;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400; }\n .igv-ui-dialog .igv-ui-dialog-header {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee; }\n .igv-ui-dialog .igv-ui-dialog-header div {\n margin-right: 4px;\n margin-bottom: 2px;\n height: 12px;\n width: 12px;\n color: #7F7F7F; }\n .igv-ui-dialog .igv-ui-dialog-header div:hover {\n cursor: pointer;\n color: #444; }\n .igv-ui-dialog .igv-ui-dialog-one-liner {\n width: 95%;\n height: 24px;\n line-height: 24px;\n text-align: left;\n margin: 8px;\n overflow-wrap: break-word;\n background-color: white;\n font-weight: bold; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel {\n width: 100%;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel div {\n margin: 16px;\n margin-top: 32px;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n width: 75px;\n height: 28px;\n line-height: 28px;\n text-align: center;\n border-color: transparent;\n border-style: solid;\n border-width: thin;\n border-radius: 2px; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel div:first-child {\n background-color: #5ea4e0; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel div:last-child {\n background-color: #c4c4c4; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel div:first-child:hover {\n cursor: pointer;\n background-color: #3b5c7f; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel div:last-child:hover {\n cursor: pointer;\n background-color: #7f7f7f; }\n .igv-ui-dialog .igv-ui-dialog-ok {\n width: 100%;\n height: 36px;\n margin-top: 32px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-ui-dialog .igv-ui-dialog-ok div {\n width: 98px;\n height: 36px;\n line-height: 36px;\n text-align: center;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n border-color: white;\n border-style: solid;\n border-width: thin;\n border-radius: 4px;\n background-color: #2B81AF; }\n .igv-ui-dialog .igv-ui-dialog-ok div:hover {\n cursor: pointer;\n background-color: #25597f; }\n\n.igv-ui-panel, .igv-ui-panel-column, .igv-ui-panel-row {\n z-index: 2048;\n background-color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n display: flex;\n justify-content: flex-start;\n align-items: flex-start; }\n\n.igv-ui-panel-column {\n display: flex;\n flex-direction: column; }\n\n.igv-ui-panel-row {\n display: flex;\n flex-direction: row; }\n\n.igv-ui-textbox {\n background-color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n display: flex;\n justify-content: flex-start;\n align-items: flex-start; }\n\n/*# sourceMappingURL=igv-ui.css.map */\n';
19093
19095
 
19094
19096
  var style = document.createElement('style');
19095
19097
  style.setAttribute('type', 'text/css');
@@ -19709,6 +19711,7 @@ const knownFileExtensions = new Set([
19709
19711
  "vcf",
19710
19712
  "bb",
19711
19713
  "bigbed",
19714
+ "biginteract",
19712
19715
  "bw",
19713
19716
  "bigwig",
19714
19717
  "bam",
@@ -19859,6 +19862,7 @@ function inferTrackType(config) {
19859
19862
  case "bed":
19860
19863
  case "bigbed":
19861
19864
  case "bb":
19865
+ case "biginteract":
19862
19866
  return "bedtype"
19863
19867
  default:
19864
19868
  return "annotation"
@@ -19897,6 +19901,218 @@ function translateDeprecatedTypes(config) {
19897
19901
  }
19898
19902
  }
19899
19903
 
19904
+ /*
19905
+ * The MIT License (MIT)
19906
+ *
19907
+ * Copyright (c) 2014 Broad Institute
19908
+ *
19909
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
19910
+ * of this software and associated documentation files (the "Software"), to deal
19911
+ * in the Software without restriction, including without limitation the rights
19912
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19913
+ * copies of the Software, and to permit persons to whom the Software is
19914
+ * furnished to do so, subject to the following conditions:
19915
+ *
19916
+ * The above copyright notice and this permission notice shall be included in
19917
+ * all copies or substantial portions of the Software.
19918
+ *
19919
+ *
19920
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19921
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19922
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19923
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19924
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19925
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19926
+ * THE SOFTWARE.
19927
+ */
19928
+
19929
+ /**
19930
+ * Test if the given value is a string or number. Not using typeof as it fails on boxed primitives.
19931
+ *
19932
+ * @param value
19933
+ * @returns boolean
19934
+ */
19935
+
19936
+ function isSimpleType(value) {
19937
+ const simpleTypes = new Set(["boolean", "number", "string", "symbol"]);
19938
+ const valueType = typeof value;
19939
+ return (value !== undefined && (simpleTypes.has(valueType) || value.substring || value.toFixed))
19940
+ }
19941
+
19942
+ function buildOptions(config, options) {
19943
+
19944
+ var defaultOptions = {
19945
+ oauthToken: config.oauthToken,
19946
+ headers: config.headers,
19947
+ withCredentials: config.withCredentials,
19948
+ filename: config.filename
19949
+ };
19950
+
19951
+ return Object.assign(defaultOptions, options)
19952
+ }
19953
+
19954
+ /**
19955
+ * isMobile test from http://detectmobilebrowsers.com
19956
+ * TODO -- improve UI design so this isn't neccessary
19957
+ * @returns {boolean}
19958
+ */
19959
+
19960
+ // igv.isMobile = function () {
19961
+ //
19962
+ // const a = (navigator.userAgent || navigator.vendor || window.opera);
19963
+ // return (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a) ||
19964
+ // /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4)))
19965
+ //
19966
+ // }
19967
+
19968
+ const doAutoscale = function (features) {
19969
+ var min, max;
19970
+
19971
+ if (features.length > 0) {
19972
+ min = Number.MAX_VALUE;
19973
+ max = -Number.MAX_VALUE;
19974
+
19975
+ features.forEach(function (f) {
19976
+ if (!Number.isNaN(f.value)) {
19977
+ min = Math.min(min, f.value);
19978
+ max = Math.max(max, f.value);
19979
+ }
19980
+ });
19981
+
19982
+ // Insure we have a zero baseline
19983
+ if (max > 0) min = Math.min(0, min);
19984
+ if (max < 0) max = 0;
19985
+ } else {
19986
+ // No features -- default
19987
+ min = 0;
19988
+ max = 100;
19989
+ }
19990
+
19991
+ return {min: min, max: max}
19992
+ };
19993
+
19994
+ const validateLocusExtent = function (chromosomeLengthBP, extent, minimumBP) {
19995
+
19996
+ let ss = extent.start;
19997
+ let ee = extent.end;
19998
+
19999
+ if (undefined === ee) {
20000
+
20001
+ ss -= minimumBP / 2;
20002
+ ee = ss + minimumBP;
20003
+
20004
+ if (ee > chromosomeLengthBP) {
20005
+ ee = chromosomeLengthBP;
20006
+ ss = ee - minimumBP;
20007
+ } else if (ss < 0) {
20008
+ ss = 0;
20009
+ ee = minimumBP;
20010
+ }
20011
+
20012
+ } else if (ee - ss < minimumBP) {
20013
+
20014
+ const center = (ee + ss) / 2;
20015
+
20016
+ if (center - minimumBP / 2 < 0) {
20017
+ ss = 0;
20018
+ ee = ss + minimumBP;
20019
+ } else if (center + minimumBP / 2 > chromosomeLengthBP) {
20020
+ ee = chromosomeLengthBP;
20021
+ ss = ee - minimumBP;
20022
+ } else {
20023
+ ss = center - minimumBP / 2;
20024
+ ee = ss + minimumBP;
20025
+ }
20026
+ }
20027
+
20028
+ extent.start = Math.ceil(ss);
20029
+ extent.end = Math.floor(ee);
20030
+ };
20031
+
20032
+ /*!
20033
+ * is-number <https://github.com/jonschlinkert/is-number>
20034
+ *
20035
+ * Copyright (c) 2014-present, Jon Schlinkert.
20036
+ * Released under the MIT License.
20037
+ */
20038
+
20039
+ const isNumber = function (num) {
20040
+ if (typeof num === 'number') {
20041
+ return num - num === 0
20042
+ }
20043
+ if (typeof num === 'string' && num.trim() !== '') {
20044
+ return Number.isFinite ? Number.isFinite(+num) : isFinite(+num)
20045
+ }
20046
+ return false
20047
+ };
20048
+
20049
+ async function getFilename(url) {
20050
+ if (isString$3(url) && url.startsWith("https://drive.google.com")) {
20051
+ // This will fail if Google API key is not defined
20052
+ if (getApiKey() === undefined) {
20053
+ throw Error("Google drive is referenced, but API key is not defined. An API key is required for Google Drive access")
20054
+ }
20055
+ const json = await getDriveFileInfo(url);
20056
+ return json.originalFileName || json.name
20057
+ } else {
20058
+ return getFilename$1(url)
20059
+ }
20060
+ }
20061
+
20062
+ function prettyBasePairNumber(raw) {
20063
+
20064
+ var denom,
20065
+ units,
20066
+ value,
20067
+ floored;
20068
+
20069
+ if (raw > 1e7) {
20070
+ denom = 1e6;
20071
+ units = " mb";
20072
+ } else if (raw > 1e4) {
20073
+
20074
+ denom = 1e3;
20075
+ units = " kb";
20076
+
20077
+ value = raw / denom;
20078
+ floored = Math.floor(value);
20079
+ return numberFormatter$1(floored) + units
20080
+ } else {
20081
+ return numberFormatter$1(raw) + " bp"
20082
+ }
20083
+
20084
+ value = raw / denom;
20085
+ floored = Math.floor(value);
20086
+
20087
+ return floored.toString() + units
20088
+ }
20089
+
20090
+
20091
+ function isDataURL(obj) {
20092
+ return (isString$3(obj) && obj.startsWith("data:"))
20093
+ }
20094
+
20095
+ function createColumn(columnContainer, className) {
20096
+ const column = div$1({class: className});
20097
+ columnContainer.appendChild(column);
20098
+ }
20099
+
20100
+
20101
+ function insertElementBefore(element, referenceNode) {
20102
+ referenceNode.parentNode.insertBefore(element, referenceNode);
20103
+ }
20104
+
20105
+ function insertElementAfter(element, referenceNode) {
20106
+ referenceNode.parentNode.insertBefore(element, referenceNode.nextSibling);
20107
+ }
20108
+
20109
+ /**
20110
+ * Test to see if page is loaded in a secure context, that is by https or is localhost.
20111
+ */
20112
+ function isSecureContext() {
20113
+ return window.location.protocol === "https:" || window.location.hostname === "localhost"
20114
+ }
20115
+
19900
20116
  /*
19901
20117
  * The MIT License (MIT)
19902
20118
  *
@@ -20275,7 +20491,7 @@ class Viewport {
20275
20491
 
20276
20492
  constructor(trackView, viewportColumn, referenceFrame, width) {
20277
20493
 
20278
- this.guid = guid$1();
20494
+ this.guid = guid$2();
20279
20495
  this.trackView = trackView;
20280
20496
  this.referenceFrame = referenceFrame;
20281
20497
 
@@ -22054,7 +22270,7 @@ GenomicInterval.prototype.containsRange = function (range) {
22054
22270
 
22055
22271
  const splitLines$3 = splitLines$5;
22056
22272
 
22057
- const reservedProperties = new Set(['fastaURL', 'indexURL', 'cytobandURL', 'indexed']);
22273
+ const reservedProperties = new Set(['fastaURL', 'indexURL', 'compressedIndexURL', 'cytobandURL', 'indexed']);
22058
22274
 
22059
22275
  class FastaSequence {
22060
22276
 
@@ -22062,6 +22278,7 @@ class FastaSequence {
22062
22278
 
22063
22279
  this.file = reference.fastaURL;
22064
22280
  this.indexFile = reference.indexURL || reference.indexFile || this.file + ".fai";
22281
+ this.compressedIndexFile = reference.compressedIndexURL || false;
22065
22282
  this.withCredentials = reference.withCredentials;
22066
22283
  this.chromosomeNames = [];
22067
22284
  this.chromosomes = {};
@@ -22147,77 +22364,292 @@ class FastaSequence {
22147
22364
  }
22148
22365
  }
22149
22366
 
22150
- async readSequence(chr, qstart, qend) {
22151
22367
 
22152
- // let offset;
22153
- // let start;
22154
- // let end;
22155
- // let basesPerLine;
22156
- // let nEndBytes;
22368
+ //Code is losely based on https://github.com/GMOD/bgzf-filehandle
22369
+ //Reworked however in orde to work with the igvxhr interface for loading files
22370
+ //Additionally, replaced calls to the Long.js interface with standard JS calls for ArrayBuffers and the associated views
22371
+ //
22372
+ //The compressed index is an array of blocks, with each block being a pair: compressed-position & uncompressed-position (both in bytes)
22373
+ async getCompressedIndex() {
22374
+ const GZI_NUM_BYTES_OFFSET = 8;
22375
+ const GZI_NUM_BYTES_BLOCK = 8;
22376
+ if (this.compressedIndex) {
22377
+ return this.compressedIndex
22378
+ }
22379
+ if (!this.compressedIndexFile) {
22380
+ this.compressedIndex = [];
22381
+ return this.compressedIndex
22382
+ }
22383
+ //In contrast to the 'normal' reference (for which the index is chromosome based), this index is block-based
22384
+ //As such there is not need to make it a hash. An array is sufficient.
22385
+ this.compressedIndex = [];
22386
+ const gziData = await igvxhr.loadArrayBuffer(this.compressedIndexFile, buildOptions(this.config));
22387
+ const givenFileSize = gziData.byteLength;
22388
+ if (givenFileSize < GZI_NUM_BYTES_OFFSET) {
22389
+ console.log("Cannot parse GZI index file: length (" + givenFileSize + " bytes) is insufficient to determine content of index.");
22390
+ return this.compressedIndex
22391
+ }
22392
+ //First 8 bytes are a little endian unsigned bigint (64bit), indicating the number of blocks in the index.
22393
+ const numBlocksBuffer = gziData.slice(0, GZI_NUM_BYTES_OFFSET);
22394
+ const numBlocks = Number((new DataView(numBlocksBuffer)).getBigUint64(0, true));
22395
+ //The remainder of the gzi content are pairs of little endian unsigned bigint (64bit) numbers.
22396
+ //The first of the pair is the compressed position of a block
22397
+ //The second of the pair is the uncompressed position of a block
22398
+
22399
+ //Sanity check:
22400
+ //Is the size of the array-buffer (of the entire file) correct with regards to the number of blocks detailled by the first 8 bytes of the file?
22401
+ //Total file-size should be:
22402
+ // 8 + 2*(num_entries*8) bytes, with the first 8 bytes indicating the number of entries
22403
+ const expectedFileSize = GZI_NUM_BYTES_OFFSET + numBlocks * 2 * GZI_NUM_BYTES_BLOCK;
22404
+ if (givenFileSize != expectedFileSize) {
22405
+ console.log("Incorrect file size of reference genome index. Expected : " + expectedFileSize + ". Received : " + givenFileSize);
22406
+ return this.compressedIndex
22407
+ }
22408
+
22409
+ //Push the first block to the index: the first block always has positions 0 for both the compressed and uncompressed file
22410
+ this.compressedIndex.push([0, 0]);
22411
+
22412
+ //Further process all the blocks of the GZI index, and keep them in memory
22413
+ for (let blockNumber = 0; blockNumber < numBlocks; blockNumber++) {
22414
+ const bufferBlockStart = GZI_NUM_BYTES_OFFSET + blockNumber * 2 * GZI_NUM_BYTES_BLOCK;
22415
+ const bufferBlockEnd = GZI_NUM_BYTES_OFFSET + blockNumber * 2 * GZI_NUM_BYTES_BLOCK + 2 * GZI_NUM_BYTES_BLOCK;
22416
+ const bufferBlock = gziData.slice(bufferBlockStart, bufferBlockEnd);
22417
+ const viewBlock = new DataView(bufferBlock);
22418
+ const compressedPosition = Number(viewBlock.getBigUint64(0, true)); //First 8 bytes
22419
+ const uncompressedPosition = Number(viewBlock.getBigUint64(GZI_NUM_BYTES_BLOCK, true)); //Last 8 bytes
22420
+ this.compressedIndex.push([compressedPosition, uncompressedPosition]);
22421
+ }
22422
+ return this.compressedIndex
22423
+ }
22424
+
22425
+ //The Fasta-index gives a byte-position of the chromosomal sequences within the FASTA file.
22426
+ //These locations need to be remapped to the locations within the zipped reference genome, using the GZI index
22427
+ //This function provides this functionality by
22428
+ //1) taking the indicated start/stop byte locations within the UNCOMPRESSED FASTA file
22429
+ //2) remapping these byte locations to the correct blocks (and associated positions) within the COMPRESSED FASTA file
22430
+ //Subsequently, the calling method can then extract the correct blocks from the compressed FASTA files and uncompressed the data
22431
+ async getRelevantCompressedBlockNumbers(queryPositionStart, queryPositionEnd) {
22432
+ const UNCOMPRESSED_POSITION = 1;
22433
+ //Fallback for impossible values
22434
+ if (queryPositionStart < 0 || queryPositionEnd < 0 || queryPositionEnd < queryPositionStart) {
22435
+ console.log("Incompatible query positions for reference-genome. Start:" + queryPositionStart + " | End:" + queryPositionEnd);
22436
+ return []
22437
+ }
22438
+ //Ensure compressed index is loaded
22439
+ await this.getCompressedIndex();
22440
+ let result = [];
22441
+ //Now search for the correct block-numbers (going from 0 to length(compressed-index)) which overlap with the provided byte-positions
22442
+ const lowestBlockNumber = 0;
22443
+ const highestBlockNumber = this.compressedIndex.length - 1;
22444
+ //Failsafe if for some reason the compressed index wasn't loaded or doesn't contain any data
22445
+ if (this.compressedIndex.length == 0) {
22446
+ console.log("Compressed index does not contain any content");
22447
+ return []
22448
+ }
22449
+ //Failsafe: if the queryPositionStart is greater than the uncompressed-position of the final block,
22450
+ //then this final block is the only possible result
22451
+ if (queryPositionStart > (this.compressedIndex)[highestBlockNumber][UNCOMPRESSED_POSITION]) {
22452
+ return [highestBlockNumber]
22453
+ }
22454
+
22455
+ //Rather than doing a linear search over all blocks, a binary search is done for speed considerations
22456
+ //We are searching for the highest block number for which its position is smaller than the query start position
22457
+ //Afterwards we will simply expand the blocks until the entire query range is covered
22458
+ let searchLow = lowestBlockNumber;
22459
+ let searchHigh = highestBlockNumber;
22460
+ let searchPosition = Math.floor(this.compressedIndex.length / 2);
22461
+ let maxIterations = this.compressedIndex.length + 1;
22462
+ let solutionFound = false;
22463
+ //instead of doing a while(true), this for-loop prevents eternal loops in case of issues
22464
+ for (let iteration = 0; iteration < maxIterations; iteration++) {
22465
+ const searchUncompressedPosition = (this.compressedIndex)[searchPosition][UNCOMPRESSED_POSITION];
22466
+ const nextSearchUncompressedPosition = (searchPosition < (this.compressedIndex.length - 1)) ? (this.compressedIndex)[searchPosition + 1][UNCOMPRESSED_POSITION] : Infinity;
22467
+ //The query position lies within the current search block
22468
+ if (searchUncompressedPosition <= queryPositionStart && nextSearchUncompressedPosition > queryPositionStart) {
22469
+ solutionFound = true;
22470
+ break //searchPosition is the correct block number index
22471
+ }
22472
+ //Current block lies before the query position
22473
+ else if (searchUncompressedPosition < queryPositionStart) {
22474
+ searchLow = searchPosition + 1;
22475
+ }
22476
+ //Current block lies after the query position
22477
+ else {
22478
+ searchHigh = searchPosition - 1;
22479
+ }
22480
+ searchPosition = Math.ceil((searchHigh - searchLow) / 2) + searchLow;
22481
+ }
22482
+ //If for some reason the binary search did not reveal a correct block index, then we return the empty result
22483
+ if (!solutionFound) {
22484
+ console.log("No blocks within compressed index found that correspond with query positions " + queryPositionStart + "," + queryPositionEnd);
22485
+ console.log(this.compressedIndex);
22486
+ return []
22487
+ }
22488
+
22489
+ //Now extend the result by adding additional blocks until the entire query range is covered
22490
+ result.push(searchPosition);
22491
+ for (let blockIndex = searchPosition + 1; blockIndex < this.compressedIndex.length; blockIndex++) {
22492
+ result.push(blockIndex);
22493
+ const blockUncompressedPosition = (this.compressedIndex)[blockIndex][UNCOMPRESSED_POSITION];
22494
+ if (blockUncompressedPosition >= queryPositionEnd) {
22495
+ break
22496
+ }
22497
+ }
22498
+
22499
+ //It is possible that the query end position lies AFTER the start of the final block
22500
+ //If this is the case, we add a 'fake' negative index which will be interpreted by the loadAndUncompressBlocks method as an indicator
22501
+ //to read until the end of the file
22502
+ const finalRelevantBlock = result[result.length - 1];
22503
+ const finalIndexBlock = this.compressedIndex.length - 1;
22504
+ if (finalRelevantBlock === finalIndexBlock && (this.compressedIndex)[finalRelevantBlock][UNCOMPRESSED_POSITION] < queryPositionEnd) {
22505
+ result.push(-1);
22506
+ }
22507
+
22508
+ return result
22509
+ }
22510
+
22511
+
22512
+ //Load the content from the blockIndices.
22513
+ //This is done on a per-block basis
22514
+ //Content of the first block will be trimmed in order to match the expected offset
22515
+ async loadAndUncompressBlocks(blockIndices, startByte) {
22516
+ const COMPRESSED_POSITION = 0;
22517
+ const UNCOMPRESSED_POSITION = 1;
22518
+ //Normally the compressed index should already exist, we're just makeing sure here
22519
+ await this.getCompressedIndex();
22520
+
22521
+ if (blockIndices.length == 0) {
22522
+ return ""
22523
+ }
22524
+
22525
+ //Storing data in seperate array with indices in order to assert order due to async behaviour of loops
22526
+ let resultCache = Array(blockIndices.length - 1);
22527
+ for (let i = 0; i < blockIndices.length - 1; i++) {
22528
+ const currentBlockNumber = blockIndices[i];
22529
+ const currentBlockInfo = (this.compressedIndex)[currentBlockNumber];
22530
+ const currentBlockCompressedPosition = currentBlockInfo[COMPRESSED_POSITION];
22531
+
22532
+ const nextBlockNumber = blockIndices[i + 1];
22533
+ let compressedBytes = [];
22534
+ if (nextBlockNumber != -1) { //default : read current entire block only
22535
+ const nextBlockInfo = (this.compressedIndex)[nextBlockNumber];
22536
+ const nextBlockCompressedPosition = nextBlockInfo[COMPRESSED_POSITION];
22537
+ const compressedLength = nextBlockCompressedPosition - currentBlockCompressedPosition;
22538
+ compressedBytes = await igvxhr.loadArrayBuffer(this.file, buildOptions(this.config, {
22539
+ range: {
22540
+ start: currentBlockCompressedPosition,
22541
+ size: compressedLength
22542
+ }
22543
+ }));
22544
+ } else { // special case for query within final block: read until the end of the file
22545
+ compressedBytes = await igvxhr.loadArrayBuffer(this.file, buildOptions(this.config, {
22546
+ range: {
22547
+ start: currentBlockCompressedPosition
22548
+ }
22549
+ }));
22550
+ }
22551
+ //now unzip the compressed bytes, and store them in the resultCache
22552
+ const uncompressedBytes = await unbgzf(compressedBytes);
22553
+ resultCache[i] = uncompressedBytes;
22554
+ }
22555
+
22556
+ //Iterate over the result cache, create sequences from the data, and create a full sequence string from the data
22557
+ let result = "";
22558
+ for (let i = 0; i < resultCache.length; i++) {
22559
+ for (let j = 0; j < resultCache[i].length; j++) {
22560
+ const c = String.fromCharCode(resultCache[i][j]);
22561
+ result = result + c;
22562
+ }
22563
+ }
22564
+
22565
+ //postprocess this data: because entire blocks are read we need to remove the first N bases of the first used block,
22566
+ //which are not included in the original query positions
22567
+ const firstBlockInfo = (this.compressedIndex)[blockIndices[0]];
22568
+ const offset = startByte - firstBlockInfo[UNCOMPRESSED_POSITION];
22569
+ result = result.substring(offset);
22570
+
22571
+ return result
22572
+ }
22573
+
22574
+
22575
+ async readSequence(chr, qstart, qend) {
22157
22576
 
22158
22577
  await this.getIndex();
22578
+ await this.getCompressedIndex(); //This will work even if no compressed index file is set
22159
22579
 
22160
22580
  const idxEntry = this.index[chr];
22161
22581
  if (!idxEntry) {
22162
22582
  console.log("No index entry for chr: " + chr);
22163
-
22164
22583
  // Tag interval with null so we don't try again
22165
22584
  this.interval = new GenomicInterval(chr, qstart, qend, null);
22166
22585
  return null
22586
+ }
22167
22587
 
22168
- } else {
22588
+ const start = Math.max(0, qstart); // qstart should never be < 0
22589
+ const end = Math.min(idxEntry.size, qend);
22590
+ const bytesPerLine = idxEntry.bytesPerLine;
22591
+ const basesPerLine = idxEntry.basesPerLine;
22592
+ const position = idxEntry.position;
22593
+ const nEndBytes = bytesPerLine - basesPerLine;
22594
+ const startLine = Math.floor(start / basesPerLine);
22595
+ const endLine = Math.floor(end / basesPerLine);
22596
+ const base0 = startLine * basesPerLine; // Base at beginning of start line
22597
+ const offset = start - base0;
22598
+ const startByte = position + startLine * bytesPerLine + offset;
22599
+ const base1 = endLine * basesPerLine;
22600
+ const offset1 = end - base1;
22601
+ const endByte = position + endLine * bytesPerLine + offset1 - 1;
22602
+ const byteCount = endByte - startByte + 1;
22603
+
22604
+ if (byteCount <= 0) {
22605
+ console.error("No sequence for " + chr + ":" + qstart + "-" + qend);
22606
+ return null
22607
+ }
22169
22608
 
22170
- const start = Math.max(0, qstart); // qstart should never be < 0
22171
- const end = Math.min(idxEntry.size, qend);
22172
- const bytesPerLine = idxEntry.bytesPerLine;
22173
- const basesPerLine = idxEntry.basesPerLine;
22174
- const position = idxEntry.position;
22175
- const nEndBytes = bytesPerLine - basesPerLine;
22176
- const startLine = Math.floor(start / basesPerLine);
22177
- const endLine = Math.floor(end / basesPerLine);
22178
- const base0 = startLine * basesPerLine; // Base at beginning of start line
22179
- const offset = start - base0;
22180
- const startByte = position + startLine * bytesPerLine + offset;
22181
- const base1 = endLine * basesPerLine;
22182
- const offset1 = end - base1;
22183
- const endByte = position + endLine * bytesPerLine + offset1 - 1;
22184
- const byteCount = endByte - startByte + 1;
22185
-
22186
- let allBytes;
22187
- if (byteCount <= 0) {
22188
- console.error("No sequence for " + chr + ":" + qstart + "-" + qend);
22189
- } else {
22190
- allBytes = await igvxhr.load(this.file, buildOptions(this.config, {
22191
- range: {
22192
- start: startByte,
22193
- size: byteCount
22194
- }
22195
- }));
22609
+ //If the compressed index file is set, then we are dealing with a compressed genome sequence
22610
+ //The selection of startByte/endByte is done for the non-compressed genome sequence.
22611
+ //These need to be 'converted' to the correct byte positions in the compressed genome sequence,
22612
+ //by making use of the compressed index (GZI file)
22613
+ let allBytes;
22614
+ if (!this.compressedIndexFile) {
22615
+ allBytes = await igvxhr.load(this.file, buildOptions(this.config, {
22616
+ range: {
22617
+ start: startByte,
22618
+ size: byteCount
22619
+ }
22620
+ }));
22621
+ } else {
22622
+ let relevantBlockIndices = await this.getRelevantCompressedBlockNumbers(startByte, endByte);
22623
+ if (relevantBlockIndices.length === 0) {
22624
+ console.log("No blocks in the compressed index that correspond with the requested byte positions (" + startByte + "," + endByte + ")");
22625
+ return null
22196
22626
  }
22627
+ allBytes = await this.loadAndUncompressBlocks(relevantBlockIndices, startByte);
22628
+ }
22197
22629
 
22198
- if (!allBytes) {
22199
- return null
22200
- } else {
22201
- let nBases,
22202
- seqBytes = "",
22203
- srcPos = 0,
22204
- allBytesLength = allBytes.length;
22630
+ if (!allBytes) {
22631
+ return null
22632
+ }
22205
22633
 
22206
- if (offset > 0) {
22207
- nBases = Math.min(end - start, basesPerLine - offset);
22208
- seqBytes += allBytes.substr(srcPos, nBases);
22209
- srcPos += (nBases + nEndBytes);
22210
- }
22634
+ let nBases,
22635
+ seqBytes = "",
22636
+ srcPos = 0,
22637
+ allBytesLength = allBytes.length;
22211
22638
 
22212
- while (srcPos < allBytesLength) {
22213
- nBases = Math.min(basesPerLine, allBytesLength - srcPos);
22214
- seqBytes += allBytes.substr(srcPos, nBases);
22215
- srcPos += (nBases + nEndBytes);
22216
- }
22639
+ if (offset > 0) {
22640
+ nBases = Math.min(end - start, basesPerLine - offset);
22641
+ seqBytes += allBytes.substr(srcPos, nBases);
22642
+ srcPos += (nBases + nEndBytes);
22643
+ }
22217
22644
 
22218
- return seqBytes
22219
- }
22645
+ while (srcPos < allBytesLength) {
22646
+ nBases = Math.min(basesPerLine, allBytesLength - srcPos);
22647
+ seqBytes += allBytes.substr(srcPos, nBases);
22648
+ srcPos += (nBases + nEndBytes);
22220
22649
  }
22650
+
22651
+ return seqBytes
22652
+
22221
22653
  }
22222
22654
  }
22223
22655
 
@@ -22329,7 +22761,7 @@ const Cytoband = function (start, end, name, typestain) {
22329
22761
  }
22330
22762
  };
22331
22763
 
22332
- const _version = "2.10.5";
22764
+ const _version = "2.11.2";
22333
22765
  function version() {
22334
22766
  return _version
22335
22767
  }
@@ -22856,7 +23288,9 @@ class TrackViewport extends Viewport {
22856
23288
  }
22857
23289
 
22858
23290
  stopSpinner() {
22859
- this.$spinner.hide();
23291
+ if (this.$spinner) {
23292
+ this.$spinner.hide();
23293
+ }
22860
23294
  }
22861
23295
 
22862
23296
  checkZoomIn() {
@@ -23183,7 +23617,7 @@ class TrackViewport extends Viewport {
23183
23617
  const str = (this.trackView.track.name || this.trackView.track.id).replace(/\W/g, '');
23184
23618
 
23185
23619
  const index = this.browser.referenceFrameList.indexOf(this.referenceFrame);
23186
- const id = `${str}_referenceFrame_${index}_guid_${guid$1()}`;
23620
+ const id = `${str}_referenceFrame_${index}_guid_${guid$2()}`;
23187
23621
 
23188
23622
  this.drawSVGWithContext(context, width, height, id, 0, 0, 0);
23189
23623
 
@@ -23206,7 +23640,7 @@ class TrackViewport extends Viewport {
23206
23640
  const str = (this.trackView.track.name || this.trackView.track.id).replace(/\W/g, '');
23207
23641
 
23208
23642
  const index = this.browser.referenceFrameList.indexOf(this.referenceFrame);
23209
- const id = `${str}_referenceFrame_${index}_guid_${guid$1()}`;
23643
+ const id = `${str}_referenceFrame_${index}_guid_${guid$2()}`;
23210
23644
 
23211
23645
  const {top: yScrollDelta} = this.$content.position();
23212
23646
 
@@ -23225,7 +23659,7 @@ class TrackViewport extends Viewport {
23225
23659
  renderTrackLabelSVG(context, tx, ty, width, height) {
23226
23660
 
23227
23661
  const str = (this.trackView.track.name || this.trackView.track.id).replace(/\W/g, '');
23228
- const id = `${str}_track_label_guid_${guid$1()}`;
23662
+ const id = `${str}_track_label_guid_${guid$2()}`;
23229
23663
 
23230
23664
  context.saveWithTranslationAndClipRect(id, tx, ty, width, height, 0);
23231
23665
 
@@ -23552,28 +23986,16 @@ class TrackViewport extends Viewport {
23552
23986
  str = track.description();
23553
23987
  } else if (track.description) {
23554
23988
  str = `<div>${track.description}</div>`;
23555
- } else {
23556
- if (track.url) {
23557
- if (isFile(track.url)) {
23558
- str = `<div><b>Filename: </b>${track.url.name}`;
23559
- } else {
23560
- str = `<div><b>URL: </b>${track.url}`;
23561
- }
23562
- } else {
23563
- str = track.name;
23564
-
23565
- }
23566
23989
  }
23567
23990
 
23568
- if (this.popover) {
23569
- this.popover.dispose();
23991
+ if (str) {
23992
+ if (this.popover) {
23993
+ this.popover.dispose();
23994
+ }
23995
+ this.popover = new Popover(this.browser.columnContainer, (track.name || ''));
23996
+ this.popover.presentContentWithEvent(event, str);
23570
23997
  }
23571
-
23572
- this.popover = new Popover(this.browser.columnContainer, (track.name || 'unnamed'));
23573
-
23574
- this.popover.presentContentWithEvent(event, str);
23575
23998
  }
23576
-
23577
23999
  }
23578
24000
 
23579
24001
  removeTrackLabelClickHandler(trackLabel) {
@@ -28743,7 +29165,7 @@ class TrackBase {
28743
29165
  this.config = config;
28744
29166
  this.url = config.url;
28745
29167
  this.type = config.type;
28746
- this.description = config.description;
29168
+
28747
29169
  this.supportHiDPI = config.supportHiDPI === undefined ? true : config.supportHiDPI;
28748
29170
 
28749
29171
  if (config.name || config.label) {
@@ -28781,6 +29203,15 @@ class TrackBase {
28781
29203
  this.onclick = config.onclick;
28782
29204
  config.onclick = undefined; // functions cannot be saved in sessions, clear it here.
28783
29205
  }
29206
+
29207
+ if (config.description) {
29208
+ // Override description -- displayed when clicking on track label. Convert to function if neccessary
29209
+ if (typeof config.description === 'function') {
29210
+ this.description = config.description;
29211
+ } else {
29212
+ this.description = () => config.description;
29213
+ }
29214
+ }
28784
29215
  }
28785
29216
 
28786
29217
  get name() {
@@ -28823,7 +29254,7 @@ class TrackBase {
28823
29254
  for (let key of Object.keys(state)) {
28824
29255
  if (key.startsWith("_")) continue // transient property
28825
29256
  const value = this[key];
28826
- if (value && (isSimpleType(value) || typeof value === "boolean")) {
29257
+ if (value && (isSimpleType(value) || typeof value === "boolean" || key === "metadata")) {
28827
29258
  state[key] = value;
28828
29259
  }
28829
29260
  }
@@ -28837,16 +29268,18 @@ class TrackBase {
28837
29268
  state.max = this.dataRange.max;
28838
29269
  }
28839
29270
 
29271
+
28840
29272
  // Check for non-json-if-yable properties. Perhaps we should test what can be saved.
28841
29273
  for (let key of Object.keys(state)) {
28842
- if (typeof state[key] === 'function') {
29274
+ const value = state[key];
29275
+ if (typeof value === 'function') {
28843
29276
  throw Error(`Property '${key}' of track '${this.name} is a function. Functions cannot be saved in sessions.`)
28844
29277
  }
28845
- if (isFile(state[key])) {
29278
+ if (value instanceof File) { // Test specifically for File. Other types of File-like objects might be savable
28846
29279
  const str = `Track ${this.name} is a local file. Sessions cannot be saved with local file references.`;
28847
29280
  throw Error(str)
28848
29281
  }
28849
- if (state[key] instanceof Promise) {
29282
+ if (value instanceof Promise) {
28850
29283
  throw Error(`Property '${key}' of track '${this.name} is a Promise. Promises cannot be saved in sessions.`)
28851
29284
  }
28852
29285
  }
@@ -29092,6 +29525,49 @@ class TrackBase {
29092
29525
 
29093
29526
  }
29094
29527
 
29528
+ /**
29529
+ * Default track description -- displayed on click of track label. This can be overriden in the track
29530
+ * configuration, or in subclasses.
29531
+ */
29532
+ description() {
29533
+
29534
+ const wrapKeyValue = (k, v) => `<div class="igv-track-label-popup-shim"><b>${k}: </b>${v}</div>`;
29535
+
29536
+ let str = '<div class="igv-track-label-popup">';
29537
+ if (this.url) {
29538
+ if (isFile(this.url)) {
29539
+ str += wrapKeyValue('Filename', this.url.name);
29540
+ } else {
29541
+ str += wrapKeyValue('URL', this.url);
29542
+ }
29543
+ } else {
29544
+ str = this.name;
29545
+ }
29546
+ if (this.config) {
29547
+ if (this.config.metadata) {
29548
+ for (let key of Object.keys(this.config.metadata)) {
29549
+ const value = this.config.metadata[key];
29550
+ str += wrapKeyValue(key, value);
29551
+ }
29552
+ }
29553
+
29554
+ // Add any config properties that are capitalized
29555
+ for (let key of Object.keys(this.config)) {
29556
+ if (key.startsWith("_")) continue // transient property
29557
+ let first = key.substr(0, 1);
29558
+ if (first !== first.toLowerCase()) {
29559
+ const value = this.config[key];
29560
+ if (value && isSimpleType(value)) {
29561
+ str += wrapKeyValue(key, value);
29562
+ }
29563
+ }
29564
+ }
29565
+
29566
+ }
29567
+ str += '</div>';
29568
+ return str
29569
+ }
29570
+
29095
29571
  static getCravatLink(chr, position, ref, alt, genomeID) {
29096
29572
 
29097
29573
  if ("hg38" === genomeID || "GRCh38" === genomeID) {
@@ -29138,6 +29614,8 @@ function paintAxis(ctx, pixelWidth, pixelHeight) {
29138
29614
  return
29139
29615
  }
29140
29616
 
29617
+ let flipAxis = (undefined === this.flipAxis) ? false : this.flipAxis;
29618
+
29141
29619
  IGVGraphics.fillRect(ctx, 0, 0, pixelWidth, pixelHeight, {'fillStyle': "rgb(255, 255, 255)"});
29142
29620
 
29143
29621
  reference = 0.95 * pixelWidth;
@@ -29152,7 +29630,7 @@ function paintAxis(ctx, pixelWidth, pixelHeight) {
29152
29630
 
29153
29631
  // tick
29154
29632
  IGVGraphics.strokeLine(ctx, x1, y1, x2, y2, font);
29155
- IGVGraphics.fillText(ctx, prettyPrint(this.dataRange.max), x1 + 4, y1 + 12, font);
29633
+ IGVGraphics.fillText(ctx, prettyPrint(flipAxis ? this.dataRange.min : this.dataRange.max), x1 + 4, y1 + 12, font);
29156
29634
 
29157
29635
  //shim = 0.25 * 0.125;
29158
29636
  y1 = y2 = (1.0 - shim) * pixelHeight;
@@ -29161,7 +29639,7 @@ function paintAxis(ctx, pixelWidth, pixelHeight) {
29161
29639
 
29162
29640
  // tick
29163
29641
  IGVGraphics.strokeLine(ctx, x1, y1, x2, y2, font);
29164
- IGVGraphics.fillText(ctx, prettyPrint(this.dataRange.min), x1 + 4, y1 - 4, font);
29642
+ IGVGraphics.fillText(ctx, prettyPrint(flipAxis ? this.dataRange.max : this.dataRange.min), x1 + 4, y1 - 4, font);
29165
29643
 
29166
29644
  IGVGraphics.strokeLine(ctx, a.x, a.y, b.x, b.y, font);
29167
29645
 
@@ -30566,6 +31044,75 @@ const chrColorMap$1 = {
30566
31044
  "chr48": "rgb(20, 255, 177)",
30567
31045
  };
30568
31046
 
31047
+ class ChordSetManager {
31048
+
31049
+ constructor(config) {
31050
+ this.tracks = [];
31051
+ this.chordSets = [];
31052
+ }
31053
+
31054
+ addChordSet(chordSet) {
31055
+
31056
+ // If a chord set with this name exists replace it (same track, same region)
31057
+ this.chordSets = this.chordSets.filter(g => g.name !== chordSet.name);
31058
+ this.chordSets.push(chordSet);
31059
+
31060
+ let track = this.tracks.find(t => chordSet.trackName === t.name);
31061
+ if (track) {
31062
+ track.chordSets = track.chordSets.filter(cs => cs.name !== chordSet.name);
31063
+ track.chordSets.push(chordSet);
31064
+ }
31065
+ if (!track) {
31066
+ track = new IGVTrack(chordSet);
31067
+ this.tracks.push(track);
31068
+ }
31069
+ }
31070
+
31071
+ clearChords() {
31072
+ this.tracks = [];
31073
+ this.chordSets = [];
31074
+ }
31075
+
31076
+ getTrack(name) {
31077
+ return this.tracks.find(t => name === t.name)
31078
+ }
31079
+
31080
+ getChordset(name) {
31081
+ return this.chordSets.find(cs => name === cs.name)
31082
+ }
31083
+
31084
+ }
31085
+
31086
+ class IGVTrack {
31087
+ constructor(chordSet) {
31088
+ this.name = chordSet.trackName;
31089
+ this.color = chordSet.trackColor;
31090
+ this.visible = true;
31091
+ this.chordSets = [chordSet];
31092
+ this.id = guid$1();
31093
+ }
31094
+
31095
+ get chords() {
31096
+ if (this.chordSets.length === 1) {
31097
+ return this.chordSets[0].chords
31098
+ }
31099
+ const chords = [];
31100
+ for (let cs of this.chordSets) {
31101
+ for (let c of cs.chords) {
31102
+ chords.push(c);
31103
+ }
31104
+ }
31105
+ return chords
31106
+ }
31107
+ }
31108
+
31109
+
31110
+ function guid$1() {
31111
+ return ("0000" + (Math.random() * Math.pow(36, 4) << 0).toString(36)).slice(-4)
31112
+ }
31113
+
31114
+ const EXP5 = Math.exp(5);
31115
+
30569
31116
  class CircularView {
30570
31117
 
30571
31118
  static isInstalled() {
@@ -30590,6 +31137,8 @@ class CircularView {
30590
31137
  if (CircularView.isInstalled()) {
30591
31138
 
30592
31139
  this.parent = parent;
31140
+ this.groupByTrack = config.groupByTrack === true;
31141
+ this.chordManager = new ChordSetManager(config);
30593
31142
 
30594
31143
  // wrapper for toolbar and circular-view container
30595
31144
  const wrapper = document.createElement('div');
@@ -30597,7 +31146,8 @@ class CircularView {
30597
31146
  parent.appendChild(wrapper);
30598
31147
 
30599
31148
  // toolbar
30600
- this.createToolbarAndTrackPanel(wrapper);
31149
+ this.createControls(wrapper);
31150
+ this.resetControlPanel();
30601
31151
 
30602
31152
  // circular view container
30603
31153
  const element = document.createElement('div');
@@ -30609,8 +31159,6 @@ class CircularView {
30609
31159
  this.setAssembly(config.assembly);
30610
31160
  }
30611
31161
 
30612
- this.tracks = config.tracks || [];
30613
-
30614
31162
  this.width = config.width || 500;
30615
31163
  this.height = config.height || 500;
30616
31164
  this.setSize(this.width, this.height);
@@ -30620,55 +31168,47 @@ class CircularView {
30620
31168
  }
30621
31169
  }
30622
31170
 
30623
- createToolbarAndTrackPanel(parent) {
30624
-
30625
- let element;
31171
+ createControls(parent) {
30626
31172
 
30627
31173
  // toolbar
30628
- element = document.createElement('div');
30629
- element.className = 'igv-circview-toolbar';
30630
- parent.appendChild(element);
30631
- this.toolbar = element;
31174
+ const toolbarDiv = document.createElement('div');
31175
+ toolbarDiv.className = 'igv-circview-toolbar';
31176
+ parent.appendChild(toolbarDiv);
31177
+ this.toolbar = toolbarDiv;
30632
31178
 
30633
-
30634
- // track panel
30635
- element = document.createElement('div');
30636
- element.className = 'igv-circview-track-panel';
30637
- parent.appendChild(element);
30638
- this.trackPanel = element;
30639
-
30640
- this.trackPanel.style.display = 'none';
31179
+ // control panel
31180
+ const controlPanelDiv = document.createElement('div');
31181
+ controlPanelDiv.className = 'igv-circview-track-panel';
31182
+ parent.appendChild(controlPanelDiv);
31183
+ this.controlPanel = controlPanelDiv;
31184
+ this.controlPanel.style.display = 'none';
30641
31185
 
30642
31186
 
30643
31187
  // toolbar button container - Track Options - Clear All
30644
- let buttonContainer = document.createElement('div');
31188
+ const buttonContainer = document.createElement('div');
30645
31189
  buttonContainer.className = 'igv-circview-toolbar-button-container';
30646
31190
  this.toolbar.appendChild(buttonContainer);
30647
31191
 
30648
- // Track Options
30649
- this.trackPanelPresentationButton = document.createElement('div');
30650
- this.trackPanelPresentationButton.className = 'igv-circview-button';
30651
- buttonContainer.appendChild(this.trackPanelPresentationButton);
30652
- this.trackPanelPresentationButton.innerText = 'none' === this.trackPanel.style.display ? 'Show Track Options' : 'Hide Track Options';
30653
- this.trackPanelPresentationButton.addEventListener('click', (event) => {
30654
-
30655
- const trackPanelRows = this.trackPanel.querySelectorAll('div');
30656
-
31192
+ // Show Controls
31193
+ this.showControlsButton = document.createElement('div');
31194
+ this.showControlsButton.className = 'igv-circview-button';
31195
+ buttonContainer.appendChild(this.showControlsButton);
31196
+ this.showControlsButton.innerText = 'none' === this.controlPanel.style.display ? 'Show Controls' : 'Hide Controls';
31197
+ this.showControlsButton.addEventListener('click', (event) => {
31198
+ const trackPanelRows = this.controlPanel.querySelectorAll('div');
30657
31199
  if (trackPanelRows.length > 0) {
30658
31200
 
30659
- if ('none' === this.trackPanel.style.display) {
30660
- this.trackPanel.style.display = 'flex';
30661
- event.target.innerText = 'Hide Track Options';
31201
+ if ('none' === this.controlPanel.style.display) {
31202
+ this.controlPanel.style.display = 'flex';
31203
+ event.target.innerText = 'Hide Controls';
30662
31204
  } else {
30663
- this.trackPanel.style.display = 'none';
30664
- event.target.innerText = 'Show Track Options';
31205
+ this.controlPanel.style.display = 'none';
31206
+ event.target.innerText = 'Show Controls';
30665
31207
  }
30666
-
30667
31208
  }
30668
-
30669
31209
  });
30670
31210
 
30671
- // Clear All Chords
31211
+ // Clear All
30672
31212
  let button = document.createElement('div');
30673
31213
  button.className = 'igv-circview-button';
30674
31214
  buttonContainer.appendChild(button);
@@ -30677,14 +31217,8 @@ class CircularView {
30677
31217
  this.clearChords();
30678
31218
  });
30679
31219
 
30680
-
30681
- // toolbar button container - Close Window
30682
- buttonContainer = document.createElement('div');
30683
- buttonContainer.className = 'igv-circview-toolbar-button-container';
30684
- this.toolbar.appendChild(buttonContainer);
30685
-
30686
- // Close Window
30687
- if(false !== this.config.showCloseButton) {
31220
+ // Close
31221
+ if (false !== this.config.showCloseButton) {
30688
31222
  button = document.createElement('div');
30689
31223
  button.className = 'igv-circview-button';
30690
31224
  buttonContainer.appendChild(button);
@@ -30693,60 +31227,115 @@ class CircularView {
30693
31227
  this.visible = false;
30694
31228
  });
30695
31229
  }
31230
+ }
30696
31231
 
31232
+ resetControlPanel() {
31233
+ this.controlPanel.innerHTML = '';
31234
+ this.controlPanel.appendChild(this.createGroupByCB());
31235
+ const chordSets = this.groupByTrack ? this.chordManager.tracks : this.chordManager.chordSets;
31236
+ for(let cs of chordSets) {
31237
+ this.addToControlPanel(cs);
31238
+ }
30697
31239
  }
30698
31240
 
30699
- addToTrackPanel(track) {
31241
+ createGroupByCB() {
31242
+ const groupByCB = document.createElement('input');
31243
+ groupByCB.type = 'checkbox';
31244
+ groupByCB.id = 'groupByCB';
31245
+ groupByCB.style.width = '1.4em';
31246
+ groupByCB.style.height = '1.4em';
31247
+ groupByCB.checked = this.groupByTrack;
30700
31248
 
30701
- // single track row - container for hide-button | color-picker-swatch | track-name
31249
+ groupByCB.onclick = (evt) => {
31250
+ this.groupByTrack = evt.target.checked;
31251
+ this.resetControlPanel();
31252
+ this.render();
31253
+ };
31254
+
31255
+ const groupByLabel = document.createElement('label');
31256
+ groupByLabel.for = 'groupByCB';
31257
+ groupByLabel.innerText = 'Group by track';
31258
+ groupByLabel.style.color = 'black';
31259
+ groupByLabel.style.paddingLeft = '10px';
30702
31260
  const trackPanelRow = document.createElement('div');
30703
- this.trackPanel.appendChild(trackPanelRow);
31261
+ trackPanelRow.style.width = '100%';
31262
+ trackPanelRow.style.paddingTop = '5px';
31263
+ trackPanelRow.style.paddingBottom = '5px';
31264
+ trackPanelRow.style.background = 'rgb(216, 230, 234)';
31265
+ trackPanelRow.appendChild(groupByCB);
31266
+ trackPanelRow.appendChild(groupByLabel);
31267
+ return trackPanelRow
31268
+ }
30704
31269
 
31270
+ addToControlPanel(chordSet) {
31271
+
31272
+ // single track row - container for hide-button | color-picker-swatch | track-name
31273
+ const row = document.createElement('div');
31274
+ this.controlPanel.appendChild(row);
30705
31275
 
30706
- let element;
30707
31276
 
30708
31277
  // track hide|show
30709
- element = document.createElement('div');
30710
- element.className = 'igv-circview-button';
30711
- trackPanelRow.appendChild(element);
30712
- element.innerText = true === track.visible ? 'Hide' : 'Show';
30713
- element.addEventListener('click', event => {
30714
- if (true === track.visible) {
30715
- this.hideTrack(track.id);
31278
+ const hideShowButton = document.createElement('div');
31279
+ hideShowButton.className = 'igv-circview-button';
31280
+ row.appendChild(hideShowButton);
31281
+ hideShowButton.innerText = true === chordSet.visible ? 'Hide' : 'Show';
31282
+ hideShowButton.addEventListener('click', event => {
31283
+ if (true === chordSet.visible) {
31284
+ this.hideTrack(chordSet.name);
30716
31285
  event.target.innerText = "Show";
30717
31286
  } else {
30718
- this.showTrack(track.id);
31287
+ this.showTrack(chordSet.name);
30719
31288
  event.target.innerText = "Hide";
30720
31289
  }
30721
-
30722
31290
  });
30723
31291
 
30724
- // track color
30725
- element.clientHeight;
30726
- const pickerButton = document.createElement('div');
30727
- pickerButton.className = 'igv-circview-button';
30728
- pickerButton.innerHTML = '&nbsp;&nbsp;&nbsp;&nbsp;'; // <- important for button to size properly
30729
- trackPanelRow.appendChild(pickerButton);
30730
- pickerButton.style.backgroundColor = setAlpha(track.color, 1);
31292
+ // The alpha range slider. Create this here so we can reference it from the color picker
31293
+ const alphaSlider = document.createElement('input');
31294
+ const valueToAlpha = (value) => Math.exp(value / 200) / EXP5;
31295
+ const alphaToValue = (alpha) => 200 * Math.log(alpha * EXP5);
31296
+
31297
+ // color
31298
+ const colorPickerButton = document.createElement('div');
31299
+ colorPickerButton.className = 'igv-circview-button';
31300
+ colorPickerButton.innerHTML = '&nbsp;&nbsp;&nbsp;&nbsp;'; // <- important for button to size properly
31301
+ row.appendChild(colorPickerButton);
31302
+ colorPickerButton.style.backgroundColor = setAlpha(chordSet.color, 1);
30731
31303
  const pickerConfig =
30732
31304
  {
30733
- parent: pickerButton,
31305
+ parent: colorPickerButton,
30734
31306
  popup: 'right',
30735
31307
  editorFormat: 'rgb',
30736
- color: track.color,
31308
+ color: chordSet.color,
30737
31309
  onChange: ({rgbaString}) => {
30738
- pickerButton.style.backgroundColor = setAlpha(rgbaString, 1);
30739
- this.setTrackColor(track.id, rgbaString);
31310
+ colorPickerButton.style.backgroundColor = setAlpha(rgbaString, 1);
31311
+ this.setTrackColor(chordSet.name, rgbaString);
31312
+ alphaSlider.value = alphaToValue(getAlpha(chordSet.color));
30740
31313
  }
30741
31314
  };
30742
-
30743
- new Picker(pickerConfig);
31315
+ const picker = new Picker(pickerConfig);
31316
+
31317
+ // alpha transparency
31318
+ alphaSlider.setAttribute('title', 'Adjust transparency of arcs');
31319
+ alphaSlider.type = 'range';
31320
+ //alphaSlider.className = 'igv-circview-alpha-slider'
31321
+ alphaSlider.style.width = '100px';
31322
+ alphaSlider.style.marginRight = '10px';
31323
+ alphaSlider.setAttribute('class', 'range');
31324
+ alphaSlider.setAttribute('min', '0');
31325
+ alphaSlider.setAttribute('max', '1000');
31326
+ alphaSlider.value = alphaToValue(getAlpha(chordSet.color));
31327
+ alphaSlider.oninput = () => {
31328
+ const v = valueToAlpha(alphaSlider.value);
31329
+ this.setTrackColor(chordSet.name, setAlpha(chordSet.color, v));
31330
+ picker.setColor(chordSet.color);
31331
+ };
31332
+ row.appendChild(alphaSlider);
30744
31333
 
30745
31334
  // track name
30746
- element = document.createElement('div');
30747
- element.style.color = 'black';
30748
- trackPanelRow.appendChild(element);
30749
- element.innerText = element.title = track.name;
31335
+ const trackNameDive = document.createElement('div');
31336
+ trackNameDive.style.color = 'black';
31337
+ row.appendChild(trackNameDive);
31338
+ trackNameDive.innerText = trackNameDive.title = chordSet.name;
30750
31339
 
30751
31340
  }
30752
31341
 
@@ -30760,7 +31349,7 @@ class CircularView {
30760
31349
  if (this.genomeId === igvGenome.id) {
30761
31350
  return
30762
31351
  }
30763
- this.tracks = [];
31352
+ this.chordManager.clearChords();
30764
31353
  this.genomeId = igvGenome.id;
30765
31354
  this.chrNames = new Set(igvGenome.chromosomes.map(chr => shortChrName$1(chr.name)));
30766
31355
 
@@ -30823,39 +31412,24 @@ class CircularView {
30823
31412
 
30824
31413
  addChords(newChords, options = {}) {
30825
31414
 
30826
- const name = options.track || options.name || "*";
31415
+ const tmp = options.track || options.name || "*";
31416
+ const trackName = tmp.split(' ')[0].replaceAll("%20", " ");
31417
+ const chordSetName = tmp.replaceAll("%20", " ");
31418
+
31419
+ const chordSet = {
31420
+ name: chordSetName,
31421
+ trackName: trackName,
31422
+ chords: newChords,
31423
+ color: options.color || "black",
31424
+ trackColor: options.trackColor || options.color || "black",
31425
+ visible: true,
31426
+ id: options.id || guid()
31427
+ };
30827
31428
 
30828
- let track = this.tracks.find(t => name === t.name);
31429
+ this.chordManager.addChordSet(chordSet);
30829
31430
 
30830
- // Override track options or create new track
30831
- if (track) {
30832
- if (options.color) {
30833
- track.color = options.color;
30834
- }
30835
- } else {
30836
- track =
30837
- {
30838
- name,
30839
- chords: [],
30840
- color: options.color || "black",
30841
- visible: true,
30842
- id: options.id || guid()
30843
- };
30844
- this.tracks.push(track);
30845
-
30846
- this.addToTrackPanel(track);
30847
- }
31431
+ this.resetControlPanel();
30848
31432
 
30849
- // Append chords to track
30850
- const currentIDs = new Set(track.chords.map(c => c.uniqueId));
30851
- for (let c of newChords) {
30852
- if (!currentIDs.has(c.uniqueId) &&
30853
- this.chrNames.has(shortChrName$1(c.refName)) &&
30854
- this.chrNames.has(shortChrName$1(c.mate.refName))) {
30855
- track.chords.push(c);
30856
- currentIDs.add(c.uniqueId);
30857
- }
30858
- }
30859
31433
  this.render();
30860
31434
  }
30861
31435
 
@@ -30881,8 +31455,9 @@ class CircularView {
30881
31455
  }
30882
31456
 
30883
31457
  clearChords() {
30884
- this.tracks = [];
30885
- this.trackPanel.innerHTML = '';
31458
+ //this.tracks = []
31459
+ this.chordManager.clearChords();
31460
+ this.resetControlPanel();
30886
31461
  this.render();
30887
31462
  }
30888
31463
 
@@ -30927,8 +31502,8 @@ class CircularView {
30927
31502
  this.parent.style.display = isVisible ? 'block' : 'none';
30928
31503
  }
30929
31504
 
30930
- hideTrack(trackID) {
30931
- let track = this.tracks.find(t => trackID === t.id);
31505
+ hideTrack(trackName) {
31506
+ let track = this.getTrack(trackName);
30932
31507
  if (track) {
30933
31508
  track.visible = false;
30934
31509
  this.render();
@@ -30937,34 +31512,44 @@ class CircularView {
30937
31512
  }
30938
31513
  }
30939
31514
 
30940
- showTrack(trackID) {
30941
- let idx = this.tracks.findIndex(t => trackID === t.id);
30942
- if (idx >= 0) {
30943
- const track = this.tracks[idx];
31515
+ showTrack(trackName) {
31516
+ let track = this.getTrack(trackName);
31517
+ if (track) {
30944
31518
  track.visible = true;
30945
- this.tracks.splice(idx, 1); // Change z-order
30946
- this.tracks.push(track);
30947
31519
  this.render();
30948
31520
  } else {
30949
- console.warn(`No track with name: ${name}`);
31521
+ console.warn(`No track with name: ${trackName}`);
30950
31522
  }
30951
31523
  }
30952
31524
 
31525
+ // showTrack(trackID) {
31526
+ // let idx = this.tracks.findIndex(t => trackID === t.id)
31527
+ // if (idx >= 0) {
31528
+ // const track = this.tracks[idx]
31529
+ // track.visible = true
31530
+ // this.tracks.splice(idx, 1) // Change z-order
31531
+ // this.tracks.push(track)
31532
+ // this.render()
31533
+ // } else {
31534
+ // console.warn(`No track with name: ${name}`)
31535
+ // }
31536
+ // }
31537
+
30953
31538
  // TODO -- remove corresponding row from track panel
30954
31539
  deleteTrack(trackID) {
30955
- let idx = this.tracks.findIndex(t => trackID === t.id);
31540
+ let idx = this.tracks.findIndex(t => trackID === t.name);
30956
31541
  if (idx >= 0) {
30957
31542
  this.tracks.splice(idx, 1);
30958
31543
  }
30959
31544
  this.render();
30960
31545
  }
30961
31546
 
30962
- getTrack(trackID) {
30963
- return this.tracks.find(t => trackID === t.id)
31547
+ getTrack(name) {
31548
+ return this.groupByTrack ? this.chordManager.getTrack(name) : this.chordManager.getChordset(name)
30964
31549
  }
30965
31550
 
30966
- setTrackColor(trackID, color) {
30967
- const t = this.getTrack(trackID);
31551
+ setTrackColor(name, color) {
31552
+ const t = this.getTrack(name);
30968
31553
  if (t) {
30969
31554
  t.color = color;
30970
31555
  const trackID = t.id;
@@ -30991,24 +31576,27 @@ class CircularView {
30991
31576
  // Remove all children from possible previous renders. React might do this for us when we render, but just in case.
30992
31577
  ReactDOM.unmountComponentAtNode(this.container);
30993
31578
 
30994
- const visibleTracks = this.tracks.filter(t => false !== t.visible);
31579
+
31580
+
31581
+ const visibleChordSets =
31582
+ (this.groupByTrack ? this.chordManager.tracks : this.chordManager.chordSets).filter(t => t.visible);
30995
31583
 
30996
31584
  const jbrowseTracks = [];
30997
31585
  const colors = [];
30998
31586
 
30999
- for (let trackConfig of visibleTracks) {
31587
+ for (let chordSet of visibleChordSets) {
31588
+
31000
31589
  jbrowseTracks.push({
31001
- trackId: trackConfig.id,
31002
- name: trackConfig.name,
31590
+ trackId: chordSet.id,
31591
+ name: chordSet.name,
31003
31592
  assemblyNames: ['forIGV'],
31004
31593
  type: 'VariantTrack',
31005
31594
  adapter: {
31006
31595
  type: 'FromConfigAdapter',
31007
- features: trackConfig.chords,
31596
+ features: chordSet.chords,
31008
31597
  }
31009
31598
  });
31010
- colors.push(trackConfig.color);
31011
-
31599
+ colors.push(chordSet.color);
31012
31600
  }
31013
31601
 
31014
31602
  this.viewState = createViewState({
@@ -31017,7 +31605,7 @@ class CircularView {
31017
31605
  });
31018
31606
 
31019
31607
  // Set view colors
31020
- for (let i = 0; i < visibleTracks.length; i++) {
31608
+ for (let i = 0; i < visibleChordSets.length; i++) {
31021
31609
  this.viewState.config.tracks[i].displays[0].renderer.strokeColor.set(colors[i]);
31022
31610
  //this.viewState.config.tracks[i].displays[0].renderer.strokeColor.set("jexl:get(feature, 'color') || 'black'");
31023
31611
  //this.viewState.config.tracks[i].displays[0].renderer.strokeColorSelected.set("jexl:get(feature, 'highlightColor') || 'red'");
@@ -31028,10 +31616,11 @@ class CircularView {
31028
31616
 
31029
31617
  ReactDOM.render(this.element, this.container);
31030
31618
 
31031
- for (let i = 0; i < visibleTracks.length; i++) {
31619
+ const onChordClick = this.config.onChordClick || defaultOnChordClick;
31620
+ for (let i = 0; i < visibleChordSets.length; i++) {
31032
31621
  this.viewState.session.view.showTrack(this.viewState.config.tracks[i].trackId);
31033
- if (this.config.onChordClick) {
31034
- this.viewState.pluginManager.jexl.addFunction('onChordClick', this.config.onChordClick);
31622
+ if (onChordClick) {
31623
+ this.viewState.pluginManager.jexl.addFunction('onChordClick', onChordClick);
31035
31624
  this.viewState.config.tracks[i].displays[0].onChordClick.set(
31036
31625
  'jexl:onChordClick(feature, track, pluginManager)'
31037
31626
  );
@@ -31042,20 +31631,32 @@ class CircularView {
31042
31631
 
31043
31632
  function setAlpha(rgba, alpha) {
31044
31633
  const [a, b, c, ignore] = rgba.split(','); // rgba(r g b alpha)
31045
- return `${ a },${ b },${ c },${ alpha })`
31634
+ return `${a},${b},${c},${alpha})`
31635
+ }
31636
+
31637
+ function getAlpha(rgba) {
31638
+ if (rgba.startsWith("rgba(")) {
31639
+ return Number(rgba.split(',')[3].replace(')', ''))
31640
+ } else {
31641
+ return 1
31642
+ }
31046
31643
  }
31047
31644
 
31048
31645
  function shortChrName$1(chrName) {
31049
31646
  return chrName.startsWith("chr") ? chrName.substring(3) : chrName
31050
31647
  }
31051
31648
 
31649
+ function defaultOnChordClick(feature, chordTrack, pluginManager) {
31650
+ console.log(feature);
31651
+ }
31652
+
31052
31653
  function guid() {
31053
31654
  return ("0000" + (Math.random() * Math.pow(36, 4) << 0).toString(36)).slice(-4)
31054
31655
  }
31055
31656
 
31056
31657
  function embedCSS$1() {
31057
31658
 
31058
- const css = '.igv-circview-container {\n z-index: 2048;\n position: absolute;\n width: fit-content;\n height: fit-content;\n box-sizing: content-box;\n color: dimgray;\n font-family: \"Open Sans\", sans-serif;\n font-size: 12px;\n background-color: white;\n border-color: dimgray;\n border-style: solid;\n border-width: thin;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start; }\n\n.igv-circview-toolbar {\n position: relative;\n width: 100%;\n height: 32px;\n background-color: lightgrey;\n border-bottom-style: solid;\n border-bottom-color: dimgray;\n border-bottom-width: thin;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center; }\n\n.igv-circview-toolbar-button-container {\n height: 100%;\n width: fit-content;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center; }\n .igv-circview-toolbar-button-container > div {\n margin: 4px; }\n\n.igv-circview-track-panel {\n z-index: 1024;\n position: absolute;\n top: 33px;\n left: 0;\n width: 100%;\n height: fit-content;\n border-bottom-style: solid;\n border-bottom-color: dimgray;\n border-bottom-width: thin;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start; }\n .igv-circview-track-panel > div {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center; }\n .igv-circview-track-panel > div > div {\n margin: 4px; }\n\n.igv-circview-swatch-button {\n cursor: pointer;\n padding: 5px;\n width: 8px;\n height: 8px;\n border: 1px solid #8d8b8b;\n border-radius: 16px; }\n\n.igv-circview-button {\n cursor: pointer;\n padding: 5px;\n color: #444;\n vertical-align: middle;\n text-align: center;\n font-family: \"Open Sans\", sans-serif;\n font-size: 12px;\n border: 1px solid #8d8b8b;\n border-radius: 4px;\n background: #efefef;\n box-shadow: 0 0 5px -1px rgba(0, 0, 0, 0.2); }\n\n.igv-circview-button:hover {\n background: #efefef;\n box-shadow: 0 0 5px -1px rgba(0, 0, 0, 0.6); }\n\n.igv-circview-button:active {\n color: #007bff;\n box-shadow: 0 0 5px -1px rgba(0, 0, 0, 0.6); }\n\n/*# sourceMappingURL=circular-view.css.map */\n';
31659
+ const css = '.igv-circview-container {\n z-index: 2048;\n position: absolute;\n width: fit-content;\n height: fit-content;\n box-sizing: content-box;\n color: dimgray;\n font-family: \"Open Sans\", sans-serif;\n font-size: 12px;\n background-color: white;\n border-color: dimgray;\n border-style: solid;\n border-width: thin;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n}\n\n.igv-circview-toolbar {\n position: relative;\n width: 100%;\n height: 32px;\n background-color: lightgrey;\n border-bottom-style: solid;\n border-bottom-color: dimgray;\n border-bottom-width: thin;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n}\n\n.igv-circview-toolbar-button-container {\n height: 100%;\n width: fit-content;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-circview-toolbar-button-container > div {\n margin: 4px;\n}\n\n.igv-circview-track-panel {\n z-index: 1024;\n position: absolute;\n top: 33px;\n left: 0;\n width: 100%;\n height: fit-content;\n border-bottom-style: solid;\n border-bottom-color: dimgray;\n border-bottom-width: thin;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n}\n.igv-circview-track-panel > div {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-circview-track-panel > div > div {\n margin: 4px;\n}\n\n.igv-circview-swatch-button {\n cursor: pointer;\n padding: 5px;\n width: 8px;\n height: 8px;\n border: 1px solid #8d8b8b;\n border-radius: 16px;\n}\n\n.igv-circview-button {\n cursor: pointer;\n padding: 5px;\n color: #444;\n vertical-align: middle;\n text-align: center;\n font-family: \"Open Sans\", sans-serif;\n font-size: 12px;\n border: 1px solid #8d8b8b;\n border-radius: 4px;\n background: #efefef;\n box-shadow: 0 0 5px -1px rgba(0, 0, 0, 0.2);\n}\n\n.igv-circview-button:hover {\n background: #efefef;\n box-shadow: 0 0 5px -1px rgba(0, 0, 0, 0.6);\n}\n\n.igv-circview-button:active {\n color: #007bff;\n box-shadow: 0 0 5px -1px rgba(0, 0, 0, 0.6);\n}\n\n/*# sourceMappingURL=circular-view.css.map */\n';
31059
31660
 
31060
31661
  const style = document.createElement('style');
31061
31662
  style.setAttribute('type', 'text/css');
@@ -32417,59 +33018,61 @@ class AlignmentTrack {
32417
33018
  list.push('<hr/>');
32418
33019
 
32419
33020
  const clickedObject = this.getClickedObject(clickState);
33021
+
32420
33022
  if (clickedObject) {
32421
33023
 
32422
33024
  const showSoftClips = this.parent.showSoftClips;
32423
33025
  const clickedAlignment = (typeof clickedObject.alignmentContaining === 'function') ?
32424
33026
  clickedObject.alignmentContaining(clickState.genomicLocation, showSoftClips) :
32425
33027
  clickedObject;
32426
-
32427
- if (clickedAlignment.isPaired() && clickedAlignment.isMateMapped()) {
32428
- list.push({
32429
- label: 'View mate in split screen',
32430
- click: () => {
32431
- if (clickedAlignment.mate) {
32432
- const referenceFrame = clickState.viewport.referenceFrame;
32433
- if (this.browser.genome.getChromosome(clickedAlignment.mate.chr)) {
32434
- this.highlightedAlignmentReadNamed = clickedAlignment.readName;
32435
- this.browser.presentMultiLocusPanel(clickedAlignment, referenceFrame);
32436
- } else {
32437
- Alert.presentAlert(`Reference does not contain chromosome: ${clickedAlignment.mate.chr}`);
33028
+ if (clickedAlignment) {
33029
+ if (clickedAlignment.isPaired() && clickedAlignment.isMateMapped()) {
33030
+ list.push({
33031
+ label: 'View mate in split screen',
33032
+ click: () => {
33033
+ if (clickedAlignment.mate) {
33034
+ const referenceFrame = clickState.viewport.referenceFrame;
33035
+ if (this.browser.genome.getChromosome(clickedAlignment.mate.chr)) {
33036
+ this.highlightedAlignmentReadNamed = clickedAlignment.readName;
33037
+ this.browser.presentMultiLocusPanel(clickedAlignment, referenceFrame);
33038
+ } else {
33039
+ Alert.presentAlert(`Reference does not contain chromosome: ${clickedAlignment.mate.chr}`);
33040
+ }
32438
33041
  }
32439
- }
32440
- },
32441
- init: undefined
32442
- });
32443
- }
32444
-
32445
- list.push({
32446
- label: 'View read sequence',
32447
- click: () => {
32448
- const alignment = clickedAlignment;
32449
- if (!alignment) return
32450
-
32451
- const seqstring = alignment.seq; //.map(b => String.fromCharCode(b)).join("");
32452
- if (!seqstring || "*" === seqstring) {
32453
- Alert.presentAlert("Read sequence: *");
32454
- } else {
32455
- Alert.presentAlert(seqstring);
32456
- }
33042
+ },
33043
+ init: undefined
33044
+ });
32457
33045
  }
32458
- });
32459
33046
 
32460
- if (isSecureContext()) {
32461
33047
  list.push({
32462
- label: 'Copy read sequence',
33048
+ label: 'View read sequence',
32463
33049
  click: () => {
32464
33050
  const alignment = clickedAlignment;
32465
33051
  if (!alignment) return
33052
+
32466
33053
  const seqstring = alignment.seq; //.map(b => String.fromCharCode(b)).join("");
32467
- navigator.clipboard.writeText(seqstring);
33054
+ if (!seqstring || "*" === seqstring) {
33055
+ Alert.presentAlert("Read sequence: *");
33056
+ } else {
33057
+ Alert.presentAlert(seqstring);
33058
+ }
32468
33059
  }
32469
33060
  });
32470
- }
32471
33061
 
32472
- list.push('<hr/>');
33062
+ if (isSecureContext()) {
33063
+ list.push({
33064
+ label: 'Copy read sequence',
33065
+ click: () => {
33066
+ const alignment = clickedAlignment;
33067
+ if (!alignment) return
33068
+ const seqstring = alignment.seq; //.map(b => String.fromCharCode(b)).join("");
33069
+ navigator.clipboard.writeText(seqstring);
33070
+ }
33071
+ });
33072
+ }
33073
+
33074
+ list.push('<hr/>');
33075
+ }
32473
33076
  }
32474
33077
 
32475
33078
  // Experimental JBrowse feature
@@ -32739,7 +33342,7 @@ function getChrColor(chr) {
32739
33342
  chrColorMap[chr] = color;
32740
33343
  return color
32741
33344
  } else {
32742
- const color = IGVColor.randomRGB();
33345
+ const color = IGVColor.randomRGB(0, 255);
32743
33346
  chrColorMap[chr] = color;
32744
33347
  return color
32745
33348
  }
@@ -32747,7 +33350,7 @@ function getChrColor(chr) {
32747
33350
 
32748
33351
  const chrColorMap = {
32749
33352
  "chrX": "rgb(204, 153, 0)",
32750
- "chrY": "rgb(153, 204, 0",
33353
+ "chrY": "rgb(153, 204, 0)",
32751
33354
  "chrUn": "rgb(50, 50, 50)",
32752
33355
  "chr1": "rgb(80, 80, 255)",
32753
33356
  "chrI": "rgb(139, 155, 187)",
@@ -33001,143 +33604,68 @@ class RulerViewport extends TrackViewport {
33001
33604
 
33002
33605
  }
33003
33606
 
33004
- /*
33005
- * The MIT License (MIT)
33006
- *
33007
- * Copyright (c) 2014 Broad Institute
33008
- *
33009
- * Permission is hereby granted, free of charge, to any person obtaining a copy
33010
- * of this software and associated documentation files (the "Software"), to deal
33011
- * in the Software without restriction, including without limitation the rights
33012
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
33013
- * copies of the Software, and to permit persons to whom the Software is
33014
- * furnished to do so, subject to the following conditions:
33015
- *
33016
- * The above copyright notice and this permission notice shall be included in
33017
- * all copies or substantial portions of the Software.
33018
- *
33019
- *
33020
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33021
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33022
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
33023
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33024
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
33025
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
33026
- * THE SOFTWARE.
33027
- */
33028
-
33029
- class IdeogramViewport extends TrackViewport {
33030
-
33031
- constructor(trackView, viewportColumn, referenceFrame, width) {
33032
- super(trackView, viewportColumn, referenceFrame, width);
33033
- }
33034
-
33035
- initializationHelper() {
33036
-
33037
- this.$ideogramCanvas = $$1('<canvas>', {class: 'igv-ideogram-canvas'});
33038
- this.$ideogramCanvas.insertBefore(this.$canvas);
33039
-
33040
- const canvas = this.$ideogramCanvas.get(0);
33041
- this.ideogram_ctx = canvas.getContext('2d');
33042
-
33043
- this.$canvas.remove();
33044
- this.canvas = undefined;
33045
- this.ctx = undefined;
33046
-
33047
- this.addMouseHandlers();
33048
-
33049
- }
33050
-
33051
- addMouseHandlers() {
33052
- this.addViewportClickHandler(this.$viewport.get(0));
33053
- }
33054
-
33055
- removeMouseHandlers() {
33056
- this.removeViewportClickHandler(this.$viewport.get(0));
33057
-
33058
- }
33059
-
33060
- addViewportClickHandler(viewport) {
33061
-
33062
- this.boundClickHandler = clickHandler.bind(this);
33063
- viewport.addEventListener('click', this.boundClickHandler);
33064
-
33065
- function clickHandler(event) {
33066
-
33067
- const {xNormalized, width} = translateMouseCoordinates$1(event, this.ideogram_ctx.canvas);
33068
- const {bpLength} = this.browser.genome.getChromosome(this.referenceFrame.chr);
33069
- const locusLength = this.referenceFrame.bpPerPixel * width;
33070
- const chrCoveragePercentage = locusLength / bpLength;
33071
-
33072
- let xPercentage = xNormalized;
33073
- if (xPercentage - (chrCoveragePercentage / 2.0) < 0) {
33074
- xPercentage = chrCoveragePercentage / 2.0;
33075
- }
33607
+ const viewportColumnManager =
33608
+ {
33609
+ createColumns: (columnContainer, count) => {
33076
33610
 
33077
- if (xPercentage + (chrCoveragePercentage / 2.0) > 1.0) {
33078
- xPercentage = 1.0 - chrCoveragePercentage / 2.0;
33611
+ for (let i = 0; i < count; i++) {
33612
+ if (0 === i) {
33613
+ createColumn(columnContainer, 'igv-column');
33614
+ } else {
33615
+ columnContainer.appendChild(div$1({class: 'igv-column-shim'}));
33616
+ createColumn(columnContainer, 'igv-column');
33617
+ }
33079
33618
  }
33080
33619
 
33081
- const ss = Math.round((xPercentage - (chrCoveragePercentage / 2.0)) * bpLength);
33082
- const ee = Math.round((xPercentage + (chrCoveragePercentage / 2.0)) * bpLength);
33083
-
33084
- this.referenceFrame.start = ss;
33085
- this.referenceFrame.end = ee;
33086
- this.referenceFrame.bpPerPixel = (ee - ss) / width;
33087
-
33088
- this.browser.updateViews(this.referenceFrame, this.browser.trackViews, true);
33089
-
33090
- }
33620
+ },
33091
33621
 
33092
- }
33622
+ removeColumnAtIndex: (i, column) => {
33623
+ const shim = 0 === i ? column.nextElementSibling : column.previousElementSibling;
33624
+ column.remove();
33625
+ shim.remove();
33626
+ },
33093
33627
 
33094
- removeViewportClickHandler(viewport) {
33095
- viewport.removeEventListener('click', this.boundClickHandler);
33096
- }
33628
+ insertAfter: referenceElement => {
33097
33629
 
33098
- setWidth(width) {
33099
- this.$viewport.width(width);
33100
- }
33630
+ const shim = div$1({class: 'igv-column-shim'});
33631
+ insertElementAfter(shim, referenceElement);
33101
33632
 
33102
- drawSVGWithContext(context, width, height, id, x, y, yClipOffset) {
33633
+ const column = div$1({class: 'igv-column'});
33634
+ insertElementAfter(column, shim);
33103
33635
 
33104
- context.saveWithTranslationAndClipRect(id, x, y, width, height, yClipOffset);
33636
+ return column
33637
+ },
33105
33638
 
33106
- this.trackView.track.draw({
33107
- context,
33108
- referenceFrame: this.referenceFrame,
33109
- pixelWidth: width,
33110
- pixelHeight: height
33111
- });
33639
+ insertBefore: (referenceElement, count) => {
33112
33640
 
33113
- context.restore();
33114
- }
33641
+ for (let i = 0; i < count; i++) {
33115
33642
 
33116
- async repaint() {
33117
- this.draw({referenceFrame: this.referenceFrame});
33118
- }
33643
+ const column = div$1({class: 'igv-column'});
33644
+ insertElementBefore(column, referenceElement);
33119
33645
 
33120
- draw({referenceFrame}) {
33646
+ if (count > 1 && i > 0) {
33647
+ const columnShim = div$1({class: 'igv-column-shim'});
33648
+ insertElementBefore(columnShim, column);
33649
+ }
33121
33650
 
33122
- this.$canvas.hide();
33651
+ }
33123
33652
 
33124
- IGVGraphics.configureHighDPICanvas(this.ideogram_ctx, this.$viewport.width(), this.$viewport.height());
33653
+ },
33125
33654
 
33126
- this.trackView.track.draw({
33127
- context: this.ideogram_ctx,
33128
- referenceFrame,
33129
- pixelWidth: this.$viewport.width(),
33130
- pixelHeight: this.$viewport.height()
33131
- });
33132
- }
33655
+ indexOfColumn: (columnContainer, column) => {
33133
33656
 
33134
- startSpinner() {
33135
- }
33657
+ const allColumns = columnContainer.querySelectorAll('.igv-column');
33136
33658
 
33137
- stopSpinner() {
33138
- }
33659
+ for (let i = 0; i < allColumns.length; i++) {
33660
+ const c = allColumns[ i ];
33661
+ if (c === column) {
33662
+ return i
33663
+ }
33664
+ }
33139
33665
 
33140
- }
33666
+ return undefined
33667
+ },
33668
+ };
33141
33669
 
33142
33670
  /*
33143
33671
  * The MIT License (MIT)
@@ -33164,185 +33692,157 @@ class IdeogramViewport extends TrackViewport {
33164
33692
  * THE SOFTWARE.
33165
33693
  */
33166
33694
 
33167
- /**
33168
- * Test if the given value is a string or number. Not using typeof as it fails on boxed primitives.
33169
- *
33170
- * @param value
33171
- * @returns boolean
33172
- */
33695
+ class IdeogramViewport extends TrackViewport {
33173
33696
 
33174
- function isSimpleType(value) {
33175
- const simpleTypes = new Set(["boolean", "number", "string", "symbol"]);
33176
- const valueType = typeof value;
33177
- return (value !== undefined && (simpleTypes.has(valueType) || value.substring || value.toFixed))
33178
- }
33697
+ constructor(trackView, viewportColumn, referenceFrame, width) {
33698
+ super(trackView, viewportColumn, referenceFrame, width);
33699
+ }
33179
33700
 
33180
- function buildOptions(config, options) {
33701
+ initializationHelper() {
33181
33702
 
33182
- var defaultOptions = {
33183
- oauthToken: config.oauthToken,
33184
- headers: config.headers,
33185
- withCredentials: config.withCredentials,
33186
- filename: config.filename
33187
- };
33703
+ this.$ideogramCanvas = $$1('<canvas>', {class: 'igv-ideogram-canvas'});
33704
+ this.$ideogramCanvas.insertBefore(this.$canvas);
33188
33705
 
33189
- return Object.assign(defaultOptions, options)
33190
- }
33706
+ const canvas = this.$ideogramCanvas.get(0);
33707
+ this.ideogram_ctx = canvas.getContext('2d');
33191
33708
 
33192
- /**
33193
- * isMobile test from http://detectmobilebrowsers.com
33194
- * TODO -- improve UI design so this isn't neccessary
33195
- * @returns {boolean}
33196
- */
33709
+ this.$canvas.remove();
33710
+ this.canvas = undefined;
33711
+ this.ctx = undefined;
33197
33712
 
33198
- // igv.isMobile = function () {
33199
- //
33200
- // const a = (navigator.userAgent || navigator.vendor || window.opera);
33201
- // return (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a) ||
33202
- // /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4)))
33203
- //
33204
- // }
33713
+ this.addMouseHandlers();
33205
33714
 
33206
- const doAutoscale = function (features) {
33207
- var min, max;
33715
+ }
33208
33716
 
33209
- if (features.length > 0) {
33210
- min = Number.MAX_VALUE;
33211
- max = -Number.MAX_VALUE;
33717
+ addMouseHandlers() {
33718
+ this.addBrowserObserver();
33719
+ this.addViewportClickHandler(this.$viewport.get(0));
33720
+ }
33212
33721
 
33213
- features.forEach(function (f) {
33214
- if (!Number.isNaN(f.value)) {
33215
- min = Math.min(min, f.value);
33216
- max = Math.max(max, f.value);
33722
+ removeMouseHandlers() {
33723
+ this.removeBrowserObserver();
33724
+ this.removeViewportClickHandler(this.$viewport.get(0));
33725
+
33726
+ }
33727
+
33728
+ addBrowserObserver() {
33729
+
33730
+ function observerHandler(referenceFrameList) {
33731
+ const column = this.$viewport.get(0).parentElement;
33732
+ if (null !== column) {
33733
+ const index = viewportColumnManager.indexOfColumn(this.browser.columnContainer, column);
33734
+ // console.log(`ideogram-viewport - locus-change-handler index(${ index }) ${ referenceFrameList[ index ].getLocusString() } ${ Date.now() } `)
33735
+ this.update(this.ideogram_ctx, this.$viewport.width(), this.$viewport.height(), referenceFrameList[ index ]);
33217
33736
  }
33218
- });
33737
+ }
33219
33738
 
33220
- // Insure we have a zero baseline
33221
- if (max > 0) min = Math.min(0, min);
33222
- if (max < 0) max = 0;
33223
- } else {
33224
- // No features -- default
33225
- min = 0;
33226
- max = 100;
33739
+ this.boundObserverHandler = observerHandler.bind(this);
33740
+ this.browser.on('locuschange', this.boundObserverHandler);
33227
33741
  }
33228
33742
 
33229
- return {min: min, max: max}
33230
- };
33743
+ removeBrowserObserver() {
33744
+ this.browser.off('locuschange', this.boundObserverHandler);
33745
+ }
33231
33746
 
33232
- const validateLocusExtent = function (chromosomeLengthBP, extent, minimumBP) {
33747
+ addViewportClickHandler(viewport) {
33233
33748
 
33234
- let ss = extent.start;
33235
- let ee = extent.end;
33749
+ function clickHandler(event) {
33236
33750
 
33237
- if (undefined === ee) {
33751
+ const column = viewport.parentElement;
33752
+ const index = viewportColumnManager.indexOfColumn(this.browser.columnContainer, column);
33753
+ const referenceFrame = this.browser.referenceFrameList[ index ];
33238
33754
 
33239
- ss -= minimumBP / 2;
33240
- ee = ss + minimumBP;
33755
+ const {xNormalized, width} = translateMouseCoordinates$1(event, this.ideogram_ctx.canvas);
33756
+ const {bpLength} = this.browser.genome.getChromosome(referenceFrame.chr);
33757
+ const locusLength = referenceFrame.bpPerPixel * width;
33758
+ const chrCoveragePercentage = locusLength / bpLength;
33241
33759
 
33242
- if (ee > chromosomeLengthBP) {
33243
- ee = chromosomeLengthBP;
33244
- ss = ee - minimumBP;
33245
- } else if (ss < 0) {
33246
- ss = 0;
33247
- ee = minimumBP;
33248
- }
33760
+ let xPercentage = xNormalized;
33761
+ if (xPercentage - (chrCoveragePercentage / 2.0) < 0) {
33762
+ xPercentage = chrCoveragePercentage / 2.0;
33763
+ }
33249
33764
 
33250
- } else if (ee - ss < minimumBP) {
33765
+ if (xPercentage + (chrCoveragePercentage / 2.0) > 1.0) {
33766
+ xPercentage = 1.0 - chrCoveragePercentage / 2.0;
33767
+ }
33251
33768
 
33252
- const center = (ee + ss) / 2;
33769
+ const ss = Math.round((xPercentage - (chrCoveragePercentage / 2.0)) * bpLength);
33770
+ const ee = Math.round((xPercentage + (chrCoveragePercentage / 2.0)) * bpLength);
33253
33771
 
33254
- if (center - minimumBP / 2 < 0) {
33255
- ss = 0;
33256
- ee = ss + minimumBP;
33257
- } else if (center + minimumBP / 2 > chromosomeLengthBP) {
33258
- ee = chromosomeLengthBP;
33259
- ss = ee - minimumBP;
33260
- } else {
33261
- ss = center - minimumBP / 2;
33262
- ee = ss + minimumBP;
33263
- }
33264
- }
33772
+ referenceFrame.start = ss;
33773
+ referenceFrame.end = ee;
33774
+ referenceFrame.bpPerPixel = (ee - ss) / width;
33265
33775
 
33266
- extent.start = Math.ceil(ss);
33267
- extent.end = Math.floor(ee);
33268
- };
33776
+ this.browser.updateViews(referenceFrame, this.browser.trackViews, true);
33269
33777
 
33270
- /*!
33271
- * is-number <https://github.com/jonschlinkert/is-number>
33272
- *
33273
- * Copyright (c) 2014-present, Jon Schlinkert.
33274
- * Released under the MIT License.
33275
- */
33778
+ }
33779
+
33780
+ this.boundClickHandler = clickHandler.bind(this);
33781
+ viewport.addEventListener('click', this.boundClickHandler);
33276
33782
 
33277
- const isNumber = function (num) {
33278
- if (typeof num === 'number') {
33279
- return num - num === 0
33280
- }
33281
- if (typeof num === 'string' && num.trim() !== '') {
33282
- return Number.isFinite ? Number.isFinite(+num) : isFinite(+num)
33283
33783
  }
33284
- return false
33285
- };
33286
33784
 
33287
- async function getFilename(url) {
33288
- if (isString$3(url) && url.startsWith("https://drive.google.com")) {
33289
- // This will fail if Google API key is not defined
33290
- if (getApiKey() === undefined) {
33291
- throw Error("Google drive is referenced, but API key is not defined. An API key is required for Google Drive access")
33292
- }
33293
- const json = await getDriveFileInfo(url);
33294
- return json.originalFileName || json.name
33295
- } else {
33296
- return getFilename$1(url)
33785
+ removeViewportClickHandler(viewport) {
33786
+ viewport.removeEventListener('click', this.boundClickHandler);
33297
33787
  }
33298
- }
33299
33788
 
33300
- function prettyBasePairNumber(raw) {
33789
+ setWidth(width) {
33790
+ this.$viewport.width(width);
33791
+ }
33301
33792
 
33302
- var denom,
33303
- units,
33304
- value,
33305
- floored;
33793
+ drawSVGWithContext(context, width, height, id, x, y, yClipOffset) {
33306
33794
 
33307
- if (raw > 1e7) {
33308
- denom = 1e6;
33309
- units = " mb";
33310
- } else if (raw > 1e4) {
33795
+ context.saveWithTranslationAndClipRect(id, x, y, width, height, yClipOffset);
33311
33796
 
33312
- denom = 1e3;
33313
- units = " kb";
33797
+ this.trackView.track.draw({
33798
+ context,
33799
+ referenceFrame: this.referenceFrame,
33800
+ pixelWidth: width,
33801
+ pixelHeight: height
33802
+ });
33314
33803
 
33315
- value = raw / denom;
33316
- floored = Math.floor(value);
33317
- return numberFormatter$1(floored) + units
33318
- } else {
33319
- return numberFormatter$1(raw) + " bp"
33804
+ context.restore();
33320
33805
  }
33321
33806
 
33322
- value = raw / denom;
33323
- floored = Math.floor(value);
33324
-
33325
- return floored.toString() + units
33326
- }
33807
+ update(context, pixelWidth, pixelHeight, referenceFrame) {
33808
+ this.$canvas.hide();
33809
+ IGVGraphics.configureHighDPICanvas(context, pixelWidth, pixelHeight);
33810
+ this.trackView.track.draw({ context, referenceFrame, pixelWidth, pixelHeight });
33811
+ }
33327
33812
 
33813
+ startSpinner() {
33814
+ }
33328
33815
 
33329
- function isDataURL(obj) {
33330
- return (isString$3(obj) && obj.startsWith("data:"))
33331
- }
33816
+ stopSpinner() {
33817
+ }
33332
33818
 
33333
- function createColumn(columnContainer, className) {
33334
- const column = div$1({class: className});
33335
- columnContainer.appendChild(column);
33336
33819
  }
33337
33820
 
33821
+ /*
33822
+ * The MIT License (MIT)
33823
+ *
33824
+ * Copyright (c) 2014 Broad Institute
33825
+ *
33826
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
33827
+ * of this software and associated documentation files (the "Software"), to deal
33828
+ * in the Software without restriction, including without limitation the rights
33829
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
33830
+ * copies of the Software, and to permit persons to whom the Software is
33831
+ * furnished to do so, subject to the following conditions:
33832
+ *
33833
+ * The above copyright notice and this permission notice shall be included in
33834
+ * all copies or substantial portions of the Software.
33835
+ *
33836
+ *
33837
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33838
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33839
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
33840
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33841
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
33842
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
33843
+ * THE SOFTWARE.
33844
+ */
33338
33845
 
33339
- function insertElementBefore(element, referenceNode) {
33340
- referenceNode.parentNode.insertBefore(element, referenceNode);
33341
- }
33342
-
33343
- function insertElementAfter(element, referenceNode) {
33344
- referenceNode.parentNode.insertBefore(element, referenceNode.nextSibling);
33345
- }
33346
33846
 
33347
33847
  function createViewport(trackView, column, referenceFrame, width) {
33348
33848
 
@@ -33355,13 +33855,6 @@ function createViewport(trackView, column, referenceFrame, width) {
33355
33855
  }
33356
33856
  }
33357
33857
 
33358
- /**
33359
- * Test to see if page is loaded in a secure context, that is by https or is localhost.
33360
- */
33361
- function isSecureContext() {
33362
- return window.location.protocol === "https:" || window.location.hostname === "localhost"
33363
- }
33364
-
33365
33858
  const maxFontSize = 10;
33366
33859
 
33367
33860
  const fontConfigureTemplate =
@@ -33377,7 +33870,7 @@ class SampleNameViewport {
33377
33870
 
33378
33871
  constructor(trackView, column, unused, width) {
33379
33872
 
33380
- this.guid = guid$1();
33873
+ this.guid = guid$2();
33381
33874
  this.trackView = trackView;
33382
33875
 
33383
33876
  this.browser = trackView.browser;
@@ -33495,7 +33988,7 @@ class SampleNameViewport {
33495
33988
  const {width, height} = this.viewport.getBoundingClientRect();
33496
33989
 
33497
33990
  const str = (this.trackView.track.name || this.trackView.track.id).replace(/\W/g, '');
33498
- const id = `${str}_sample_names_guid_${guid$1()}`;
33991
+ const id = `${str}_sample_names_guid_${guid$2()}`;
33499
33992
 
33500
33993
  context.saveWithTranslationAndClipRect(id, deltaX, deltaY + yScrollDelta, width, height, -yScrollDelta);
33501
33994
 
@@ -33879,7 +34372,7 @@ class TrackView {
33879
34372
 
33880
34373
  constructor(browser, columnContainer, track) {
33881
34374
 
33882
- this.namespace = `trackview-${guid$1()}`;
34375
+ this.namespace = `trackview-${guid$2()}`;
33883
34376
 
33884
34377
  this.browser = browser;
33885
34378
  this.track = track;
@@ -34234,11 +34727,13 @@ class TrackView {
34234
34727
  for (let viewport of reloadableViewports) {
34235
34728
  await viewport.loadFeatures();
34236
34729
  }
34730
+
34731
+ if (this.disposed) return // Track was removed during load
34237
34732
 
34238
34733
  // Very special case for variant tracks in multilocus view. The # of rows to allocate to the variant (site)
34239
34734
  // section depends on data from all the views. We only need to adjust this however if any data was loaded
34240
34735
  // (i.e. reloadableViewports.length > 0)
34241
- if (typeof this.track.variantRowCount === 'function' && reloadableViewports.length > 0) {
34736
+ if (this.track && typeof this.track.variantRowCount === 'function' && reloadableViewports.length > 0) {
34242
34737
  let maxRow = 0;
34243
34738
  for (let viewport of this.viewports) {
34244
34739
  if (viewport.tile && viewport.tile.features) {
@@ -34254,7 +34749,6 @@ class TrackView {
34254
34749
  }
34255
34750
  }
34256
34751
 
34257
- if (this.disposed) return // Track was removed during load
34258
34752
 
34259
34753
  if (this.track.autoscale) {
34260
34754
  let allFeatures = [];
@@ -34702,7 +35196,7 @@ function renderSVGAxis(context, track, axisCanvas, deltaX, deltaY) {
34702
35196
  const {y, width, height} = axisCanvas.getBoundingClientRect();
34703
35197
 
34704
35198
  const str = (track.name || track.id).replace(/\W/g, '');
34705
- const id = `${str}_axis_guid_${guid$1()}`;
35199
+ const id = `${str}_axis_guid_${guid$2()}`;
34706
35200
 
34707
35201
  context.saveWithTranslationAndClipRect(id, deltaX, y + deltaY, width, height, 0);
34708
35202
 
@@ -34982,6 +35476,9 @@ class GFFFeature {
34982
35476
 
34983
35477
  pd.push({name: 'Type', value: this.type});
34984
35478
  pd.push({name: 'Source', value: this.source});
35479
+ if (this.score !== undefined) {
35480
+ pd.push({name: 'Score', value: this.score});
35481
+ }
34985
35482
 
34986
35483
  if (this.attributeString) {
34987
35484
  const atts = parseAttributeString(this.attributeString, this.delim);
@@ -35230,7 +35727,7 @@ function decode(tokens, header) {
35230
35727
  chr: tokens[0],
35231
35728
  start: parseInt(tokens[3]) - 1,
35232
35729
  end: parseInt(tokens[4]),
35233
- score: "." === tokens[5] ? 0 : Number(tokens[5]),
35730
+ score: "." === tokens[5] ? undefined : Number(tokens[5]),
35234
35731
  strand: tokens[6],
35235
35732
  phase: "." === tokens[7] ? 0 : parseInt(tokens[7]),
35236
35733
  attributeString: tokens[8],
@@ -36586,244 +37083,29 @@ class SegFeature {
36586
37083
  * THE SOFTWARE.
36587
37084
  */
36588
37085
 
36589
-
36590
- const knownAltBases = new Set(["A", "C", "T", "G"].map(c => c.charCodeAt(0)));
36591
-
37086
+ /**
37087
+ * Create a variant from an array of tokens representing a line in a "VCF" file
37088
+ * @param tokens
37089
+ */
36592
37090
  function createVCFVariant(tokens) {
36593
- return new Variant(tokens)
36594
- }
36595
-
36596
-
36597
- class Variant {
36598
-
36599
- constructor(tokens) {
36600
- this.chr = tokens[0]; // TODO -- use genome aliases
36601
- this.pos = parseInt(tokens[1]);
36602
- this.names = tokens[2]; // id in VCF
36603
- this.referenceBases = tokens[3];
36604
- this.alternateBases = tokens[4];
36605
- this.quality = tokens[5];
36606
- this.filter = tokens[6];
36607
- this.info = getInfoObject(tokens[7]);
36608
- this.init();
36609
- }
36610
-
36611
- init() {
36612
-
36613
- const ref = this.referenceBases;
36614
- const altBases = this.alternateBases;
36615
-
36616
- if (this.info) {
36617
- if (this.info["VT"]) {
36618
- this.type = this.info["VT"];
36619
- } else if (this.info["SVTYPE"]) {
36620
- this.type = "SV";
36621
- } else if (this.info["PERIOD"]) {
36622
- this.type = "STR";
36623
- }
36624
- }
36625
- if (this.type === undefined) {
36626
- this.type = determineType(ref, altBases);
36627
- }
36628
- if (this.type === "NONVARIANT") {
36629
- this.heterozygosity = 0;
36630
- }
36631
-
36632
- // Determine start/end coordinates -- these are the coordinates representing the actual variant,
36633
- // not the leading or trailing reference
36634
- if (this.info["END"]) {
36635
- this.start = this.pos - 1;
36636
- if (this.info["CHR2"] && this.info["CHR2"] !== this.chr) {
36637
- this.end = this.start + 1;
36638
- } else {
36639
- this.end = Number.parseInt(this.info["END"]);
36640
- }
36641
- } else {
36642
- if (this.type === "NONVARIANT") {
36643
- this.start = this.pos - 1; // convert to 0-based coordinate convention
36644
- this.end = this.start + ref.length;
36645
- } else {
36646
-
36647
- const altTokens = altBases.split(",").filter(token => token.length > 0);
36648
- this.alleles = [];
36649
- this.start = undefined;
36650
- this.end = undefined;
36651
-
36652
- for (let alt of altTokens) {
36653
-
36654
- this.alleles.push(alt);
36655
-
36656
- // We don't yet handle SV and other special alt representations
36657
- if ("SV" !== this.type && isKnownAlt(alt)) {
36658
-
36659
- let altLength = alt.length;
36660
- let lengthOnRef = ref.length;
36661
- const lmin = Math.min(altLength, lengthOnRef);
36662
-
36663
- // Trim off matching bases. Try first match, then right -> left, then any remaining left -> right
36664
- let s = 0;
36665
-
36666
- while (s < lmin && (ref.charCodeAt(s) === alt.charCodeAt(s))) {
36667
- s++;
36668
- altLength--;
36669
- lengthOnRef--;
36670
- }
36671
-
36672
- // right -> left from end
36673
- while (altLength > 0 && lengthOnRef > 0) {
36674
- const altIdx = s + altLength - 1;
36675
- const refIdx = s + lengthOnRef - 1;
36676
- if (alt.charCodeAt(altIdx) === ref.charCodeAt(refIdx)) {
36677
- altLength--;
36678
- lengthOnRef--;
36679
- } else {
36680
- break
36681
- }
36682
- }
36683
-
36684
- // if any remaining, left -> right
36685
- while (altLength > 0 && lengthOnRef > 0) {
36686
- const altIdx = s;
36687
- const refIdx = s;
36688
- if (alt.charCodeAt(altIdx) === ref.charCodeAt(refIdx)) {
36689
- s++;
36690
- altLength--;
36691
- lengthOnRef--;
36692
- } else {
36693
- break
36694
- }
36695
- }
36696
-
36697
- const alleleStart = this.pos + s - 1; // -1 for zero based coordinates
36698
- const alleleEnd = alleleStart + lengthOnRef;
36699
- this.start = this.start === undefined ? alleleStart : Math.min(this.start, alleleStart);
36700
- this.end = this.end === undefined ? alleleEnd : Math.max(this.end, alleleEnd);
36701
- }
36702
- }
36703
-
36704
- // Default to single base representation @ position for variant types not otherwise handled
36705
- if (this.start === undefined) {
36706
- this.start = this.pos - 1;
36707
- this.end = this.pos;
36708
- }
36709
- }
36710
- }
36711
- }
36712
-
36713
-
36714
- popupData(genomicLocation, genomeId) {
36715
-
36716
-
36717
- const posString = `${numberFormatter$1(this.pos)}`;
36718
- const locString = this.start === this.end ?
36719
- `${numberFormatter$1(this.start)} | ${numberFormatter$1(this.start + 1)}` :
36720
- `${numberFormatter$1(this.start + 1)}-${numberFormatter$1(this.end)}`;
36721
- const fields = [
36722
- {name: "Chr", value: this.chr},
36723
- {name: "Pos", value: posString},
36724
- {name: "Loc", value: locString},
36725
- {name: "Names", value: this.names ? this.names : ""},
36726
- {name: "Ref", value: this.referenceBases},
36727
- {name: "Alt", value: this.alternateBases.replace("<", "&lt;")},
36728
- {name: "Qual", value: this.quality},
36729
- {name: "Filter", value: this.filter}
36730
- ];
36731
-
36732
- if ("SNP" === this.type) {
36733
- let ref = this.referenceBases;
36734
- if (ref.length === 1) {
36735
- let altArray = this.alternateBases.split(",");
36736
- for (let alt of altArray) {
36737
- if (alt.length === 1) {
36738
- let l = TrackBase.getCravatLink(this.chr, this.pos, ref, alt, genomeId);
36739
- if (l) {
36740
- fields.push('<hr/>');
36741
- fields.push({html: l});
36742
- }
36743
- }
36744
- }
36745
- }
36746
- }
36747
-
36748
- if (this.hasOwnProperty("heterozygosity")) {
36749
- fields.push({name: "Heterozygosity", value: this.heterozygosity});
36750
- }
36751
-
36752
- if (this.info) {
36753
- fields.push({html: '<hr style="border-top: dotted 1px;border-color: #c9c3ba" />'});
36754
- for (let key of Object.keys(this.info)) {
36755
- fields.push({name: key, value: arrayToString(decodeURIComponent(this.info[key]))});
36756
- }
36757
- }
36758
-
36759
- return fields
36760
-
36761
- };
36762
-
36763
- isRefBlock() {
36764
- return "NONVARIANT" === this.type
36765
- }
36766
-
36767
- }
36768
-
36769
- function getInfoObject(infoStr) {
36770
- var info = {};
37091
+ const variant = new Variant();
37092
+ variant.chr = tokens[0]; // TODO -- use genome aliases
37093
+ variant.pos = parseInt(tokens[1]);
37094
+ variant.names = tokens[2]; // id in VCF
37095
+ variant.referenceBases = tokens[3];
37096
+ variant.alternateBases = tokens[4];
37097
+ variant.quality = tokens[5];
37098
+ variant.filter = tokens[6];
37099
+ variant.info = {};
37100
+ const infoStr = tokens[7];
36771
37101
  if (infoStr) {
36772
- infoStr.split(';').forEach(function (elem) {
37102
+ for (let elem of infoStr.split(';')) {
36773
37103
  var element = elem.split('=');
36774
- info[element[0]] = element[1];
36775
- });
36776
- }
36777
- return info
36778
- }
36779
-
36780
-
36781
- function isKnownAlt(alt) {
36782
- for (let i = 0; i < alt.length; i++) {
36783
- if (!knownAltBases.has(alt.charCodeAt(i))) {
36784
- return false
37104
+ variant.info[element[0]] = element[1];
36785
37105
  }
36786
37106
  }
36787
- return true
36788
- }
36789
-
36790
-
36791
- function determineType(ref, altAlleles) {
36792
- const refLength = ref.length;
36793
- if (altAlleles === undefined) {
36794
- return "UNKNOWN"
36795
- } else if (altAlleles.trim().length === 0 ||
36796
- altAlleles === "<NON_REF>" ||
36797
- altAlleles === "<*>" ||
36798
- altAlleles === ".") {
36799
- return "NONVARIANT"
36800
- } else {
36801
- const alleles = altAlleles.split(",");
36802
- const types = alleles.map(function (a) {
36803
- if (refLength === 1 && a.length === 1) {
36804
- return "SNP"
36805
- } else {
36806
- return "<NON_REF>" === a ? "NONVARIANT" : "OTHER"
36807
- }
36808
- });
36809
- let type = types[0];
36810
- for (let t of types) {
36811
- if (t !== type) {
36812
- return "MIXED"
36813
- }
36814
- }
36815
- return type
36816
- }
36817
- }
36818
-
36819
- function arrayToString(value, delim) {
36820
-
36821
- if (delim === undefined) delim = ",";
36822
-
36823
- if (!(Array.isArray(value))) {
36824
- return value
36825
- }
36826
- return value.join(delim)
37107
+ variant.init();
37108
+ return variant;
36827
37109
  }
36828
37110
 
36829
37111
 
@@ -36887,6 +37169,217 @@ function createGAVariant(json) {
36887
37169
 
36888
37170
  }
36889
37171
 
37172
+
37173
+ class Variant {
37174
+
37175
+ init() {
37176
+
37177
+ const ref = this.referenceBases;
37178
+ const altBases = this.alternateBases;
37179
+
37180
+ if (this.info) {
37181
+ if (this.info["VT"]) {
37182
+ this.type = this.info["VT"];
37183
+ } else if (this.info["SVTYPE"]) {
37184
+ this.type = "SV";
37185
+ } else if (this.info["PERIOD"]) {
37186
+ this.type = "STR";
37187
+ }
37188
+ }
37189
+ if (this.type === undefined) {
37190
+ this.type = determineType(ref, altBases);
37191
+ }
37192
+ if (this.type === "NONVARIANT") {
37193
+ this.heterozygosity = 0;
37194
+ }
37195
+
37196
+ // Determine start/end coordinates -- these are the coordinates representing the actual variant,
37197
+ // not the leading or trailing reference
37198
+ if (this.info["END"]) {
37199
+ this.start = this.pos - 1;
37200
+ if (this.info["CHR2"] && this.info["CHR2"] !== this.chr) {
37201
+ this.end = this.start + 1;
37202
+ } else {
37203
+ this.end = Number.parseInt(this.info["END"]);
37204
+ }
37205
+ } else {
37206
+ if (this.type === "NONVARIANT") {
37207
+ this.start = this.pos - 1; // convert to 0-based coordinate convention
37208
+ this.end = this.start + ref.length;
37209
+ } else {
37210
+
37211
+ const altTokens = altBases.split(",").filter(token => token.length > 0);
37212
+ this.alleles = [];
37213
+ this.start = undefined;
37214
+ this.end = undefined;
37215
+
37216
+ for (let alt of altTokens) {
37217
+
37218
+ this.alleles.push(alt);
37219
+
37220
+ // We don't yet handle SV and other special alt representations
37221
+ if ("SV" !== this.type && isKnownAlt(alt)) {
37222
+
37223
+ let altLength = alt.length;
37224
+ let lengthOnRef = ref.length;
37225
+ const lmin = Math.min(altLength, lengthOnRef);
37226
+
37227
+ // Trim off matching bases. Try first match, then right -> left, then any remaining left -> right
37228
+ let s = 0;
37229
+
37230
+ while (s < lmin && (ref.charCodeAt(s) === alt.charCodeAt(s))) {
37231
+ s++;
37232
+ altLength--;
37233
+ lengthOnRef--;
37234
+ }
37235
+
37236
+ // right -> left from end
37237
+ while (altLength > 0 && lengthOnRef > 0) {
37238
+ const altIdx = s + altLength - 1;
37239
+ const refIdx = s + lengthOnRef - 1;
37240
+ if (alt.charCodeAt(altIdx) === ref.charCodeAt(refIdx)) {
37241
+ altLength--;
37242
+ lengthOnRef--;
37243
+ } else {
37244
+ break
37245
+ }
37246
+ }
37247
+
37248
+ // if any remaining, left -> right
37249
+ while (altLength > 0 && lengthOnRef > 0) {
37250
+ const altIdx = s;
37251
+ const refIdx = s;
37252
+ if (alt.charCodeAt(altIdx) === ref.charCodeAt(refIdx)) {
37253
+ s++;
37254
+ altLength--;
37255
+ lengthOnRef--;
37256
+ } else {
37257
+ break
37258
+ }
37259
+ }
37260
+
37261
+ const alleleStart = this.pos + s - 1; // -1 for zero based coordinates
37262
+ const alleleEnd = alleleStart + lengthOnRef;
37263
+ this.start = this.start === undefined ? alleleStart : Math.min(this.start, alleleStart);
37264
+ this.end = this.end === undefined ? alleleEnd : Math.max(this.end, alleleEnd);
37265
+ }
37266
+ }
37267
+
37268
+ // Default to single base representation @ position for variant types not otherwise handled
37269
+ if (this.start === undefined) {
37270
+ this.start = this.pos - 1;
37271
+ this.end = this.pos;
37272
+ }
37273
+ }
37274
+ }
37275
+ }
37276
+
37277
+
37278
+ popupData(genomicLocation, genomeId) {
37279
+
37280
+
37281
+ const posString = `${numberFormatter$1(this.pos)}`;
37282
+ const locString = this.start === this.end ?
37283
+ `${numberFormatter$1(this.start)} | ${numberFormatter$1(this.start + 1)}` :
37284
+ `${numberFormatter$1(this.start + 1)}-${numberFormatter$1(this.end)}`;
37285
+ const fields = [
37286
+ {name: "Chr", value: this.chr},
37287
+ {name: "Pos", value: posString},
37288
+ {name: "Loc", value: locString},
37289
+ {name: "Names", value: this.names ? this.names : ""},
37290
+ {name: "Ref", value: this.referenceBases},
37291
+ {name: "Alt", value: this.alternateBases.replace("<", "&lt;")},
37292
+ {name: "Qual", value: this.quality},
37293
+ {name: "Filter", value: this.filter}
37294
+ ];
37295
+
37296
+ if ("SNP" === this.type) {
37297
+ let ref = this.referenceBases;
37298
+ if (ref.length === 1) {
37299
+ let altArray = this.alternateBases.split(",");
37300
+ for (let alt of altArray) {
37301
+ if (alt.length === 1) {
37302
+ let l = TrackBase.getCravatLink(this.chr, this.pos, ref, alt, genomeId);
37303
+ if (l) {
37304
+ fields.push('<hr/>');
37305
+ fields.push({html: l});
37306
+ }
37307
+ }
37308
+ }
37309
+ }
37310
+ }
37311
+
37312
+ if (this.hasOwnProperty("heterozygosity")) {
37313
+ fields.push({name: "Heterozygosity", value: this.heterozygosity});
37314
+ }
37315
+
37316
+ if (this.info) {
37317
+ fields.push({html: '<hr style="border-top: dotted 1px;border-color: #c9c3ba" />'});
37318
+ for (let key of Object.keys(this.info)) {
37319
+ fields.push({name: key, value: arrayToString(decodeURIComponent(this.info[key]))});
37320
+ }
37321
+ }
37322
+
37323
+ return fields
37324
+
37325
+ };
37326
+
37327
+ isRefBlock() {
37328
+ return "NONVARIANT" === this.type
37329
+ }
37330
+
37331
+ }
37332
+
37333
+ const knownAltBases = new Set(["A", "C", "T", "G"].map(c => c.charCodeAt(0)));
37334
+
37335
+ function isKnownAlt(alt) {
37336
+ for (let i = 0; i < alt.length; i++) {
37337
+ if (!knownAltBases.has(alt.charCodeAt(i))) {
37338
+ return false
37339
+ }
37340
+ }
37341
+ return true
37342
+ }
37343
+
37344
+
37345
+ function determineType(ref, altAlleles) {
37346
+ const refLength = ref.length;
37347
+ if (altAlleles === undefined) {
37348
+ return "UNKNOWN"
37349
+ } else if (altAlleles.trim().length === 0 ||
37350
+ altAlleles === "<NON_REF>" ||
37351
+ altAlleles === "<*>" ||
37352
+ altAlleles === ".") {
37353
+ return "NONVARIANT"
37354
+ } else {
37355
+ const alleles = altAlleles.split(",");
37356
+ const types = alleles.map(function (a) {
37357
+ if (refLength === 1 && a.length === 1) {
37358
+ return "SNP"
37359
+ } else {
37360
+ return "<NON_REF>" === a ? "NONVARIANT" : "OTHER"
37361
+ }
37362
+ });
37363
+ let type = types[0];
37364
+ for (let t of types) {
37365
+ if (t !== type) {
37366
+ return "MIXED"
37367
+ }
37368
+ }
37369
+ return type
37370
+ }
37371
+ }
37372
+
37373
+ function arrayToString(value, delim) {
37374
+
37375
+ if (delim === undefined) delim = ",";
37376
+
37377
+ if (!(Array.isArray(value))) {
37378
+ return value
37379
+ }
37380
+ return value.join(delim)
37381
+ }
37382
+
36890
37383
  /*
36891
37384
  * The MIT License (MIT)
36892
37385
  *
@@ -37031,6 +37524,7 @@ class VcfParser {
37031
37524
  if (tokens.length === nExpectedColumns) {
37032
37525
  const variant = createVCFVariant(tokens);
37033
37526
  variant.header = this.header; // Keep a pointer to the header to interpret fields for popup text
37527
+ //variant.line = line // Uncomment for debugging
37034
37528
  allFeatures.push(variant);
37035
37529
 
37036
37530
  if (tokens.length > 9) {
@@ -39153,6 +39647,7 @@ class HtsgetVariantReader extends HtsgetReader {
39153
39647
  * THE SOFTWARE.
39154
39648
  */
39155
39649
 
39650
+ const DEFAULT_MAX_WG_COUNT = 10000;
39156
39651
 
39157
39652
  /**
39158
39653
  * feature source for "bed like" files (tab or whitespace delimited files with 1 feature per line: bed, gff, vcf, etc)
@@ -39167,8 +39662,9 @@ class TextFeatureSource {
39167
39662
  this.config = config || {};
39168
39663
  this.genome = genome;
39169
39664
  this.sourceType = (config.sourceType === undefined ? "file" : config.sourceType);
39665
+ this.maxWGCount = config.maxWGCount || DEFAULT_MAX_WG_COUNT;
39170
39666
 
39171
- const queryableFormats = new Set(["bigwig", "bw", "bigbed", "bb", "tdf"]);
39667
+ const queryableFormats = new Set(["bigwig", "bw", "bigbed", "bb", "biginteract", "tdf"]);
39172
39668
 
39173
39669
  if (config.features && Array.isArray(config.features)) {
39174
39670
  // Explicit array of features
@@ -39384,10 +39880,22 @@ class TextFeatureSource {
39384
39880
  // TODO -- filter by pixel size
39385
39881
  getWGFeatures(allFeatures) {
39386
39882
 
39883
+ const makeWGFeature = (f) => {
39884
+ const wg = Object.assign({}, f);
39885
+ wg.chr = "all";
39886
+ wg.start = genome.getGenomeCoordinate(f.chr, f.start);
39887
+ wg.end = genome.getGenomeCoordinate(f.chr, f.end);
39888
+ wg._f = f;
39889
+ // Don't draw exons in whole genome view
39890
+ if (wg["exons"]) delete wg["exons"];
39891
+ return wg
39892
+ };
39893
+
39387
39894
  const genome = this.genome;
39388
39895
  const wgChromosomeNames = new Set(genome.wgChromosomeNames);
39389
39896
  const wgFeatures = [];
39390
-
39897
+ let count = 0;
39898
+ const max = this.maxWGCount;
39391
39899
  for (let c of genome.wgChromosomeNames) {
39392
39900
 
39393
39901
  const features = allFeatures[c];
@@ -39396,19 +39904,18 @@ class TextFeatureSource {
39396
39904
  for (let f of features) {
39397
39905
  let queryChr = genome.getChromosomeName(f.chr);
39398
39906
  if (wgChromosomeNames.has(queryChr)) {
39399
-
39400
- const wg = Object.assign({}, f);
39401
-
39402
- wg.chr = "all";
39403
- wg.start = genome.getGenomeCoordinate(f.chr, f.start);
39404
- wg.end = genome.getGenomeCoordinate(f.chr, f.end);
39405
- wg._f = f;
39406
-
39407
- // Don't draw exons in whole genome view
39408
- if (wg["exons"]) delete wg["exons"];
39409
-
39410
- wgFeatures.push(wg);
39907
+ if (wgFeatures.length < max) {
39908
+ wgFeatures.push(makeWGFeature(f));
39909
+ } else {
39910
+ //Reservoir sampling
39911
+ const samplingProb = max / (count + 1);
39912
+ if (Math.random() < samplingProb) {
39913
+ const idx = Math.floor(Math.random() * (max - 1));
39914
+ wgFeatures[idx] = makeWGFeature(f);
39915
+ }
39916
+ }
39411
39917
  }
39918
+ count++;
39412
39919
  }
39413
39920
  }
39414
39921
  }
@@ -39579,9 +40086,9 @@ class BufferedReader {
39579
40086
 
39580
40087
  //table chromatinInteract
39581
40088
 
39582
- function getDecoder(definedFieldCount, fieldCount, autoSql) {
40089
+ function getDecoder(definedFieldCount, fieldCount, autoSql, format) {
39583
40090
 
39584
- if (autoSql && 'chromatinInteract' === autoSql.table) {
40091
+ if (autoSql && 'chromatinInteract' === autoSql.table || "biginteract" === format) {
39585
40092
  return decodeInteract
39586
40093
  } else {
39587
40094
  const standardFieldCount = definedFieldCount - 3;
@@ -39728,6 +40235,7 @@ class BWReader {
39728
40235
 
39729
40236
  constructor(config, genome) {
39730
40237
  this.path = config.url;
40238
+ this.format = config.format || "bigwig";
39731
40239
  this.genome = genome;
39732
40240
  this.rpTreeCache = {};
39733
40241
  this.config = config;
@@ -40328,7 +40836,7 @@ function decodeWigData(data, chrIdx1, bpStart, chrIdx2, bpEnd, featureArray, chr
40328
40836
  function getBedDataDecoder() {
40329
40837
 
40330
40838
  const minSize = 3 * 4 + 1; // Minimum # of bytes required for a bed record
40331
- const decoder = getDecoder(this.header.definedFieldCount, this.header.fieldCount, this.autoSql);
40839
+ const decoder = getDecoder(this.header.definedFieldCount, this.header.fieldCount, this.autoSql, this.format);
40332
40840
  return function (data, chrIdx1, bpStart, chrIdx2, bpEnd, featureArray, chrDict) {
40333
40841
  const binaryParser = new BinaryParser(data);
40334
40842
  while (binaryParser.remLength() >= minSize) {
@@ -41196,7 +41704,7 @@ function zoomLevelForScale(chr, bpPerPixel, genome) {
41196
41704
  function FeatureSource(config, genome) {
41197
41705
 
41198
41706
  const format = config.format ? config.format.toLowerCase() : undefined;
41199
- if ('bigwig' === format || 'bigbed' === format || 'bb' === format) {
41707
+ if ('bigwig' === format || 'bigbed' === format || 'bb' === format || "biginteract" === format) {
41200
41708
  return new BWSource(config, genome)
41201
41709
  } else if ("tdf" === format) {
41202
41710
  return new TDFSource(config, genome)
@@ -41337,7 +41845,7 @@ function renderFeature(feature, bpStart, xScale, pixelHeight, ctx, options) {
41337
41845
  // single-exon transcript
41338
41846
  const xLeft = Math.max(0, coord.px);
41339
41847
  const xRight = Math.min(pixelWidth, coord.px1);
41340
- const width = Math.max(coord.pw, xRight - xLeft);
41848
+ const width = xRight - xLeft;
41341
41849
  ctx.fillRect(xLeft, py, width, h);
41342
41850
 
41343
41851
  // Arrows
@@ -42062,7 +42570,7 @@ class FeatureTrack extends TrackBase {
42062
42570
  desc += "</html>";
42063
42571
  return desc
42064
42572
  } else {
42065
- return this.name
42573
+ return super.description();
42066
42574
  }
42067
42575
 
42068
42576
  };
@@ -42144,8 +42652,12 @@ class WigTrack extends TrackBase {
42144
42652
 
42145
42653
  const format = config.format ? config.format.toLowerCase() : config.format;
42146
42654
  if ("bigwig" === format) {
42655
+ this.flipAxis = config.flipAxis ? config.flipAxis : false;
42656
+ this.logScale = config.logScale ? config.logScale : false;
42147
42657
  this.featureSource = new BWSource(config, this.browser.genome);
42148
42658
  } else if ("tdf" === format) {
42659
+ this.flipAxis = config.flipAxis ? config.flipAxis : false;
42660
+ this.logScale = config.logScale ? config.logScale : false;
42149
42661
  this.featureSource = new TDFSource(config, this.browser.genome);
42150
42662
  } else {
42151
42663
  this.featureSource = FeatureSource(config, this.browser.genome);
@@ -42194,7 +42706,20 @@ class WigTrack extends TrackBase {
42194
42706
  }
42195
42707
 
42196
42708
  menuItemList() {
42197
- return MenuUtils.numericDataMenuItems(this.trackView)
42709
+ let items = [];
42710
+ if (this.flipAxis !== undefined) {
42711
+ items.push({
42712
+ label:"Flip y-axis",
42713
+ click: () => {
42714
+ this.flipAxis = !this.flipAxis;
42715
+ this.trackView.repaintViews();
42716
+ }
42717
+ });
42718
+ }
42719
+
42720
+ items = items.concat(MenuUtils.numericDataMenuItems(this.trackView));
42721
+
42722
+ return items
42198
42723
  }
42199
42724
 
42200
42725
  async getHeader() {
@@ -42205,6 +42730,27 @@ class WigTrack extends TrackBase {
42205
42730
  return this.header
42206
42731
  }
42207
42732
 
42733
+ // TODO: refactor to igvUtils.js
42734
+ getScaleFactor(min, max, height, logScale) {
42735
+ const scale = logScale ? height / (Math.log10(max + 1) - (min <= 0 ? 0 : Math.log10(min + 1))) : height / (max - min);
42736
+ return scale
42737
+ }
42738
+
42739
+ computeYPixelValue(yValue, yScaleFactor) {
42740
+ return (this.flipAxis ? (yValue - this.dataRange.min) : (this.dataRange.max - yValue)) * yScaleFactor
42741
+ }
42742
+
42743
+ computeYPixelValueInLogScale(yValue, yScaleFactor) {
42744
+ let maxValue = this.dataRange.max;
42745
+ let minValue = this.dataRange.min;
42746
+ if (maxValue <= 0) return 0 // TODO:
42747
+ if (minValue <= -1) minValue = 0;
42748
+ minValue = (minValue <= 0) ? 0 : Math.log10(minValue + 1);
42749
+ maxValue = Math.log10(maxValue + 1);
42750
+ yValue = Math.log10(yValue + 1);
42751
+ return ((this.flipAxis ? (yValue - minValue) : (maxValue - yValue)) * yScaleFactor)
42752
+ }
42753
+
42208
42754
  draw(options) {
42209
42755
 
42210
42756
  const features = options.features;
@@ -42212,7 +42758,7 @@ class WigTrack extends TrackBase {
42212
42758
  const bpPerPixel = options.bpPerPixel;
42213
42759
  const bpStart = options.bpStart;
42214
42760
  const pixelWidth = options.pixelWidth;
42215
- const pixelHeight = options.pixelHeight;
42761
+ options.pixelHeight;
42216
42762
  const bpEnd = bpStart + pixelWidth * bpPerPixel + 1;
42217
42763
  let lastPixelEnd = -1;
42218
42764
  let lastValue = -1;
@@ -42224,9 +42770,10 @@ class WigTrack extends TrackBase {
42224
42770
  baselineColor = IGVColor.addAlpha(posColor, 0.1);
42225
42771
  }
42226
42772
 
42227
- const yScale = (yValue) => {
42228
- return ((this.dataRange.max - yValue) / (this.dataRange.max - this.dataRange.min)) * pixelHeight
42229
- };
42773
+ const scaleFactor = this.getScaleFactor(this.dataRange.min, this.dataRange.max, options.pixelHeight, this.logScale);
42774
+ const yScale = (yValue) => this.logScale
42775
+ ? this.computeYPixelValueInLogScale(yValue, scaleFactor)
42776
+ : this.computeYPixelValue(yValue, scaleFactor);
42230
42777
 
42231
42778
  if (features && features.length > 0) {
42232
42779
 
@@ -42236,7 +42783,7 @@ class WigTrack extends TrackBase {
42236
42783
  // nothing to paint.
42237
42784
  if (this.dataRange.max > this.dataRange.min) {
42238
42785
 
42239
- const y0 = this.dataRange.min == 0 ? pixelHeight : yScale(0);
42786
+ const y0 = yScale(0);
42240
42787
  for (let f of features) {
42241
42788
 
42242
42789
  if (f.end < bpStart) continue
@@ -42260,9 +42807,6 @@ class WigTrack extends TrackBase {
42260
42807
 
42261
42808
  } else {
42262
42809
  let height = y - y0;
42263
- if ((Math.abs(height)) < 1) {
42264
- height = height < 0 ? -1 : 1;
42265
- }
42266
42810
  const pixelEnd = x + width;
42267
42811
  if (pixelEnd > lastPixelEnd || (f.value >= 0 && f.value > lastValue) || (f.value < 0 && f.value < lastNegValue)) {
42268
42812
  IGVGraphics.fillRect(ctx, x, y0, width, height, {fillStyle: color});
@@ -42359,6 +42903,21 @@ class WigTrack extends TrackBase {
42359
42903
  dispose() {
42360
42904
  this.trackView = undefined;
42361
42905
  }
42906
+
42907
+ /**
42908
+ * Return the current state of the track. Used to create sessions and bookmarks.
42909
+ *
42910
+ * @returns {*|{}}
42911
+ */
42912
+ getState() {
42913
+
42914
+ const config = super.getState();
42915
+
42916
+ if (this.flipAxis !== undefined) config.flipAxis = this.flipAxis;
42917
+ if (this.logScale !== undefined) config.logScale = this.logScale;
42918
+
42919
+ return config
42920
+ }
42362
42921
  }
42363
42922
 
42364
42923
  /**
@@ -42523,7 +43082,11 @@ class SegTrack extends TrackBase {
42523
43082
 
42524
43083
  // this.featureSource = config.sourceType === "bigquery" ?
42525
43084
  // new igv.BigQueryFeatureSource(this.config) :
42526
- this.featureSource = FeatureSource(this.config, this.browser.genome);
43085
+
43086
+ // Disable whole genome downsampling unless explicitly.
43087
+ const configCopy = Object.assign({}, this.config);
43088
+ configCopy.maxWGCount = configCopy.maxWGCount || Number.MAX_SAFE_INTEGER;
43089
+ this.featureSource = FeatureSource(configCopy, this.browser.genome);
42527
43090
 
42528
43091
  this.initialSort = config.sort;
42529
43092
  }
@@ -43173,6 +43736,19 @@ function autoscale(chr, featureArrays) {
43173
43736
  * THE SOFTWARE.
43174
43737
  */
43175
43738
 
43739
+ function getArcType(config) {
43740
+ if (!config.arcType) {
43741
+ return "nested"
43742
+ }
43743
+ switch (config.arcType) {
43744
+ case "chiapet":
43745
+ return "inView"
43746
+ case "chiapetoutbound":
43747
+ return "partialInView"
43748
+ default:
43749
+ return config.arcType
43750
+ }
43751
+ }
43176
43752
 
43177
43753
  class InteractionTrack extends TrackBase {
43178
43754
 
@@ -43187,13 +43763,14 @@ class InteractionTrack extends TrackBase {
43187
43763
  this.sinTheta = Math.sin(this.theta);
43188
43764
  this.cosTheta = Math.cos(this.theta);
43189
43765
  this.height = config.height || 250;
43190
- this.arcType = config.arcType || "nested"; // nested | proportional
43766
+ this.arcType = getArcType(config); // nested | proportional | inView | partialInView
43191
43767
  this.arcOrientation = (config.arcOrientation === undefined ? true : config.arcOrientation); // true for up, false for down
43192
43768
  this.showBlocks = config.showBlocks === undefined ? true : config.showBlocks;
43193
43769
  this.blockHeight = config.blockHeight || 3;
43194
43770
  this.thickness = config.thickness || 1;
43195
43771
  this.color = config.color || "rgb(180,25,137)";
43196
- this.alpha = config.alpha || 0.15;
43772
+ this.alpha = config.alpha || 0.02; // was: 0.15
43773
+ this.painter = {flipAxis: !this.arcOrientation, dataRange: this.dataRange, paintAxis: paintAxis};
43197
43774
 
43198
43775
  if (config.valueColumn) {
43199
43776
  this.valueColumn = config.valueColumn;
@@ -43258,6 +43835,8 @@ class InteractionTrack extends TrackBase {
43258
43835
 
43259
43836
  if (this.arcType === "proportional") {
43260
43837
  this.drawProportional(options);
43838
+ } else if (this.arcType === "inView" || this.arcType === "partialInView") {
43839
+ this.drawProportional(options);
43261
43840
  } else {
43262
43841
  this.drawNested(options);
43263
43842
  }
@@ -43289,6 +43868,9 @@ class InteractionTrack extends TrackBase {
43289
43868
 
43290
43869
  for (let feature of featureList) {
43291
43870
 
43871
+ // Reset transient property drawState. An undefined value => feature has not been drawn.
43872
+ feature.drawState = undefined;
43873
+
43292
43874
  let color = feature.color || this.color;
43293
43875
  if (color && this.config.useScore) {
43294
43876
  color = getAlphaColor(color, scoreShade(feature.score));
@@ -43358,7 +43940,10 @@ class InteractionTrack extends TrackBase {
43358
43940
  }
43359
43941
  const otherChr = feature.chr === feature.chr1 ? feature.chr2 : feature.chr1;
43360
43942
  ctx.strokeStyle = color;
43361
- ctx.fillStyle = color;
43943
+ // get a sense of trans "spread"
43944
+ ctx.fillStyle = getAlphaColor(getChrColor(otherChr), 0.5);
43945
+ // ctx.fillStyle = color
43946
+
43362
43947
  if (direction) {
43363
43948
  // UP
43364
43949
  ctx.fillRect(pixelStart, this.height / 2, w, this.height / 2);
@@ -43392,6 +43977,11 @@ class InteractionTrack extends TrackBase {
43392
43977
  }
43393
43978
  }
43394
43979
 
43980
+ getScaleFactor(min, max, height, logScale) {
43981
+ const scale = logScale ? height / (Math.log10(max + 1) - (min <= 0 ? 0 : Math.log10(min + 1))) : height / (max - min);
43982
+ return scale
43983
+ }
43984
+
43395
43985
  drawProportional(options) {
43396
43986
 
43397
43987
  const ctx = options.context;
@@ -43400,12 +43990,9 @@ class InteractionTrack extends TrackBase {
43400
43990
  const bpPerPixel = options.bpPerPixel;
43401
43991
  const bpStart = options.bpStart;
43402
43992
  const xScale = bpPerPixel;
43993
+ const refStart = options.referenceFrame.start;
43994
+ const refEnd = options.referenceFrame.end;
43403
43995
 
43404
- // SVG output for proportional arcs are currently not supported because "ellipse" is not implemented
43405
- // if(typeof ctx.ellipse !== 'function') {
43406
- // Alert.presentAlert("SVG output of proportional arcs is currently not supported.")
43407
- // return;
43408
- // }
43409
43996
 
43410
43997
  IGVGraphics.fillRect(ctx, 0, options.pixelTop, pixelWidth, pixelHeight, {'fillStyle': "rgb(255, 255, 255)"});
43411
43998
 
@@ -43413,37 +44000,65 @@ class InteractionTrack extends TrackBase {
43413
44000
 
43414
44001
  if (featureList && featureList.length > 0) {
43415
44002
 
43416
- const yScale = this.logScale ?
43417
- options.pixelHeight / Math.log10(this.dataRange.max + 1) :
43418
- options.pixelHeight / (this.dataRange.max - this.dataRange.min);
43419
-
44003
+ // we use the min as a filter but not moving the axis
44004
+ const effectiveMin = 0;
44005
+ const yScale = this.getScaleFactor(effectiveMin, this.dataRange.max, options.pixelHeight - 1, this.logScale);
43420
44006
  const y = this.arcOrientation ? options.pixelHeight : 0;
43421
44007
 
43422
44008
  for (let feature of featureList) {
43423
44009
 
44010
+ // Reset transient property drawState. An undefined value => feature has not been drawn.
44011
+ feature.drawState = undefined;
44012
+
43424
44013
  const value = this.valueColumn ? feature[this.valueColumn] : feature.score;
43425
44014
  if (value === undefined || Number.isNaN(value)) continue
43426
44015
 
43427
- const radiusY = this.logScale ?
43428
- Math.log10(value + 1) * yScale :
43429
- value * yScale;
44016
+ const radiusY = Math.round((this.logScale ? Math.log10(value + 1) : value) * yScale);
43430
44017
 
43431
44018
  if (feature.chr1 === feature.chr2 || feature.chr === 'all') {
43432
44019
 
43433
44020
  const {m1, m2} = getMidpoints(feature, this.browser.genome);
43434
44021
 
43435
- let pixelStart = (m1 - bpStart) / xScale;
43436
- let pixelEnd = (m2 - bpStart) / xScale;
44022
+ let pixelStart = Math.round((m1 - bpStart) / xScale);
44023
+ let pixelEnd = Math.round((m2 - bpStart) / xScale);
43437
44024
  let w = (pixelEnd - pixelStart);
43438
44025
  if (w < 3) {
43439
44026
  w = 3;
43440
44027
  pixelStart--;
43441
44028
  }
43442
44029
 
43443
- if (pixelEnd < 0 || pixelStart > pixelWidth || value < this.dataRange.min) continue
44030
+ // Various filters
44031
+ if (value < this.dataRange.min || value > this.dataRange.max) continue
44032
+ if ("proportional" !== this.arcType) {
44033
+ const showOutbound = (this.arcType === "partialInView");
44034
+ const within = (m1 >= refStart && m2 <= refEnd);
44035
+ let outBound = false;
44036
+ let inBound = false;
44037
+ if (!within && showOutbound) {
44038
+ outBound = (refStart <= m1 && m1 <= refEnd);
44039
+ if (!outBound) inBound = (refStart <= m2 && m2 <= refEnd);
44040
+ }
44041
+ if (!(within || outBound || inBound)) continue
44042
+ }
44043
+
43444
44044
 
43445
44045
  const radiusX = w / 2;
43446
44046
  const xc = pixelStart + w / 2;
44047
+ feature.drawState = {xc, yc: y, radiusX, radiusY};
44048
+
44049
+ // const arcKey = ((pixelStart << 16) | pixelEnd)
44050
+ // let arc = arcCaches.get(arcKey)
44051
+ // if (arc !== undefined) {
44052
+ // if (arc.has(radiusY)) {
44053
+ // continue
44054
+ // }
44055
+ // arc.add(radiusY)
44056
+ // } else {
44057
+ // let arcHeights = new Set()
44058
+ // arcHeights.add(radiusY)
44059
+ // arcCaches.set(arcKey, arcHeights)
44060
+ // }
44061
+
43447
44062
  const counterClockwise = this.arcOrientation ? true : false;
43448
44063
  const color = feature.color || this.color;
43449
44064
  ctx.strokeStyle = color;
@@ -43457,6 +44072,15 @@ class InteractionTrack extends TrackBase {
43457
44072
  ctx.stroke();
43458
44073
  }
43459
44074
 
44075
+ if (this.alpha) {
44076
+ ctx.fillStyle = getAlphaColor(color, this.alpha);
44077
+ if (true === ctx.isSVG) {
44078
+ ctx.fillEllipse(xc, y, radiusX, radiusY, 0, 0, Math.PI, counterClockwise);
44079
+ } else {
44080
+ ctx.fill();
44081
+ }
44082
+ }
44083
+
43460
44084
  if (this.showBlocks && feature.chr !== 'all') {
43461
44085
  ctx.fillStyle = color;
43462
44086
  const s1 = (feature.start1 - bpStart) / xScale;
@@ -43468,21 +44092,11 @@ class InteractionTrack extends TrackBase {
43468
44092
  ctx.fillRect(s2, y, e2 - s2, hb);
43469
44093
  }
43470
44094
 
43471
- if (this.alpha) {
43472
- ctx.fillStyle = getAlphaColor(color, this.alpha);
43473
- if (true === ctx.isSVG) {
43474
- ctx.fillEllipse(xc, y, radiusX, radiusY, 0, 0, Math.PI, counterClockwise);
43475
- } else {
43476
- ctx.fill();
43477
- }
43478
-
43479
- }
43480
-
43481
- feature.drawState = {xc, yc: y, radiusX, radiusY};
43482
44095
  } else {
44096
+ // Inter chromosome
43483
44097
  let pixelStart = Math.round((feature.start - bpStart) / xScale);
43484
44098
  let pixelEnd = Math.round((feature.end - bpStart) / xScale);
43485
- if (pixelEnd < 0 || pixelStart > pixelWidth || value < this.dataRange.min) continue
44099
+ if (pixelEnd < 0 || pixelStart > pixelWidth || value < this.dataRange.min || value > this.dataRange.max) continue
43486
44100
 
43487
44101
  const h = Math.min(radiusY, this.height - 13); // Leave room for text
43488
44102
  let w = (pixelEnd - pixelStart);
@@ -43493,6 +44107,8 @@ class InteractionTrack extends TrackBase {
43493
44107
  const otherChr = feature.chr === feature.chr1 ? feature.chr2 : feature.chr1;
43494
44108
  ctx.font = "8px sans-serif";
43495
44109
  ctx.textAlign = "center";
44110
+ // get a sense of trans "spread"
44111
+ ctx.fillStyle = getAlphaColor(getChrColor(otherChr), 0.5);
43496
44112
  if (this.arcOrientation) {
43497
44113
  // UP
43498
44114
  const y = this.height - h;
@@ -43509,6 +44125,26 @@ class InteractionTrack extends TrackBase {
43509
44125
  }
43510
44126
  }
43511
44127
 
44128
+ clearAxis(ctx, pixelWidth, pixelHeight) {
44129
+ IGVGraphics.fillRect(ctx, 0, 0, pixelWidth, pixelHeight, {'fillStyle': "rgb(255, 255, 255)"});
44130
+ }
44131
+
44132
+ paintAxis(ctx, pixelWidth, pixelHeight) {
44133
+ // dataRane is interpreted differently for interactino tracks -- all arcs are drawn from "zero", irrespective of dataRange.min
44134
+ const axisRange = {min: 0, max: this.dataRange.max};
44135
+ if (this.arcType === "proportional") {
44136
+ this.painter.flipAxis = !this.arcOrientation;
44137
+ this.painter.dataRange = axisRange;
44138
+ this.painter.paintAxis(ctx, pixelWidth, pixelHeight);
44139
+ } else if (this.arcType === "inView" || this.arcType === "partialInView") {
44140
+ this.painter.flipAxis = !this.arcOrientation;
44141
+ this.painter.dataRange = axisRange;
44142
+ this.painter.paintAxis(ctx, pixelWidth, pixelHeight);
44143
+ } else {
44144
+ this.clearAxis(ctx, pixelWidth, pixelHeight);
44145
+ }
44146
+ }
44147
+
43512
44148
  menuItemList() {
43513
44149
 
43514
44150
  let items = [
@@ -43525,10 +44161,13 @@ class InteractionTrack extends TrackBase {
43525
44161
  if (this.hasValue) {
43526
44162
  const lut =
43527
44163
  {
43528
- "nested": "Nested Arcs",
43529
- "proportional": "Proportional Arcs"
44164
+ "nested": "Nested",
44165
+ "proportional": "Proportional - All",
44166
+ "inView": "Proportional - Both Ends in View",
44167
+ "partialInView": "Proportional - One End in View"
43530
44168
  };
43531
- for (let arcType of ["nested", "proportional"]) {
44169
+ items.push("<b>Arc Type</b>");
44170
+ for (let arcType of ["nested", "proportional", "inView", "partialInView"]) {
43532
44171
  items.push(
43533
44172
  {
43534
44173
  object: $$1(createCheckbox$1(lut[arcType], arcType === this.arcType)),
@@ -43539,45 +44178,37 @@ class InteractionTrack extends TrackBase {
43539
44178
  });
43540
44179
  }
43541
44180
  }
44181
+ items.push("<hr/>");
43542
44182
 
43543
44183
  items.push({
43544
- object: $$1(createCheckbox$1("Show Blocks", this.showBlocks)),
44184
+ name: "Toggle arc direction",
43545
44185
  click: () => {
43546
- this.showBlocks = !this.showBlocks;
44186
+ this.arcOrientation = !this.arcOrientation;
43547
44187
  this.trackView.repaintViews();
43548
44188
  }
43549
44189
  });
43550
44190
  items.push({
43551
- name: "Toggle arc direction",
44191
+ name: this.showBlocks ? "Hide Blocks" : "Show Blocks",
43552
44192
  click: () => {
43553
- this.arcOrientation = !this.arcOrientation;
44193
+ this.showBlocks = !this.showBlocks;
43554
44194
  this.trackView.repaintViews();
43555
44195
  }
43556
44196
  });
43557
44197
 
43558
- if (this.arcType === "proportional") {
43559
- items.push("<HR>");
44198
+
44199
+ if (this.arcType === "proportional" || this.arcType === "inView" || this.arcType === "partialInView") {
44200
+ // MenuUtils.numericDataMenuItems(this.trackView).forEach(item => items.push(item))
43560
44201
  items = items.concat(MenuUtils.numericDataMenuItems(this.trackView));
43561
44202
  }
43562
44203
 
43563
44204
  if (this.browser.circularView && true === this.browser.circularViewVisible) {
44205
+ items.push('<hr/>');
43564
44206
  items.push({
43565
44207
  label: 'Add interactions to circular view',
43566
44208
  click: () => {
43567
-
43568
- const inView = [];
43569
44209
  for (let viewport of this.trackView.viewports) {
43570
- const refFrame = viewport.referenceFrame;
43571
- for (let f of viewport.getCachedFeatures()) {
43572
- if (f.end >= refFrame.start && f.start <= refFrame.end) {
43573
- inView.push(f);
43574
- }
43575
- }
44210
+ this.addChordsForViewport(viewport.referenceFrame);
43576
44211
  }
43577
-
43578
- const chords = makeBedPEChords(inView);
43579
- const color = IGVColor.addAlpha(this.color, 0.5);
43580
- this.browser.circularView.addChords(chords, {track: this.name, color: color});
43581
44212
  }
43582
44213
  });
43583
44214
  }
@@ -43596,22 +44227,43 @@ class InteractionTrack extends TrackBase {
43596
44227
  label: 'Add interactions to circular view',
43597
44228
  click: () => {
43598
44229
  const refFrame = viewport.referenceFrame;
43599
- const inView = "all" === refFrame.chr ?
43600
- this.featureSource.getAllFeatures() :
43601
- this.featureSource.featureCache.queryFeatures(refFrame.chr, refFrame.start, refFrame.end);
43602
- this.browser.circularViewVisible = true;
43603
- const chords = makeBedPEChords(inView);
43604
- const color = IGVColor.addAlpha(this.color, 0.5);
43605
- this.browser.circularView.addChords(chords, {track: this.name, color: color});
44230
+ // first pass: to get all the relevant features
44231
+ this.addChordsForViewport(refFrame);
43606
44232
  }
43607
44233
  });
43608
44234
 
43609
44235
  list.push('<hr/>');
43610
44236
  return list
43611
44237
  }
43612
-
43613
44238
  }
43614
44239
 
44240
+ /**
44241
+ * Add chords to the circular view for the given viewport, represented by its reference frame
44242
+ * @param refFrame
44243
+ */
44244
+ addChordsForViewport(refFrame) {
44245
+ const cachedFeatures = "all" === refFrame.chr ?
44246
+ this.featureSource.getAllFeatures() :
44247
+ this.featureSource.featureCache.queryFeatures(refFrame.chr, refFrame.start, refFrame.end);
44248
+
44249
+ // inView features are simply features that have been drawn, i.e. have a drawState
44250
+ const inView = cachedFeatures.filter(f => f.drawState);
44251
+ if(inView.length === 0) erturn;
44252
+
44253
+ this.browser.circularViewVisible = true;
44254
+ const chords = makeBedPEChords(inView);
44255
+
44256
+ // for filtered set, distinguishing the chromosomes is more critical than tracks
44257
+ const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? this.color : getChrColor(refFrame.chr), 0.5);
44258
+ const trackColor = IGVColor.addAlpha(this.color, 0.5);
44259
+
44260
+ // name the chord set to include filtering information
44261
+ const encodedName = this.name.replaceAll(' ', '%20');
44262
+ const chordSetName = "all" === refFrame.chr ?
44263
+ encodedName :
44264
+ `${encodedName} (${refFrame.chr}:${refFrame.start}-${refFrame.end} ; range:${this.dataRange.min}-${this.dataRange.max})`;
44265
+ this.browser.circularView.addChords(chords, {track: chordSetName, color: chordSetColor, trackColor: trackColor});
44266
+ }
43615
44267
 
43616
44268
  doAutoscale(features) {
43617
44269
 
@@ -43678,10 +44330,12 @@ class InteractionTrack extends TrackBase {
43678
44330
  const featureList = features || clickState.viewport.getCachedFeatures();
43679
44331
  const candidates = [];
43680
44332
  if (featureList) {
43681
- const proportional = this.arcType === "proportional";
44333
+ const proportional = (this.arcType === "proportional" || this.arcType === "inView" || this.arcType === "partialInView");
43682
44334
 
43683
44335
  for (let feature of featureList) {
44336
+
43684
44337
  if (!feature.drawState) continue
44338
+
43685
44339
  if (feature.chr1 === feature.chr2 || feature.chr === 'all') {
43686
44340
  if (proportional) {
43687
44341
  //(x-xc)^2/radiusX^2 + (y-yc)^2/radiusY^2 <= 1
@@ -43719,6 +44373,26 @@ class InteractionTrack extends TrackBase {
43719
44373
  }
43720
44374
  return candidates.map((c) => c.feature)
43721
44375
  }
44376
+
44377
+ /**
44378
+ * Return the current state of the track. Used to create sessions and bookmarks.
44379
+ *
44380
+ * @returns {*|{}}
44381
+ */
44382
+ getState() {
44383
+
44384
+ const config = super.getState();
44385
+
44386
+ // if (this.height !== undefined) config.height = this.height;
44387
+ if (this.arcType !== undefined) config.arcType = this.arcType;
44388
+ if (this.arcOrientation !== undefined) config.arcOrientation = this.arcOrientation;
44389
+ if (this.showBlocks !== undefined) config.showBlocks = this.showBlocks;
44390
+ if (this.blockHeight !== undefined) config.blockHeight = this.blockHeight;
44391
+ if (this.thickness !== undefined) config.thickness = this.thickness;
44392
+ if (this.alpha !== undefined) config.alpha = this.alpha;
44393
+
44394
+ return config
44395
+ }
43722
44396
  }
43723
44397
 
43724
44398
  function getMidpoints(feature, genome) {
@@ -43782,29 +44456,93 @@ function getAlphaColor(color, alpha) {
43782
44456
 
43783
44457
 
43784
44458
  /**
43785
- * Called in the context of FeatureSource
44459
+ * Called in the context of FeatureSource (i.e. this == the feature source (a TextFeatureSource) for the track
44460
+ *
43786
44461
  * @param allFeatures
43787
44462
  * @returns {[]}
43788
44463
  */
43789
44464
  function getWGFeatures(allFeatures) {
43790
44465
 
44466
+ const makeWGFeature = (f) => {
44467
+ const wg = Object.assign({}, f);
44468
+ wg.chr = "all";
44469
+ wg.start = genome.getGenomeCoordinate(f.chr1, f.start1);
44470
+ wg.end = genome.getGenomeCoordinate(f.chr2, f.end2);
44471
+ return wg
44472
+ };
44473
+
43791
44474
  const genome = this.genome;
43792
- const wgFeatures = [];
44475
+
44476
+ // First pass -- find the max score feature
44477
+ let maxScoreFeature;
44478
+ let totalFeatureCount = 0;
43793
44479
  for (let c of genome.wgChromosomeNames) {
43794
- const chrFeatures = allFeatures[c];
44480
+ let chrFeatures = allFeatures[c];
43795
44481
  if (chrFeatures) {
43796
44482
  for (let f of chrFeatures) {
43797
44483
  if (!f.dup) {
43798
- const wg = Object.assign({}, f);
43799
- wg.chr = "all";
43800
- wg.start = genome.getGenomeCoordinate(f.chr1, f.start1);
43801
- wg.end = genome.getGenomeCoordinate(f.chr2, f.end2);
43802
- wgFeatures.push(wg);
44484
+ totalFeatureCount++;
44485
+ if (f.score && (!maxScoreFeature || f.score > maxScoreFeature.score)) {
44486
+ maxScoreFeature = f;
44487
+ }
44488
+ }
44489
+ }
44490
+ }
44491
+ }
44492
+
44493
+ const maxCount = this.maxWGCount;
44494
+ const nBins = maxScoreFeature && maxScoreFeature.score > 0 && totalFeatureCount > maxCount ? 5 : 1; // TODO make a function of total # of features & maxCount?
44495
+ const featuresPerBin = Math.floor(maxCount / nBins);
44496
+ const binSize = maxScoreFeature && maxScoreFeature.score > 0 ? Math.log(maxScoreFeature.score) / nBins : Number.MAX_SAFE_INTEGER;
44497
+
44498
+ let binnedFeatures = [];
44499
+ let counts = [];
44500
+ for (let i = 0; i < nBins; i++) {
44501
+ counts.push([0]);
44502
+ binnedFeatures.push([]);
44503
+ }
44504
+
44505
+ for (let c of genome.wgChromosomeNames) {
44506
+ let chrFeatures = allFeatures[c];
44507
+ if (chrFeatures) {
44508
+ for (let f of chrFeatures) {
44509
+ if (!f.dup) {
44510
+ const bin = f.score ? Math.min(nBins - 1, Math.floor(Math.log(f.score) / binSize)) : 0;
44511
+ if (binnedFeatures[bin].length < featuresPerBin) {
44512
+ binnedFeatures[bin].push(makeWGFeature(f));
44513
+ } else {
44514
+ //Reservoir sampling
44515
+ const samplingProb = featuresPerBin / (counts[bin] + 1);
44516
+ if (Math.random() < samplingProb) {
44517
+ const idx = Math.floor(Math.random() * (featuresPerBin - 1));
44518
+ binnedFeatures[bin][idx] = makeWGFeature(f);
44519
+ }
44520
+ }
44521
+ counts[bin]++;
43803
44522
  }
43804
44523
  }
43805
44524
  }
43806
44525
  }
43807
44526
 
44527
+ let wgFeatures;
44528
+ if (nBins === 1) {
44529
+ wgFeatures = binnedFeatures[0];
44530
+ } else {
44531
+ wgFeatures = [];
44532
+ for (let bf of binnedFeatures) {
44533
+ for (let f of bf) wgFeatures.push(f);
44534
+ }
44535
+ // Keep the feature with max score
44536
+ if (maxScoreFeature) {
44537
+ wgFeatures.push(makeWGFeature(maxScoreFeature));
44538
+ }
44539
+ wgFeatures.sort(function (a, b) {
44540
+ return a.start - b.start
44541
+ });
44542
+ console.log(wgFeatures.length);
44543
+ }
44544
+
44545
+
43808
44546
  return wgFeatures
43809
44547
  }
43810
44548
 
@@ -44876,7 +45614,6 @@ class GWASTrack extends TrackBase {
44876
45614
  this.divider = config.divider || "rgb(225,225,225)";
44877
45615
  this.dotSize = config.dotSize || 3;
44878
45616
  this.popoverWindow = (config.popoverWindow === undefined ? DEFAULT_POPOVER_WINDOW : config.popoverWindow);
44879
- this.description = config.description; // might be null
44880
45617
 
44881
45618
  this.colorScales = config.color ?
44882
45619
  new ConstantColorScale(config.color) :
@@ -46428,12 +47165,6 @@ class SpliceJunctionTrack extends TrackBase {
46428
47165
  return data
46429
47166
  }
46430
47167
 
46431
-
46432
- description() {
46433
- return this.name
46434
-
46435
- }
46436
-
46437
47168
  /**
46438
47169
  * Called when the track is removed. Do any needed cleanup here
46439
47170
  */
@@ -47000,7 +47731,7 @@ class ReferenceFrame {
47000
47731
  this.end = end;
47001
47732
 
47002
47733
  this.bpPerPixel = bpPerPixel;
47003
- this.id = guid$1();
47734
+ this.id = guid$2();
47004
47735
  }
47005
47736
 
47006
47737
  calculateEnd(pixels) {
@@ -47722,7 +48453,7 @@ class CursorGuide {
47722
48453
 
47723
48454
  this.addMouseHandler(browser);
47724
48455
 
47725
- this.setVisibility(browser.config.showCursorTrackingGuide);
48456
+ this.setVisibility(browser.config.showCursorGuide);
47726
48457
 
47727
48458
  }
47728
48459
 
@@ -47739,35 +48470,40 @@ class CursorGuide {
47739
48470
  const target = document.elementFromPoint(event.clientX, event.clientY);
47740
48471
 
47741
48472
  let viewport = undefined;
47742
- if (target.parentElement.classList.contains('igv-viewport-content')) {
47743
- viewport = target.parentElement.parentElement;
47744
- } else if (target.parentElement.classList.contains('igv-viewport') && target.classList.contains('igv-viewport-content')) {
47745
- viewport = target.parentElement;
47746
- }
47747
48473
 
47748
- if (viewport && browser.getRulerTrackView()) {
48474
+ if (target.parentElement) {
47749
48475
 
47750
- this.verticalGuide.style.left = `${x}px`;
48476
+ if (target.parentElement.classList.contains('igv-viewport-content')) {
48477
+ viewport = target.parentElement.parentElement;
48478
+ } else if (target.parentElement.classList.contains('igv-viewport') && target.classList.contains('igv-viewport-content')) {
48479
+ viewport = target.parentElement;
48480
+ }
48481
+
48482
+ if (viewport && browser.getRulerTrackView()) {
48483
+
48484
+ this.verticalGuide.style.left = `${x}px`;
47751
48485
 
47752
- const columns = browser.root.querySelectorAll('.igv-column');
47753
- let index = undefined;
47754
- const viewportParent = viewport.parentElement;
47755
- for (let i = 0; i < columns.length; i++) {
47756
- if (undefined === index && viewportParent === columns[i]) {
47757
- index = i;
48486
+ const columns = browser.root.querySelectorAll('.igv-column');
48487
+ let index = undefined;
48488
+ const viewportParent = viewport.parentElement;
48489
+ for (let i = 0; i < columns.length; i++) {
48490
+ if (undefined === index && viewportParent === columns[i]) {
48491
+ index = i;
48492
+ }
47758
48493
  }
47759
- }
47760
48494
 
47761
- const rulerViewport = browser.getRulerTrackView().viewports[index];
47762
- const result = rulerViewport.mouseMove(event);
48495
+ const rulerViewport = browser.getRulerTrackView().viewports[index];
48496
+ const result = rulerViewport.mouseMove(event);
47763
48497
 
47764
- if (result) {
48498
+ if (result) {
47765
48499
 
47766
- const {start, bp, end} = result;
47767
- const interpolant = (bp - start) / (end - start);
48500
+ const {start, bp, end} = result;
48501
+ const interpolant = (bp - start) / (end - start);
48502
+
48503
+ if (this.customMouseHandler) {
48504
+ this.customMouseHandler({start, bp, end, interpolant});
48505
+ }
47768
48506
 
47769
- if (this.customMouseHandler) {
47770
- this.customMouseHandler({start, bp, end, interpolant});
47771
48507
  }
47772
48508
 
47773
48509
  }
@@ -47781,8 +48517,8 @@ class CursorGuide {
47781
48517
  this.columnContainer.removeEventListener('mousemove', this.boundMouseMoveHandler);
47782
48518
  }
47783
48519
 
47784
- setVisibility(showCursorTrackingGuide) {
47785
- if (true === showCursorTrackingGuide) {
48520
+ setVisibility(showCursorGuide) {
48521
+ if (true === showCursorGuide) {
47786
48522
  this.show();
47787
48523
  } else {
47788
48524
  this.hide();
@@ -47953,50 +48689,52 @@ class CenterLineButton {
47953
48689
  * THE SOFTWARE.
47954
48690
  */
47955
48691
 
47956
- const TrackLabelControl = function (parent, browser) {
48692
+ class TrackLabelControl {
47957
48693
 
47958
- this.button = div$1({class: 'igv-navbar-button'});
47959
- parent.appendChild(this.button);
47960
- this.button.textContent = 'track labels';
48694
+ constructor(parent, browser) {
47961
48695
 
47962
- this.button.addEventListener('click', () => {
47963
- browser.trackLabelsVisible = !browser.trackLabelsVisible;
47964
- this.setState(browser.trackLabelsVisible);
47965
- browser.setTrackLabelVisibility(browser.trackLabelsVisible);
47966
- });
48696
+ this.button = div$1({class: 'igv-navbar-button'});
48697
+ parent.appendChild(this.button);
48698
+ this.button.textContent = 'track labels';
47967
48699
 
47968
- this.browser = browser;
48700
+ this.button.addEventListener('click', () => {
48701
+ browser.trackLabelsVisible = !browser.trackLabelsVisible;
48702
+ this.setState(browser.trackLabelsVisible);
48703
+ browser.setTrackLabelVisibility(browser.trackLabelsVisible);
48704
+ });
47969
48705
 
47970
- this.setVisibility(browser.config.showTrackLabelButton);
48706
+ this.browser = browser;
47971
48707
 
47972
- this.setState(browser.trackLabelsVisible);
48708
+ this.setVisibility(browser.config.showTrackLabelButton);
47973
48709
 
47974
- };
48710
+ this.setState(browser.trackLabelsVisible);
48711
+ }
47975
48712
 
47976
- TrackLabelControl.prototype.setVisibility = function (showTrackLabelButton) {
47977
- if (true === showTrackLabelButton) {
47978
- this.show();
47979
- } else {
47980
- this.hide();
48713
+ setVisibility(showTrackLabelButton) {
48714
+ if (true === showTrackLabelButton) {
48715
+ this.show();
48716
+ } else {
48717
+ this.hide();
48718
+ }
47981
48719
  }
47982
- };
47983
48720
 
47984
- TrackLabelControl.prototype.setState = function (trackLabelsVisible) {
47985
- if (true === trackLabelsVisible) {
47986
- this.button.classList.add('igv-navbar-button-clicked');
47987
- } else {
47988
- this.button.classList.remove('igv-navbar-button-clicked');
48721
+ setState(trackLabelsVisible) {
48722
+ if (true === trackLabelsVisible) {
48723
+ this.button.classList.add('igv-navbar-button-clicked');
48724
+ } else {
48725
+ this.button.classList.remove('igv-navbar-button-clicked');
48726
+ }
47989
48727
  }
47990
- };
47991
48728
 
47992
- TrackLabelControl.prototype.show = function () {
47993
- this.button.style.display = 'block';
47994
- this.setState(this.browser.trackLabelsVisible);
47995
- };
48729
+ show() {
48730
+ this.button.style.display = 'block';
48731
+ this.setState(this.browser.trackLabelsVisible);
48732
+ }
47996
48733
 
47997
- TrackLabelControl.prototype.hide = function () {
47998
- this.button.style.display = 'none';
47999
- };
48734
+ hide() {
48735
+ this.button.style.display = 'none';
48736
+ }
48737
+ }
48000
48738
 
48001
48739
  /*
48002
48740
  * The MIT License (MIT)
@@ -48281,55 +49019,6 @@ const SVGSaveControl = function (parent, browser) {
48281
49019
  button.addEventListener('click', () => browser.saveSVGtoFile({}));
48282
49020
  };
48283
49021
 
48284
- const viewportColumnManager =
48285
- {
48286
- createColumns: (columnContainer, count) => {
48287
-
48288
- for (let i = 0; i < count; i++) {
48289
- if (0 === i) {
48290
- createColumn(columnContainer, 'igv-column');
48291
- } else {
48292
- columnContainer.appendChild(div$1({class: 'igv-column-shim'}));
48293
- createColumn(columnContainer, 'igv-column');
48294
- }
48295
- }
48296
-
48297
- },
48298
-
48299
- removeColumnAtIndex: (i, column) => {
48300
- const shim = 0 === i ? column.nextElementSibling : column.previousElementSibling;
48301
- column.remove();
48302
- shim.remove();
48303
- },
48304
-
48305
- insertAfter: referenceElement => {
48306
-
48307
- const shim = div$1({class: 'igv-column-shim'});
48308
- insertElementAfter(shim, referenceElement);
48309
-
48310
- const column = div$1({class: 'igv-column'});
48311
- insertElementAfter(column, shim);
48312
-
48313
- return column
48314
- },
48315
-
48316
- insertBefore: (referenceElement, count) => {
48317
-
48318
- for (let i = 0; i < count; i++) {
48319
-
48320
- const column = div$1({class: 'igv-column'});
48321
- insertElementBefore(column, referenceElement);
48322
-
48323
- if (count > 1 && i > 0) {
48324
- const columnShim = div$1({class: 'igv-column-shim'});
48325
- insertElementBefore(columnShim, column);
48326
- }
48327
-
48328
- }
48329
-
48330
- },
48331
- };
48332
-
48333
49022
  /*
48334
49023
  * The MIT License (MIT)
48335
49024
  *
@@ -48711,6 +49400,18 @@ CircularViewControl.prototype.hide = function () {
48711
49400
  this.button.style.display = 'none';
48712
49401
  };
48713
49402
 
49403
+ /**
49404
+ * User supplied button for the navbar
49405
+ */
49406
+
49407
+ const CustomButton = function (parent, browser, b) {
49408
+
49409
+ const button = div$1({class: 'igv-navbar-button'});
49410
+ parent.append(button);
49411
+ button.textContent = b.label;
49412
+ button.addEventListener('click', () => b.callback(browser));
49413
+ };
49414
+
48714
49415
  /*
48715
49416
  * The MIT License (MIT)
48716
49417
  *
@@ -48756,7 +49457,7 @@ class Browser {
48756
49457
  constructor(config, parentDiv) {
48757
49458
 
48758
49459
  this.config = config;
48759
- this.guid = guid$1();
49460
+ this.guid = guid$2();
48760
49461
  this.namespace = '.browser_' + this.guid;
48761
49462
 
48762
49463
  this.parent = parentDiv;
@@ -48808,7 +49509,7 @@ class Browser {
48808
49509
 
48809
49510
  this.isCenterLineVisible = config.showCenterGuide;
48810
49511
 
48811
- this.cursorGuideVisible = config.showCursorTrackingGuide;
49512
+ this.cursorGuideVisible = config.showCursorGuide;
48812
49513
 
48813
49514
  this.showSampleNames = config.showSampleNames;
48814
49515
  this.showSampleNameButton = config.showSampleNameButton;
@@ -48921,6 +49622,12 @@ class Browser {
48921
49622
  this.svgSaveControl = new SVGSaveControl($toggle_button_container.get(0), this);
48922
49623
  }
48923
49624
 
49625
+ if(config.customButtons) {
49626
+ for(let b of config.customButtons) {
49627
+ new CustomButton($toggle_button_container.get(0), this, b);
49628
+ }
49629
+ }
49630
+
48924
49631
  this.zoomWidget = new ZoomWidget(this, $navbarRightContainer.get(0));
48925
49632
 
48926
49633
  if (false === config.showNavigation) {
@@ -48928,13 +49635,13 @@ class Browser {
48928
49635
  }
48929
49636
 
48930
49637
  this.inputDialog = new InputDialog(this.root);
48931
- this.inputDialog.container.id = `igv-input-dialog-${guid$1()}`;
49638
+ this.inputDialog.container.id = `igv-input-dialog-${guid$2()}`;
48932
49639
 
48933
49640
  this.dataRangeDialog = new DataRangeDialog($$1(this.root));
48934
- this.dataRangeDialog.$container.get(0).id = `igv-data-range-dialog-${guid$1()}`;
49641
+ this.dataRangeDialog.$container.get(0).id = `igv-data-range-dialog-${guid$2()}`;
48935
49642
 
48936
49643
  this.genericColorPicker = new GenericColorPicker({parent: this.columnContainer, width: 432});
48937
- this.genericColorPicker.container.id = `igv-track-color-picker-${guid$1()}`;
49644
+ this.genericColorPicker.container.id = `igv-track-color-picker-${guid$2()}`;
48938
49645
 
48939
49646
  return $navBar
48940
49647
 
@@ -49963,46 +50670,6 @@ class Browser {
49963
50670
 
49964
50671
  }
49965
50672
 
49966
- async selectMultiLocusPanel(referenceFrame) {
49967
-
49968
- const referenceFrameIndex = this.referenceFrameList.indexOf(referenceFrame);
49969
-
49970
- // Remove columns for unselected panels
49971
- this.columnContainer.querySelectorAll('.igv-column').forEach((column, c) => {
49972
- if (c === referenceFrameIndex) ; else {
49973
-
49974
- column.remove();
49975
- }
49976
- });
49977
-
49978
- // Remove all column shims
49979
- this.columnContainer.querySelectorAll('.igv-column-shim').forEach(shim => shim.remove());
49980
-
49981
- // Discard viewports
49982
- for (let trackView of this.trackViews) {
49983
-
49984
- const retain = trackView.viewports[referenceFrameIndex];
49985
- trackView.viewports.filter((viewport, i) => i !== referenceFrameIndex).forEach(viewport => viewport.dispose());
49986
- trackView.viewports = [retain];
49987
-
49988
- }
49989
-
49990
- const viewportWidth = this.calculateViewportWidth(1);
49991
- referenceFrame.bpPerPixel = (referenceFrame.end - referenceFrame.start) / viewportWidth;
49992
-
49993
- this.referenceFrameList = [referenceFrame];
49994
-
49995
- this.trackViews.forEach(({viewports}) => viewports.forEach(viewport => viewport.setWidth(viewportWidth)));
49996
-
49997
-
49998
- this.centerLineList = this.createCenterLineList(this.columnContainer);
49999
-
50000
- this.updateUIWithReferenceFrameList();
50001
-
50002
- await this.updateViews(true);
50003
-
50004
- }
50005
-
50006
50673
  async selectMultiLocusPanel(referenceFrame) {
50007
50674
 
50008
50675
  const referenceFrameIndex = this.referenceFrameList.indexOf(referenceFrame);
@@ -50226,6 +50893,7 @@ class Browser {
50226
50893
  // Build locus array (multi-locus view). Use the first track to extract the loci, any track could be used.
50227
50894
  const locus = [];
50228
50895
  const gtexSelections = {};
50896
+ let hasGtexSelections = false;
50229
50897
  let anyTrackView = this.trackViews[0];
50230
50898
  for (let {referenceFrame} of anyTrackView.viewports) {
50231
50899
  const locusString = referenceFrame.getLocusString();
@@ -50236,12 +50904,11 @@ class Browser {
50236
50904
  snp: referenceFrame.selection.snp
50237
50905
  };
50238
50906
  gtexSelections[locusString] = selection;
50907
+ hasGtexSelections = true;
50239
50908
  }
50240
50909
  }
50241
50910
  json["locus"] = locus.length === 1 ? locus[0] : locus;
50242
-
50243
- const gtexKeys = Object.getOwnPropertyNames(gtexSelections);
50244
- if (gtexKeys.length > 0) {
50911
+ if (hasGtexSelections) {
50245
50912
  json["gtexSelections"] = gtexSelections;
50246
50913
  }
50247
50914
 
@@ -50265,27 +50932,21 @@ class Browser {
50265
50932
  trackJson.push(config);
50266
50933
  }
50267
50934
  } catch (e) {
50268
- errors.push(e);
50935
+ console.error(`Track: ${track.name}: ${e}`);
50936
+ errors.push(`Track: ${track.name}: ${e}`);
50269
50937
  }
50270
50938
  }
50271
50939
 
50272
50940
  if (errors.length > 0) {
50273
50941
  let n = 1;
50274
- let message = 'Errors encountered saving session:';
50942
+ let message = 'Errors encountered saving session: </br>';
50275
50943
  for (let e of errors) {
50276
- message += ` (${n++}) ${e.toString()}.`;
50944
+ message += ` (${n++}) ${e.toString()} <br/>`;
50277
50945
  }
50278
50946
  throw Error(message)
50279
50947
  }
50280
50948
 
50281
50949
 
50282
- const locaTrackFiles = trackJson.filter((track) => {
50283
- track.url && isFile(track.url);
50284
- });
50285
-
50286
- if (locaTrackFiles.length > 0) {
50287
- throw new Error(`Error. Sessions cannot include local file references.`)
50288
- }
50289
50950
 
50290
50951
  json["tracks"] = trackJson;
50291
50952
 
@@ -50673,7 +51334,7 @@ async function createBrowser(parentDiv, config) {
50673
51334
 
50674
51335
  setDefaults(config);
50675
51336
 
50676
- if (config.queryParametersSupported !== false) {
51337
+ if (config.queryParametersSupported) {
50677
51338
  extractQuery(config);
50678
51339
  }
50679
51340
  if (config.apiKey) {
@@ -50737,10 +51398,6 @@ async function visibilityChange() {
50737
51398
 
50738
51399
  function setDefaults(config) {
50739
51400
 
50740
- if (undefined === config.promisified) {
50741
- config.promisified = false;
50742
- }
50743
-
50744
51401
  if (undefined === config.minimumBases) {
50745
51402
  config.minimumBases = 40;
50746
51403
  }
@@ -50769,8 +51426,9 @@ function setDefaults(config) {
50769
51426
  config.showCursorTrackingGuideButton = true;
50770
51427
  }
50771
51428
 
50772
- if (undefined === config.showCursorTrackingGuide) {
50773
- config.showCursorTrackingGuide = false;
51429
+
51430
+ if (undefined === config.showCursorGuide) {
51431
+ config.showCursorGuide = config.showCursorTrackingGuide || false; // showCursorTrackingGuide is a synonym
50774
51432
  }
50775
51433
 
50776
51434
  if (undefined === config.showCenterGuideButton) {
@@ -50861,6 +51519,8 @@ function extractQuery(config) {
50861
51519
  config[key] = value;
50862
51520
  }
50863
51521
  i = j + 1;
51522
+ } else {
51523
+ i++;
50864
51524
  }
50865
51525
  }
50866
51526
  }
@@ -50897,7 +51557,7 @@ async function createTrack(config, browser) {
50897
51557
 
50898
51558
  function embedCSS() {
50899
51559
 
50900
- var css = '.igv-navbar {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n box-sizing: border-box;\n width: 100%;\n color: #444;\n font-size: 12px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n line-height: 32px;\n padding-left: 8px;\n padding-right: 8px;\n margin-top: 2px;\n margin-bottom: 6px;\n height: 32px;\n border-style: solid;\n border-radius: 3px;\n border-width: thin;\n border-color: #bfbfbf;\n background-color: #f3f3f3; }\n .igv-navbar .igv-navbar-left-container {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n height: 32px;\n line-height: 32px; }\n .igv-navbar .igv-navbar-left-container .igv-logo {\n width: 34px;\n height: 32px;\n margin-right: 8px; }\n .igv-navbar .igv-navbar-left-container .igv-current-genome {\n height: 32px;\n margin-left: 4px;\n margin-right: 4px;\n user-select: none;\n line-height: 32px;\n vertical-align: middle;\n text-align: center; }\n .igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n height: 100%; }\n .igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-chromosome-select-widget-container {\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n height: 100%;\n width: 125px;\n margin-right: 4px; }\n .igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-chromosome-select-widget-container select {\n display: block;\n cursor: pointer;\n width: 100px;\n height: 75%;\n outline: none;\n font-size: 12px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400; }\n .igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-locus-size-group {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n margin-left: 8px;\n height: 22px; }\n .igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-locus-size-group .igv-search-container {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n width: 210px;\n height: 22px;\n line-height: 22px; }\n .igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-locus-size-group .igv-search-container input.igv-search-input {\n cursor: text;\n width: 85%;\n height: 22px;\n line-height: 22px;\n font-size: 12px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n text-align: left;\n padding-left: 8px;\n margin-right: 8px;\n outline: none;\n border-style: solid;\n border-radius: 3px;\n border-width: thin;\n border-color: #bfbfbf;\n background-color: white; }\n .igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-locus-size-group .igv-search-container .igv-search-icon-container {\n cursor: pointer;\n height: 16px;\n width: 16px; }\n .igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-locus-size-group .igv-windowsize-panel-container {\n margin-left: 4px;\n user-select: none; }\n .igv-navbar .igv-navbar-right-container {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n height: 32px;\n line-height: 32px; }\n .igv-navbar .igv-navbar-right-container .igv-navbar-toggle-button-container {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n height: 100%; }\n .igv-navbar .igv-navbar-right-container .igv-navbar-toggle-button-container div {\n margin-left: 0;\n margin-right: 4px; }\n .igv-navbar .igv-navbar-right-container .igv-navbar-toggle-button-container div:last-child {\n margin-left: 0;\n margin-right: 0; }\n .igv-navbar .igv-navbar-right-container .igv-navbar-toggle-button-container-750 {\n display: none; }\n .igv-navbar .igv-navbar-right-container .igv-zoom-widget {\n color: #737373;\n font-size: 18px;\n height: 32px;\n line-height: 32px;\n margin-left: 8px;\n user-select: none;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center; }\n .igv-navbar .igv-navbar-right-container .igv-zoom-widget div {\n cursor: pointer;\n margin-left: unset;\n margin-right: unset; }\n .igv-navbar .igv-navbar-right-container .igv-zoom-widget div:first-child {\n height: 24px;\n width: 24px;\n margin-left: unset;\n margin-right: 8px; }\n .igv-navbar .igv-navbar-right-container .igv-zoom-widget div:last-child {\n height: 24px;\n width: 24px;\n margin-left: 8px;\n margin-right: unset; }\n .igv-navbar .igv-navbar-right-container .igv-zoom-widget div:nth-child(even) {\n display: block;\n height: fit-content; }\n .igv-navbar .igv-navbar-right-container .igv-zoom-widget input {\n display: block;\n width: 125px; }\n .igv-navbar .igv-navbar-right-container .igv-zoom-widget svg {\n display: block; }\n .igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 {\n color: #737373;\n font-size: 18px;\n height: 32px;\n line-height: 32px;\n margin-left: 8px;\n user-select: none;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center; }\n .igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 div {\n cursor: pointer;\n margin-left: unset;\n margin-right: unset; }\n .igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 div:first-child {\n height: 24px;\n width: 24px;\n margin-left: unset;\n margin-right: 8px; }\n .igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 div:last-child {\n height: 24px;\n width: 24px;\n margin-left: 8px;\n margin-right: unset; }\n .igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 div:nth-child(even) {\n width: 0;\n height: 0;\n display: none; }\n .igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 input {\n width: 0;\n height: 0;\n display: none; }\n .igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 svg {\n display: block; }\n .igv-navbar .igv-navbar-right-container .igv-zoom-widget-hidden {\n display: none; }\n\n.igv-navbar-button {\n display: block;\n box-sizing: unset;\n padding-left: 6px;\n padding-right: 6px;\n height: 18px;\n text-transform: capitalize;\n user-select: none;\n line-height: 18px;\n text-align: center;\n vertical-align: middle;\n font-family: \"Open Sans\", sans-serif;\n font-size: 11px;\n font-weight: 200;\n color: #737373;\n background-color: #f3f3f3;\n border-color: #737373;\n border-style: solid;\n border-width: thin;\n border-radius: 6px; }\n\n.igv-navbar-button-clicked {\n color: white;\n background-color: #737373; }\n\n.igv-navbar-button:hover {\n cursor: pointer; }\n\n.igv-zoom-in-notice-container {\n z-index: 1024;\n position: absolute;\n top: 8px;\n left: 50%;\n transform: translate(-50%, 0%);\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n justify-content: center;\n align-items: center;\n background-color: white; }\n .igv-zoom-in-notice-container > div {\n padding-left: 4px;\n padding-right: 4px;\n padding-top: 2px;\n padding-bottom: 2px;\n width: 100%;\n height: 100%;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n color: #3f3f3f; }\n\n.igv-zoom-in-notice {\n position: absolute;\n top: 10px;\n left: 50%; }\n .igv-zoom-in-notice div {\n position: relative;\n left: -50%;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n color: #3f3f3f;\n background-color: rgba(255, 255, 255, 0.51);\n z-index: 64; }\n\n.igv-container-spinner {\n position: absolute;\n top: 90%;\n left: 50%;\n transform: translate(-50%, -50%);\n z-index: 1024;\n width: 24px;\n height: 24px;\n pointer-events: none;\n color: #737373; }\n\n.igv-multi-locus-close-button {\n position: absolute;\n top: 2px;\n right: 0;\n padding-left: 2px;\n padding-right: 2px;\n width: 14px;\n height: 14px;\n color: #666666;\n background-color: white;\n z-index: 1000; }\n .igv-multi-locus-close-button > svg {\n vertical-align: top; }\n\n.igv-multi-locus-close-button:hover {\n cursor: pointer;\n color: #434343; }\n\n.igv-multi-locus-ruler-label {\n z-index: 64;\n position: absolute;\n top: 2px;\n left: 0;\n width: 100%;\n height: 14px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: center;\n align-items: center; }\n .igv-multi-locus-ruler-label div {\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n color: #101010;\n background-color: white; }\n\n.igv-multi-locus-ruler-label-square-dot {\n z-index: 64;\n position: absolute;\n left: 50%;\n top: 5%;\n transform: translate(-50%, 0%);\n background-color: white;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center; }\n .igv-multi-locus-ruler-label-square-dot > div:first-child {\n width: 14px;\n height: 14px; }\n .igv-multi-locus-ruler-label-square-dot > div:last-child {\n margin-left: 16px;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n color: #101010; }\n\n.igv-multi-locus-ruler-label:hover {\n cursor: pointer; }\n\n.igv-ruler-sweeper {\n display: none;\n pointer-events: none;\n position: absolute;\n top: 0;\n left: 0;\n width: 0;\n height: 100%;\n z-index: 99999;\n background-color: rgba(68, 134, 247, 0.25); }\n\n.igv-ruler-tooltip {\n pointer-events: none;\n z-index: 128;\n position: absolute;\n top: 0;\n left: 0;\n width: 1px;\n height: 32px;\n background-color: transparent;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center; }\n .igv-ruler-tooltip > div {\n pointer-events: none;\n width: 128px;\n height: auto;\n padding: 1px;\n color: #373737;\n font-size: 10px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n background-color: white;\n border-style: solid;\n border-width: thin;\n border-color: #373737; }\n\n.igv-track-label {\n position: absolute;\n left: 8px;\n top: 8px;\n width: auto;\n height: auto;\n max-width: 200px;\n padding-left: 4px;\n padding-right: 4px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n font-weight: 400;\n text-align: center;\n user-select: none;\n -moz-user-select: none;\n -webkit-user-select: none;\n border-color: #444;\n border-radius: 2px;\n border-style: solid;\n border-width: thin;\n background-color: white;\n z-index: 128;\n cursor: pointer; }\n\n.igv-track-label:hover,\n.igv-track-label:focus,\n.igv-track-label:active {\n background-color: #e8e8e8; }\n\n.igv-center-line {\n display: none;\n pointer-events: none;\n position: absolute;\n top: 0;\n bottom: 0;\n transform: translateX(-50%);\n z-index: 8;\n user-select: none;\n -moz-user-select: none;\n -webkit-user-select: none;\n border-left-style: dashed;\n border-left-width: thin;\n border-right-style: dashed;\n border-right-width: thin; }\n\n.igv-center-line-wide {\n background-color: rgba(0, 0, 0, 0);\n border-left-color: rgba(127, 127, 127, 0.51);\n border-right-color: rgba(127, 127, 127, 0.51); }\n\n.igv-center-line-thin {\n background-color: rgba(0, 0, 0, 0);\n border-left-color: rgba(127, 127, 127, 0.51);\n border-right-color: rgba(0, 0, 0, 0); }\n\n.igv-cursor-guide-horizontal {\n display: none;\n pointer-events: none;\n user-select: none;\n -moz-user-select: none;\n -webkit-user-select: none;\n position: absolute;\n left: 0;\n right: 0;\n top: 50%;\n height: 1px;\n z-index: 1;\n margin-left: 50px;\n margin-right: 54px;\n border-top-style: dotted;\n border-top-width: thin;\n border-top-color: rgba(127, 127, 127, 0.76); }\n\n.igv-cursor-guide-vertical {\n pointer-events: none;\n user-select: none;\n -moz-user-select: none;\n -webkit-user-select: none;\n position: absolute;\n top: 0;\n bottom: 0;\n left: 50%;\n width: 1px;\n z-index: 1;\n border-left-style: dotted;\n border-left-width: thin;\n border-left-color: rgba(127, 127, 127, 0.76);\n display: none; }\n\n.igv-user-feedback {\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 512px;\n height: 360px;\n z-index: 2048;\n background-color: white;\n border-color: #a2a2a2;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n color: #444;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center; }\n .igv-user-feedback div:first-child {\n position: relative;\n height: 24px;\n width: 100%;\n background-color: white;\n border-bottom-color: #a2a2a2;\n border-bottom-style: solid;\n border-bottom-width: thin; }\n .igv-user-feedback div:first-child div {\n position: absolute;\n top: 2px;\n width: 16px;\n height: 16px;\n background-color: transparent; }\n .igv-user-feedback div:first-child div:first-child {\n left: 8px; }\n .igv-user-feedback div:first-child div:last-child {\n cursor: pointer;\n right: 8px; }\n .igv-user-feedback div:last-child {\n width: 100%;\n height: calc(100% - 24px);\n border-width: 0; }\n .igv-user-feedback div:last-child div {\n width: auto;\n height: auto;\n margin: 8px; }\n\n.igv-generic-dialog-container {\n position: absolute;\n top: 0;\n left: 0;\n width: 300px;\n height: 200px;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n z-index: 2048;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center; }\n .igv-generic-dialog-container .igv-generic-dialog-header {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee; }\n .igv-generic-dialog-container .igv-generic-dialog-header div {\n margin-right: 4px;\n margin-bottom: 2px;\n height: 12px;\n width: 12px;\n color: #7F7F7F; }\n .igv-generic-dialog-container .igv-generic-dialog-header div:hover {\n cursor: pointer;\n color: #444; }\n .igv-generic-dialog-container .igv-generic-dialog-one-liner {\n color: #373737;\n width: 95%;\n height: 24px;\n line-height: 24px;\n text-align: left;\n margin-top: 8px;\n padding-left: 8px;\n overflow-wrap: break-word;\n background-color: white; }\n .igv-generic-dialog-container .igv-generic-dialog-label-input {\n margin-top: 8px;\n width: 95%;\n height: 24px;\n color: #373737;\n line-height: 24px;\n padding-left: 8px;\n background-color: white;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center; }\n .igv-generic-dialog-container .igv-generic-dialog-label-input div {\n width: 30%;\n height: 100%;\n font-size: 16px;\n text-align: right;\n padding-right: 8px;\n background-color: white; }\n .igv-generic-dialog-container .igv-generic-dialog-label-input input {\n display: block;\n height: 100%;\n width: 100%;\n padding-left: 4px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n color: #373737;\n text-align: left;\n outline: none;\n border-style: solid;\n border-width: thin;\n border-color: #7F7F7F;\n background-color: white; }\n .igv-generic-dialog-container .igv-generic-dialog-label-input input {\n width: 50%;\n font-size: 16px; }\n .igv-generic-dialog-container .igv-generic-dialog-input {\n margin-top: 8px;\n width: calc(100% - 16px);\n height: 24px;\n color: #373737;\n line-height: 24px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-generic-dialog-container .igv-generic-dialog-input input {\n display: block;\n height: 100%;\n width: 100%;\n padding-left: 4px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n color: #373737;\n text-align: left;\n outline: none;\n border-style: solid;\n border-width: thin;\n border-color: #7F7F7F;\n background-color: white; }\n .igv-generic-dialog-container .igv-generic-dialog-input input {\n font-size: 16px; }\n .igv-generic-dialog-container .igv-generic-dialog-ok-cancel {\n width: 100%;\n height: 28px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-generic-dialog-container .igv-generic-dialog-ok-cancel div {\n margin-top: 32px;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n width: 75px;\n height: 28px;\n line-height: 28px;\n text-align: center;\n border-color: transparent;\n border-style: solid;\n border-width: thin;\n border-radius: 2px; }\n .igv-generic-dialog-container .igv-generic-dialog-ok-cancel div:first-child {\n margin-left: 32px;\n margin-right: 0;\n background-color: #5ea4e0; }\n .igv-generic-dialog-container .igv-generic-dialog-ok-cancel div:last-child {\n margin-left: 0;\n margin-right: 32px;\n background-color: #c4c4c4; }\n .igv-generic-dialog-container .igv-generic-dialog-ok-cancel div:first-child:hover {\n cursor: pointer;\n background-color: #3b5c7f; }\n .igv-generic-dialog-container .igv-generic-dialog-ok-cancel div:last-child:hover {\n cursor: pointer;\n background-color: #7f7f7f; }\n .igv-generic-dialog-container .igv-generic-dialog-ok {\n width: 100%;\n height: 36px;\n margin-top: 32px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-generic-dialog-container .igv-generic-dialog-ok div {\n width: 98px;\n height: 36px;\n line-height: 36px;\n text-align: center;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n border-color: white;\n border-style: solid;\n border-width: thin;\n border-radius: 4px;\n background-color: #2B81AF; }\n .igv-generic-dialog-container .igv-generic-dialog-ok div:hover {\n cursor: pointer;\n background-color: #25597f; }\n\n.igv-generic-container {\n position: absolute;\n top: 0;\n left: 0;\n z-index: 2048;\n background-color: white;\n cursor: pointer;\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n justify-content: flex-start;\n align-items: center; }\n .igv-generic-container div:first-child {\n cursor: move;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n height: 24px;\n width: 100%;\n background-color: #dddddd; }\n .igv-generic-container div:first-child i {\n display: block;\n color: #5f5f5f;\n cursor: pointer;\n width: 14px;\n height: 14px;\n margin-right: 8px;\n margin-bottom: 4px; }\n\n.igv-menu-popup {\n position: absolute;\n top: 0;\n left: 0;\n width: max-content;\n z-index: 4096;\n cursor: pointer;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n font-weight: 400;\n color: #4b4b4b;\n background: white;\n border-radius: 4px;\n border-color: #7F7F7F;\n border-style: solid;\n border-width: thin;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-end;\n text-align: left; }\n .igv-menu-popup > div:not(:first-child) {\n width: 100%; }\n .igv-menu-popup > div:not(:first-child) > div {\n background: white; }\n .igv-menu-popup > div:not(:first-child) > div.context-menu {\n padding-left: 4px;\n padding-right: 4px; }\n .igv-menu-popup > div:not(:first-child) > div:last-child {\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n border-bottom-color: transparent;\n border-bottom-style: solid;\n border-bottom-width: thin; }\n .igv-menu-popup > div:not(:first-child) > div:hover {\n background: #efefef; }\n\n.igv-menu-popup-shim {\n padding-left: 8px;\n padding-right: 8px; }\n\n.igv-menu-popup-header {\n position: relative;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-color: transparent;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center; }\n .igv-menu-popup-header div {\n margin-right: 4px;\n height: 12px;\n width: 12px;\n color: #7F7F7F; }\n .igv-menu-popup-header div:hover {\n cursor: pointer;\n color: #444; }\n\n.igv-menu-popup-check-container {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n width: 100%;\n height: 20px;\n background-color: transparent; }\n .igv-menu-popup-check-container div {\n padding-top: 2px;\n padding-left: 8px; }\n .igv-menu-popup-check-container div:first-child {\n position: relative;\n width: 12px;\n height: 12px; }\n .igv-menu-popup-check-container div:first-child svg {\n position: absolute;\n width: 12px;\n height: 12px; }\n\n.igv-user-feedback {\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 512px;\n height: 360px;\n z-index: 2048;\n background-color: white;\n border-color: #a2a2a2;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n color: #444;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center; }\n .igv-user-feedback div:first-child {\n position: relative;\n height: 24px;\n width: 100%;\n background-color: white;\n border-bottom-color: #a2a2a2;\n border-bottom-style: solid;\n border-bottom-width: thin; }\n .igv-user-feedback div:first-child div {\n position: absolute;\n top: 2px;\n width: 16px;\n height: 16px;\n background-color: transparent; }\n .igv-user-feedback div:first-child div:first-child {\n left: 8px; }\n .igv-user-feedback div:first-child div:last-child {\n cursor: pointer;\n right: 8px; }\n .igv-user-feedback div:last-child {\n width: 100%;\n height: calc(100% - 24px);\n border-width: 0; }\n .igv-user-feedback div:last-child div {\n width: auto;\n height: auto;\n margin: 8px; }\n\n.igv-loading-spinner-container {\n z-index: 1024;\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 32px;\n height: 32px;\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n justify-content: center;\n align-items: center; }\n .igv-loading-spinner-container > div {\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n border-radius: 50%;\n border: 4px solid rgba(128, 128, 128, 0.5);\n border-top-color: white;\n animation: spin 1s ease-in-out infinite;\n -webkit-animation: spin 1s ease-in-out infinite; }\n\n@keyframes spin {\n to {\n -webkit-transform: rotate(360deg);\n transform: rotate(360deg); } }\n@-webkit-keyframes spin {\n to {\n -webkit-transform: rotate(360deg);\n transform: rotate(360deg); } }\n.igv-container {\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n padding-top: 4px;\n user-select: none;\n -webkit-user-select: none;\n -ms-user-select: none; }\n\n.igv-viewport {\n position: relative;\n margin-top: 5px;\n overflow-x: hidden;\n overflow-y: hidden; }\n\n.igv-viewport-content {\n position: relative;\n width: 100%; }\n .igv-viewport-content > canvas {\n position: relative;\n display: block; }\n\n.igv-column-container {\n position: relative;\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: stretch;\n width: 100%; }\n\n.igv-column-shim {\n width: 1px;\n margin-left: 2px;\n margin-right: 2px;\n background-color: #545453; }\n\n.igv-column {\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%; }\n\n.igv-axis-column {\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n width: 50px; }\n .igv-axis-column > div {\n margin-top: 5px;\n width: 100%; }\n\n.igv-sample-name-column {\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%; }\n\n.igv-scrollbar-column {\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n width: 14px; }\n .igv-scrollbar-column > div {\n position: relative;\n margin-top: 5px;\n width: 14px; }\n .igv-scrollbar-column > div > div {\n cursor: pointer;\n position: absolute;\n top: 0;\n left: 2px;\n width: 8px;\n border-width: 1px;\n border-style: solid;\n border-color: #c4c4c4;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px; }\n .igv-scrollbar-column > div > div:hover {\n background-color: #c4c4c4; }\n\n.igv-track-drag-column {\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n width: 12px;\n background-color: white; }\n .igv-track-drag-column > .igv-track-drag-handle {\n z-index: 512;\n position: relative;\n cursor: pointer;\n margin-top: 5px;\n width: 100%;\n border-style: solid;\n border-width: 0;\n border-top-right-radius: 6px;\n border-bottom-right-radius: 6px;\n background-color: #c4c4c4; }\n .igv-track-drag-column .igv-track-drag-handle-hover {\n background-color: #787878; }\n .igv-track-drag-column > .igv-track-drag-shim {\n position: relative;\n margin-top: 5px;\n width: 100%;\n border-style: solid;\n border-width: 0; }\n\n.igv-gear-menu-column {\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n width: 28px; }\n .igv-gear-menu-column > div {\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n margin-top: 5px;\n width: 100%;\n background: white; }\n .igv-gear-menu-column > div > div {\n position: relative;\n margin-top: 4px;\n width: 16px;\n height: 16px;\n color: #7F7F7F; }\n .igv-gear-menu-column > div > div:hover {\n cursor: pointer;\n color: #444; }\n\n/*# sourceMappingURL=dom.css.map */\n';
51560
+ var css = '.igv-navbar {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n box-sizing: border-box;\n width: 100%;\n color: #444;\n font-size: 12px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n line-height: 32px;\n padding-left: 8px;\n padding-right: 8px;\n margin-top: 2px;\n margin-bottom: 6px;\n height: 32px;\n border-style: solid;\n border-radius: 3px;\n border-width: thin;\n border-color: #bfbfbf;\n background-color: #f3f3f3; }\n .igv-navbar .igv-navbar-left-container {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n height: 32px;\n line-height: 32px; }\n .igv-navbar .igv-navbar-left-container .igv-logo {\n width: 34px;\n height: 32px;\n margin-right: 8px; }\n .igv-navbar .igv-navbar-left-container .igv-current-genome {\n height: 32px;\n margin-left: 4px;\n margin-right: 4px;\n user-select: none;\n line-height: 32px;\n vertical-align: middle;\n text-align: center; }\n .igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n height: 100%; }\n .igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-chromosome-select-widget-container {\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n height: 100%;\n width: 125px;\n margin-right: 4px; }\n .igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-chromosome-select-widget-container select {\n display: block;\n cursor: pointer;\n width: 100px;\n height: 75%;\n outline: none;\n font-size: 12px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400; }\n .igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-locus-size-group {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n margin-left: 8px;\n height: 22px; }\n .igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-locus-size-group .igv-search-container {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n width: 210px;\n height: 22px;\n line-height: 22px; }\n .igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-locus-size-group .igv-search-container input.igv-search-input {\n cursor: text;\n width: 85%;\n height: 22px;\n line-height: 22px;\n font-size: 12px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n text-align: left;\n padding-left: 8px;\n margin-right: 8px;\n outline: none;\n border-style: solid;\n border-radius: 3px;\n border-width: thin;\n border-color: #bfbfbf;\n background-color: white; }\n .igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-locus-size-group .igv-search-container .igv-search-icon-container {\n cursor: pointer;\n height: 16px;\n width: 16px; }\n .igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-locus-size-group .igv-windowsize-panel-container {\n margin-left: 4px;\n user-select: none; }\n .igv-navbar .igv-navbar-right-container {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n height: 32px;\n line-height: 32px; }\n .igv-navbar .igv-navbar-right-container .igv-navbar-toggle-button-container {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n height: 100%; }\n .igv-navbar .igv-navbar-right-container .igv-navbar-toggle-button-container div {\n margin-left: 0;\n margin-right: 4px; }\n .igv-navbar .igv-navbar-right-container .igv-navbar-toggle-button-container div:last-child {\n margin-left: 0;\n margin-right: 0; }\n .igv-navbar .igv-navbar-right-container .igv-navbar-toggle-button-container-750 {\n display: none; }\n .igv-navbar .igv-navbar-right-container .igv-zoom-widget {\n color: #737373;\n font-size: 18px;\n height: 32px;\n line-height: 32px;\n margin-left: 8px;\n user-select: none;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center; }\n .igv-navbar .igv-navbar-right-container .igv-zoom-widget div {\n cursor: pointer;\n margin-left: unset;\n margin-right: unset; }\n .igv-navbar .igv-navbar-right-container .igv-zoom-widget div:first-child {\n height: 24px;\n width: 24px;\n margin-left: unset;\n margin-right: 8px; }\n .igv-navbar .igv-navbar-right-container .igv-zoom-widget div:last-child {\n height: 24px;\n width: 24px;\n margin-left: 8px;\n margin-right: unset; }\n .igv-navbar .igv-navbar-right-container .igv-zoom-widget div:nth-child(even) {\n display: block;\n height: fit-content; }\n .igv-navbar .igv-navbar-right-container .igv-zoom-widget input {\n display: block;\n width: 125px; }\n .igv-navbar .igv-navbar-right-container .igv-zoom-widget svg {\n display: block; }\n .igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 {\n color: #737373;\n font-size: 18px;\n height: 32px;\n line-height: 32px;\n margin-left: 8px;\n user-select: none;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center; }\n .igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 div {\n cursor: pointer;\n margin-left: unset;\n margin-right: unset; }\n .igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 div:first-child {\n height: 24px;\n width: 24px;\n margin-left: unset;\n margin-right: 8px; }\n .igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 div:last-child {\n height: 24px;\n width: 24px;\n margin-left: 8px;\n margin-right: unset; }\n .igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 div:nth-child(even) {\n width: 0;\n height: 0;\n display: none; }\n .igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 input {\n width: 0;\n height: 0;\n display: none; }\n .igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 svg {\n display: block; }\n .igv-navbar .igv-navbar-right-container .igv-zoom-widget-hidden {\n display: none; }\n\n.igv-navbar-button {\n display: block;\n box-sizing: unset;\n padding-left: 6px;\n padding-right: 6px;\n height: 18px;\n text-transform: capitalize;\n user-select: none;\n line-height: 18px;\n text-align: center;\n vertical-align: middle;\n font-family: \"Open Sans\", sans-serif;\n font-size: 11px;\n font-weight: 200;\n color: #737373;\n background-color: #f3f3f3;\n border-color: #737373;\n border-style: solid;\n border-width: thin;\n border-radius: 6px; }\n\n.igv-navbar-button-clicked {\n color: white;\n background-color: #737373; }\n\n.igv-navbar-button:hover {\n cursor: pointer; }\n\n.igv-zoom-in-notice-container {\n z-index: 1024;\n position: absolute;\n top: 8px;\n left: 50%;\n transform: translate(-50%, 0%);\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n justify-content: center;\n align-items: center;\n background-color: white; }\n .igv-zoom-in-notice-container > div {\n padding-left: 4px;\n padding-right: 4px;\n padding-top: 2px;\n padding-bottom: 2px;\n width: 100%;\n height: 100%;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n color: #3f3f3f; }\n\n.igv-zoom-in-notice {\n position: absolute;\n top: 10px;\n left: 50%; }\n .igv-zoom-in-notice div {\n position: relative;\n left: -50%;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n color: #3f3f3f;\n background-color: rgba(255, 255, 255, 0.51);\n z-index: 64; }\n\n.igv-container-spinner {\n position: absolute;\n top: 90%;\n left: 50%;\n transform: translate(-50%, -50%);\n z-index: 1024;\n width: 24px;\n height: 24px;\n pointer-events: none;\n color: #737373; }\n\n.igv-multi-locus-close-button {\n position: absolute;\n top: 2px;\n right: 0;\n padding-left: 2px;\n padding-right: 2px;\n width: 18px;\n height: 18px;\n color: #666666;\n background-color: white;\n z-index: 1000; }\n .igv-multi-locus-close-button > svg {\n vertical-align: top; }\n\n.igv-multi-locus-close-button:hover {\n cursor: pointer;\n color: #434343; }\n\n.igv-multi-locus-ruler-label {\n z-index: 64;\n position: absolute;\n top: 2px;\n left: 0;\n width: 100%;\n height: 14px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: center;\n align-items: center; }\n .igv-multi-locus-ruler-label div {\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n color: #101010;\n background-color: white; }\n\n.igv-multi-locus-ruler-label-square-dot {\n z-index: 64;\n position: absolute;\n left: 50%;\n top: 5%;\n transform: translate(-50%, 0%);\n background-color: white;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center; }\n .igv-multi-locus-ruler-label-square-dot > div:first-child {\n width: 14px;\n height: 14px; }\n .igv-multi-locus-ruler-label-square-dot > div:last-child {\n margin-left: 16px;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n color: #101010; }\n\n.igv-multi-locus-ruler-label:hover {\n cursor: pointer; }\n\n.igv-ruler-sweeper {\n display: none;\n pointer-events: none;\n position: absolute;\n top: 0;\n left: 0;\n width: 0;\n height: 100%;\n z-index: 99999;\n background-color: rgba(68, 134, 247, 0.25); }\n\n.igv-ruler-tooltip {\n pointer-events: none;\n z-index: 128;\n position: absolute;\n top: 0;\n left: 0;\n width: 1px;\n height: 32px;\n background-color: transparent;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center; }\n .igv-ruler-tooltip > div {\n pointer-events: none;\n width: 128px;\n height: auto;\n padding: 1px;\n color: #373737;\n font-size: 10px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n background-color: white;\n border-style: solid;\n border-width: thin;\n border-color: #373737; }\n\n.igv-track-label {\n position: absolute;\n left: 8px;\n top: 8px;\n width: auto;\n height: auto;\n max-width: 200px;\n padding-left: 4px;\n padding-right: 4px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n font-weight: 400;\n text-align: center;\n user-select: none;\n -moz-user-select: none;\n -webkit-user-select: none;\n border-color: #444;\n border-radius: 2px;\n border-style: solid;\n border-width: thin;\n background-color: white;\n z-index: 128;\n cursor: pointer; }\n\n.igv-track-label:hover,\n.igv-track-label:focus,\n.igv-track-label:active {\n background-color: #e8e8e8; }\n\n.igv-track-label-popup-shim {\n padding-left: 8px;\n padding-right: 8px;\n padding-top: 4px; }\n\n.igv-center-line {\n display: none;\n pointer-events: none;\n position: absolute;\n top: 0;\n bottom: 0;\n transform: translateX(-50%);\n z-index: 8;\n user-select: none;\n -moz-user-select: none;\n -webkit-user-select: none;\n border-left-style: dashed;\n border-left-width: thin;\n border-right-style: dashed;\n border-right-width: thin; }\n\n.igv-center-line-wide {\n background-color: rgba(0, 0, 0, 0);\n border-left-color: rgba(127, 127, 127, 0.51);\n border-right-color: rgba(127, 127, 127, 0.51); }\n\n.igv-center-line-thin {\n background-color: rgba(0, 0, 0, 0);\n border-left-color: rgba(127, 127, 127, 0.51);\n border-right-color: rgba(0, 0, 0, 0); }\n\n.igv-cursor-guide-horizontal {\n display: none;\n pointer-events: none;\n user-select: none;\n -moz-user-select: none;\n -webkit-user-select: none;\n position: absolute;\n left: 0;\n right: 0;\n top: 50%;\n height: 1px;\n z-index: 1;\n margin-left: 50px;\n margin-right: 54px;\n border-top-style: dotted;\n border-top-width: thin;\n border-top-color: rgba(127, 127, 127, 0.76); }\n\n.igv-cursor-guide-vertical {\n pointer-events: none;\n user-select: none;\n -moz-user-select: none;\n -webkit-user-select: none;\n position: absolute;\n top: 0;\n bottom: 0;\n left: 50%;\n width: 1px;\n z-index: 1;\n border-left-style: dotted;\n border-left-width: thin;\n border-left-color: rgba(127, 127, 127, 0.76);\n display: none; }\n\n.igv-user-feedback {\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 512px;\n height: 360px;\n z-index: 2048;\n background-color: white;\n border-color: #a2a2a2;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n color: #444;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center; }\n .igv-user-feedback div:first-child {\n position: relative;\n height: 24px;\n width: 100%;\n background-color: white;\n border-bottom-color: #a2a2a2;\n border-bottom-style: solid;\n border-bottom-width: thin; }\n .igv-user-feedback div:first-child div {\n position: absolute;\n top: 2px;\n width: 16px;\n height: 16px;\n background-color: transparent; }\n .igv-user-feedback div:first-child div:first-child {\n left: 8px; }\n .igv-user-feedback div:first-child div:last-child {\n cursor: pointer;\n right: 8px; }\n .igv-user-feedback div:last-child {\n width: 100%;\n height: calc(100% - 24px);\n border-width: 0; }\n .igv-user-feedback div:last-child div {\n width: auto;\n height: auto;\n margin: 8px; }\n\n.igv-generic-dialog-container {\n position: absolute;\n top: 0;\n left: 0;\n width: 300px;\n height: 200px;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n z-index: 2048;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center; }\n .igv-generic-dialog-container .igv-generic-dialog-header {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee; }\n .igv-generic-dialog-container .igv-generic-dialog-header div {\n margin-right: 4px;\n margin-bottom: 2px;\n height: 12px;\n width: 12px;\n color: #7F7F7F; }\n .igv-generic-dialog-container .igv-generic-dialog-header div:hover {\n cursor: pointer;\n color: #444; }\n .igv-generic-dialog-container .igv-generic-dialog-one-liner {\n color: #373737;\n width: 95%;\n height: 24px;\n line-height: 24px;\n text-align: left;\n margin-top: 8px;\n padding-left: 8px;\n overflow-wrap: break-word;\n background-color: white; }\n .igv-generic-dialog-container .igv-generic-dialog-label-input {\n margin-top: 8px;\n width: 95%;\n height: 24px;\n color: #373737;\n line-height: 24px;\n padding-left: 8px;\n background-color: white;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center; }\n .igv-generic-dialog-container .igv-generic-dialog-label-input div {\n width: 30%;\n height: 100%;\n font-size: 16px;\n text-align: right;\n padding-right: 8px;\n background-color: white; }\n .igv-generic-dialog-container .igv-generic-dialog-label-input input {\n display: block;\n height: 100%;\n width: 100%;\n padding-left: 4px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n color: #373737;\n text-align: left;\n outline: none;\n border-style: solid;\n border-width: thin;\n border-color: #7F7F7F;\n background-color: white; }\n .igv-generic-dialog-container .igv-generic-dialog-label-input input {\n width: 50%;\n font-size: 16px; }\n .igv-generic-dialog-container .igv-generic-dialog-input {\n margin-top: 8px;\n width: calc(100% - 16px);\n height: 24px;\n color: #373737;\n line-height: 24px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-generic-dialog-container .igv-generic-dialog-input input {\n display: block;\n height: 100%;\n width: 100%;\n padding-left: 4px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n color: #373737;\n text-align: left;\n outline: none;\n border-style: solid;\n border-width: thin;\n border-color: #7F7F7F;\n background-color: white; }\n .igv-generic-dialog-container .igv-generic-dialog-input input {\n font-size: 16px; }\n .igv-generic-dialog-container .igv-generic-dialog-ok-cancel {\n width: 100%;\n height: 28px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-generic-dialog-container .igv-generic-dialog-ok-cancel div {\n margin-top: 32px;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n width: 75px;\n height: 28px;\n line-height: 28px;\n text-align: center;\n border-color: transparent;\n border-style: solid;\n border-width: thin;\n border-radius: 2px; }\n .igv-generic-dialog-container .igv-generic-dialog-ok-cancel div:first-child {\n margin-left: 32px;\n margin-right: 0;\n background-color: #5ea4e0; }\n .igv-generic-dialog-container .igv-generic-dialog-ok-cancel div:last-child {\n margin-left: 0;\n margin-right: 32px;\n background-color: #c4c4c4; }\n .igv-generic-dialog-container .igv-generic-dialog-ok-cancel div:first-child:hover {\n cursor: pointer;\n background-color: #3b5c7f; }\n .igv-generic-dialog-container .igv-generic-dialog-ok-cancel div:last-child:hover {\n cursor: pointer;\n background-color: #7f7f7f; }\n .igv-generic-dialog-container .igv-generic-dialog-ok {\n width: 100%;\n height: 36px;\n margin-top: 32px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-generic-dialog-container .igv-generic-dialog-ok div {\n width: 98px;\n height: 36px;\n line-height: 36px;\n text-align: center;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n border-color: white;\n border-style: solid;\n border-width: thin;\n border-radius: 4px;\n background-color: #2B81AF; }\n .igv-generic-dialog-container .igv-generic-dialog-ok div:hover {\n cursor: pointer;\n background-color: #25597f; }\n\n.igv-generic-container {\n position: absolute;\n top: 0;\n left: 0;\n z-index: 2048;\n background-color: white;\n cursor: pointer;\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n justify-content: flex-start;\n align-items: center; }\n .igv-generic-container div:first-child {\n cursor: move;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n height: 24px;\n width: 100%;\n background-color: #dddddd; }\n .igv-generic-container div:first-child i {\n display: block;\n color: #5f5f5f;\n cursor: pointer;\n width: 14px;\n height: 14px;\n margin-right: 8px;\n margin-bottom: 4px; }\n\n.igv-menu-popup {\n position: absolute;\n top: 0;\n left: 0;\n width: max-content;\n z-index: 4096;\n cursor: pointer;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n font-weight: 400;\n color: #4b4b4b;\n background: white;\n border-radius: 4px;\n border-color: #7F7F7F;\n border-style: solid;\n border-width: thin;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-end;\n text-align: left; }\n .igv-menu-popup > div:not(:first-child) {\n width: 100%; }\n .igv-menu-popup > div:not(:first-child) > div {\n background: white; }\n .igv-menu-popup > div:not(:first-child) > div.context-menu {\n padding-left: 4px;\n padding-right: 4px; }\n .igv-menu-popup > div:not(:first-child) > div:last-child {\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n border-bottom-color: transparent;\n border-bottom-style: solid;\n border-bottom-width: thin; }\n .igv-menu-popup > div:not(:first-child) > div:hover {\n background: #efefef; }\n\n.igv-menu-popup-shim {\n padding-left: 8px;\n padding-right: 8px;\n padding-bottom: 1px;\n padding-top: 1px; }\n\n.igv-menu-popup-header {\n position: relative;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-color: transparent;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center; }\n .igv-menu-popup-header div {\n margin-right: 4px;\n height: 12px;\n width: 12px;\n color: #7F7F7F; }\n .igv-menu-popup-header div:hover {\n cursor: pointer;\n color: #444; }\n\n.igv-menu-popup-check-container {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n width: 100%;\n height: 20px;\n margin-right: 4px;\n background-color: transparent; }\n .igv-menu-popup-check-container div {\n padding-top: 2px;\n padding-left: 8px; }\n .igv-menu-popup-check-container div:first-child {\n position: relative;\n width: 12px;\n height: 12px; }\n .igv-menu-popup-check-container div:first-child svg {\n position: absolute;\n width: 12px;\n height: 12px; }\n\n.igv-user-feedback {\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 512px;\n height: 360px;\n z-index: 2048;\n background-color: white;\n border-color: #a2a2a2;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n color: #444;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center; }\n .igv-user-feedback div:first-child {\n position: relative;\n height: 24px;\n width: 100%;\n background-color: white;\n border-bottom-color: #a2a2a2;\n border-bottom-style: solid;\n border-bottom-width: thin; }\n .igv-user-feedback div:first-child div {\n position: absolute;\n top: 2px;\n width: 16px;\n height: 16px;\n background-color: transparent; }\n .igv-user-feedback div:first-child div:first-child {\n left: 8px; }\n .igv-user-feedback div:first-child div:last-child {\n cursor: pointer;\n right: 8px; }\n .igv-user-feedback div:last-child {\n width: 100%;\n height: calc(100% - 24px);\n border-width: 0; }\n .igv-user-feedback div:last-child div {\n width: auto;\n height: auto;\n margin: 8px; }\n\n.igv-loading-spinner-container {\n z-index: 1024;\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 32px;\n height: 32px;\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n justify-content: center;\n align-items: center; }\n .igv-loading-spinner-container > div {\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n border-radius: 50%;\n border: 4px solid rgba(128, 128, 128, 0.5);\n border-top-color: white;\n animation: spin 1s ease-in-out infinite;\n -webkit-animation: spin 1s ease-in-out infinite; }\n\n@keyframes spin {\n to {\n -webkit-transform: rotate(360deg);\n transform: rotate(360deg); } }\n@-webkit-keyframes spin {\n to {\n -webkit-transform: rotate(360deg);\n transform: rotate(360deg); } }\n.igv-container {\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n padding-top: 4px;\n user-select: none;\n -webkit-user-select: none;\n -ms-user-select: none; }\n\n.igv-viewport {\n position: relative;\n margin-top: 5px;\n overflow-x: hidden;\n overflow-y: hidden; }\n\n.igv-viewport-content {\n position: relative;\n width: 100%; }\n .igv-viewport-content > canvas {\n position: relative;\n display: block; }\n\n.igv-column-container {\n position: relative;\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: stretch;\n width: 100%; }\n\n.igv-column-shim {\n width: 1px;\n margin-left: 2px;\n margin-right: 2px;\n background-color: #545453; }\n\n.igv-column {\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%; }\n\n.igv-axis-column {\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n width: 50px; }\n .igv-axis-column > div {\n margin-top: 5px;\n width: 100%; }\n\n.igv-sample-name-column {\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%; }\n\n.igv-scrollbar-column {\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n width: 14px; }\n .igv-scrollbar-column > div {\n position: relative;\n margin-top: 5px;\n width: 14px; }\n .igv-scrollbar-column > div > div {\n cursor: pointer;\n position: absolute;\n top: 0;\n left: 2px;\n width: 8px;\n border-width: 1px;\n border-style: solid;\n border-color: #c4c4c4;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px; }\n .igv-scrollbar-column > div > div:hover {\n background-color: #c4c4c4; }\n\n.igv-track-drag-column {\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n width: 12px;\n background-color: white; }\n .igv-track-drag-column > .igv-track-drag-handle {\n z-index: 512;\n position: relative;\n cursor: pointer;\n margin-top: 5px;\n width: 100%;\n border-style: solid;\n border-width: 0;\n border-top-right-radius: 6px;\n border-bottom-right-radius: 6px;\n background-color: #c4c4c4; }\n .igv-track-drag-column .igv-track-drag-handle-hover {\n background-color: #787878; }\n .igv-track-drag-column > .igv-track-drag-shim {\n position: relative;\n margin-top: 5px;\n width: 100%;\n border-style: solid;\n border-width: 0; }\n\n.igv-gear-menu-column {\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n width: 28px; }\n .igv-gear-menu-column > div {\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n margin-top: 5px;\n width: 100%;\n background: white; }\n .igv-gear-menu-column > div > div {\n position: relative;\n margin-top: 4px;\n width: 16px;\n height: 16px;\n color: #7F7F7F; }\n .igv-gear-menu-column > div > div:hover {\n cursor: pointer;\n color: #444; }\n\n/*# sourceMappingURL=dom.css.map */\n';
50901
51561
 
50902
51562
  var style = document.createElement('style');
50903
51563
  style.setAttribute('type', 'text/css');