igv 2.10.5 → 2.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/igv.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: 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 -webkit-user-select: all;\n -moz-user-select: all;\n -ms-user-select: all;\n user-select: all;\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');
@@ -19897,6 +19899,218 @@ function translateDeprecatedTypes(config) {
19897
19899
  }
19898
19900
  }
19899
19901
 
19902
+ /*
19903
+ * The MIT License (MIT)
19904
+ *
19905
+ * Copyright (c) 2014 Broad Institute
19906
+ *
19907
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
19908
+ * of this software and associated documentation files (the "Software"), to deal
19909
+ * in the Software without restriction, including without limitation the rights
19910
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19911
+ * copies of the Software, and to permit persons to whom the Software is
19912
+ * furnished to do so, subject to the following conditions:
19913
+ *
19914
+ * The above copyright notice and this permission notice shall be included in
19915
+ * all copies or substantial portions of the Software.
19916
+ *
19917
+ *
19918
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19919
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19920
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19921
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19922
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19923
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19924
+ * THE SOFTWARE.
19925
+ */
19926
+
19927
+ /**
19928
+ * Test if the given value is a string or number. Not using typeof as it fails on boxed primitives.
19929
+ *
19930
+ * @param value
19931
+ * @returns boolean
19932
+ */
19933
+
19934
+ function isSimpleType(value) {
19935
+ const simpleTypes = new Set(["boolean", "number", "string", "symbol"]);
19936
+ const valueType = typeof value;
19937
+ return (value !== undefined && (simpleTypes.has(valueType) || value.substring || value.toFixed))
19938
+ }
19939
+
19940
+ function buildOptions(config, options) {
19941
+
19942
+ var defaultOptions = {
19943
+ oauthToken: config.oauthToken,
19944
+ headers: config.headers,
19945
+ withCredentials: config.withCredentials,
19946
+ filename: config.filename
19947
+ };
19948
+
19949
+ return Object.assign(defaultOptions, options)
19950
+ }
19951
+
19952
+ /**
19953
+ * isMobile test from http://detectmobilebrowsers.com
19954
+ * TODO -- improve UI design so this isn't neccessary
19955
+ * @returns {boolean}
19956
+ */
19957
+
19958
+ // igv.isMobile = function () {
19959
+ //
19960
+ // const a = (navigator.userAgent || navigator.vendor || window.opera);
19961
+ // 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) ||
19962
+ // /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)))
19963
+ //
19964
+ // }
19965
+
19966
+ const doAutoscale = function (features) {
19967
+ var min, max;
19968
+
19969
+ if (features.length > 0) {
19970
+ min = Number.MAX_VALUE;
19971
+ max = -Number.MAX_VALUE;
19972
+
19973
+ features.forEach(function (f) {
19974
+ if (!Number.isNaN(f.value)) {
19975
+ min = Math.min(min, f.value);
19976
+ max = Math.max(max, f.value);
19977
+ }
19978
+ });
19979
+
19980
+ // Insure we have a zero baseline
19981
+ if (max > 0) min = Math.min(0, min);
19982
+ if (max < 0) max = 0;
19983
+ } else {
19984
+ // No features -- default
19985
+ min = 0;
19986
+ max = 100;
19987
+ }
19988
+
19989
+ return {min: min, max: max}
19990
+ };
19991
+
19992
+ const validateLocusExtent = function (chromosomeLengthBP, extent, minimumBP) {
19993
+
19994
+ let ss = extent.start;
19995
+ let ee = extent.end;
19996
+
19997
+ if (undefined === ee) {
19998
+
19999
+ ss -= minimumBP / 2;
20000
+ ee = ss + minimumBP;
20001
+
20002
+ if (ee > chromosomeLengthBP) {
20003
+ ee = chromosomeLengthBP;
20004
+ ss = ee - minimumBP;
20005
+ } else if (ss < 0) {
20006
+ ss = 0;
20007
+ ee = minimumBP;
20008
+ }
20009
+
20010
+ } else if (ee - ss < minimumBP) {
20011
+
20012
+ const center = (ee + ss) / 2;
20013
+
20014
+ if (center - minimumBP / 2 < 0) {
20015
+ ss = 0;
20016
+ ee = ss + minimumBP;
20017
+ } else if (center + minimumBP / 2 > chromosomeLengthBP) {
20018
+ ee = chromosomeLengthBP;
20019
+ ss = ee - minimumBP;
20020
+ } else {
20021
+ ss = center - minimumBP / 2;
20022
+ ee = ss + minimumBP;
20023
+ }
20024
+ }
20025
+
20026
+ extent.start = Math.ceil(ss);
20027
+ extent.end = Math.floor(ee);
20028
+ };
20029
+
20030
+ /*!
20031
+ * is-number <https://github.com/jonschlinkert/is-number>
20032
+ *
20033
+ * Copyright (c) 2014-present, Jon Schlinkert.
20034
+ * Released under the MIT License.
20035
+ */
20036
+
20037
+ const isNumber = function (num) {
20038
+ if (typeof num === 'number') {
20039
+ return num - num === 0
20040
+ }
20041
+ if (typeof num === 'string' && num.trim() !== '') {
20042
+ return Number.isFinite ? Number.isFinite(+num) : isFinite(+num)
20043
+ }
20044
+ return false
20045
+ };
20046
+
20047
+ async function getFilename(url) {
20048
+ if (isString$3(url) && url.startsWith("https://drive.google.com")) {
20049
+ // This will fail if Google API key is not defined
20050
+ if (getApiKey() === undefined) {
20051
+ throw Error("Google drive is referenced, but API key is not defined. An API key is required for Google Drive access")
20052
+ }
20053
+ const json = await getDriveFileInfo(url);
20054
+ return json.originalFileName || json.name
20055
+ } else {
20056
+ return getFilename$1(url)
20057
+ }
20058
+ }
20059
+
20060
+ function prettyBasePairNumber(raw) {
20061
+
20062
+ var denom,
20063
+ units,
20064
+ value,
20065
+ floored;
20066
+
20067
+ if (raw > 1e7) {
20068
+ denom = 1e6;
20069
+ units = " mb";
20070
+ } else if (raw > 1e4) {
20071
+
20072
+ denom = 1e3;
20073
+ units = " kb";
20074
+
20075
+ value = raw / denom;
20076
+ floored = Math.floor(value);
20077
+ return numberFormatter$1(floored) + units
20078
+ } else {
20079
+ return numberFormatter$1(raw) + " bp"
20080
+ }
20081
+
20082
+ value = raw / denom;
20083
+ floored = Math.floor(value);
20084
+
20085
+ return floored.toString() + units
20086
+ }
20087
+
20088
+
20089
+ function isDataURL(obj) {
20090
+ return (isString$3(obj) && obj.startsWith("data:"))
20091
+ }
20092
+
20093
+ function createColumn(columnContainer, className) {
20094
+ const column = div$1({class: className});
20095
+ columnContainer.appendChild(column);
20096
+ }
20097
+
20098
+
20099
+ function insertElementBefore(element, referenceNode) {
20100
+ referenceNode.parentNode.insertBefore(element, referenceNode);
20101
+ }
20102
+
20103
+ function insertElementAfter(element, referenceNode) {
20104
+ referenceNode.parentNode.insertBefore(element, referenceNode.nextSibling);
20105
+ }
20106
+
20107
+ /**
20108
+ * Test to see if page is loaded in a secure context, that is by https or is localhost.
20109
+ */
20110
+ function isSecureContext() {
20111
+ return window.location.protocol === "https:" || window.location.hostname === "localhost"
20112
+ }
20113
+
19900
20114
  /*
19901
20115
  * The MIT License (MIT)
19902
20116
  *
@@ -20275,7 +20489,7 @@ class Viewport {
20275
20489
 
20276
20490
  constructor(trackView, viewportColumn, referenceFrame, width) {
20277
20491
 
20278
- this.guid = guid$1();
20492
+ this.guid = guid$2();
20279
20493
  this.trackView = trackView;
20280
20494
  this.referenceFrame = referenceFrame;
20281
20495
 
@@ -22054,7 +22268,7 @@ GenomicInterval.prototype.containsRange = function (range) {
22054
22268
 
22055
22269
  const splitLines$3 = splitLines$5;
22056
22270
 
22057
- const reservedProperties = new Set(['fastaURL', 'indexURL', 'cytobandURL', 'indexed']);
22271
+ const reservedProperties = new Set(['fastaURL', 'indexURL', 'compressedIndexURL', 'cytobandURL', 'indexed']);
22058
22272
 
22059
22273
  class FastaSequence {
22060
22274
 
@@ -22062,6 +22276,7 @@ class FastaSequence {
22062
22276
 
22063
22277
  this.file = reference.fastaURL;
22064
22278
  this.indexFile = reference.indexURL || reference.indexFile || this.file + ".fai";
22279
+ this.compressedIndexFile = reference.compressedIndexURL || false;
22065
22280
  this.withCredentials = reference.withCredentials;
22066
22281
  this.chromosomeNames = [];
22067
22282
  this.chromosomes = {};
@@ -22147,77 +22362,292 @@ class FastaSequence {
22147
22362
  }
22148
22363
  }
22149
22364
 
22150
- async readSequence(chr, qstart, qend) {
22151
22365
 
22152
- // let offset;
22153
- // let start;
22154
- // let end;
22155
- // let basesPerLine;
22156
- // let nEndBytes;
22366
+ //Code is losely based on https://github.com/GMOD/bgzf-filehandle
22367
+ //Reworked however in orde to work with the igvxhr interface for loading files
22368
+ //Additionally, replaced calls to the Long.js interface with standard JS calls for ArrayBuffers and the associated views
22369
+ //
22370
+ //The compressed index is an array of blocks, with each block being a pair: compressed-position & uncompressed-position (both in bytes)
22371
+ async getCompressedIndex() {
22372
+ const GZI_NUM_BYTES_OFFSET = 8;
22373
+ const GZI_NUM_BYTES_BLOCK = 8;
22374
+ if (this.compressedIndex) {
22375
+ return this.compressedIndex
22376
+ }
22377
+ if (!this.compressedIndexFile) {
22378
+ this.compressedIndex = [];
22379
+ return this.compressedIndex
22380
+ }
22381
+ //In contrast to the 'normal' reference (for which the index is chromosome based), this index is block-based
22382
+ //As such there is not need to make it a hash. An array is sufficient.
22383
+ this.compressedIndex = [];
22384
+ const gziData = await igvxhr.loadArrayBuffer(this.compressedIndexFile, buildOptions(this.config));
22385
+ const givenFileSize = gziData.byteLength;
22386
+ if (givenFileSize < GZI_NUM_BYTES_OFFSET) {
22387
+ console.log("Cannot parse GZI index file: length (" + givenFileSize + " bytes) is insufficient to determine content of index.");
22388
+ return this.compressedIndex
22389
+ }
22390
+ //First 8 bytes are a little endian unsigned bigint (64bit), indicating the number of blocks in the index.
22391
+ const numBlocksBuffer = gziData.slice(0, GZI_NUM_BYTES_OFFSET);
22392
+ const numBlocks = Number((new DataView(numBlocksBuffer)).getBigUint64(0, true));
22393
+ //The remainder of the gzi content are pairs of little endian unsigned bigint (64bit) numbers.
22394
+ //The first of the pair is the compressed position of a block
22395
+ //The second of the pair is the uncompressed position of a block
22396
+
22397
+ //Sanity check:
22398
+ //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?
22399
+ //Total file-size should be:
22400
+ // 8 + 2*(num_entries*8) bytes, with the first 8 bytes indicating the number of entries
22401
+ const expectedFileSize = GZI_NUM_BYTES_OFFSET + numBlocks * 2 * GZI_NUM_BYTES_BLOCK;
22402
+ if (givenFileSize != expectedFileSize) {
22403
+ console.log("Incorrect file size of reference genome index. Expected : " + expectedFileSize + ". Received : " + givenFileSize);
22404
+ return this.compressedIndex
22405
+ }
22406
+
22407
+ //Push the first block to the index: the first block always has positions 0 for both the compressed and uncompressed file
22408
+ this.compressedIndex.push([0, 0]);
22409
+
22410
+ //Further process all the blocks of the GZI index, and keep them in memory
22411
+ for (let blockNumber = 0; blockNumber < numBlocks; blockNumber++) {
22412
+ const bufferBlockStart = GZI_NUM_BYTES_OFFSET + blockNumber * 2 * GZI_NUM_BYTES_BLOCK;
22413
+ const bufferBlockEnd = GZI_NUM_BYTES_OFFSET + blockNumber * 2 * GZI_NUM_BYTES_BLOCK + 2 * GZI_NUM_BYTES_BLOCK;
22414
+ const bufferBlock = gziData.slice(bufferBlockStart, bufferBlockEnd);
22415
+ const viewBlock = new DataView(bufferBlock);
22416
+ const compressedPosition = Number(viewBlock.getBigUint64(0, true)); //First 8 bytes
22417
+ const uncompressedPosition = Number(viewBlock.getBigUint64(GZI_NUM_BYTES_BLOCK, true)); //Last 8 bytes
22418
+ this.compressedIndex.push([compressedPosition, uncompressedPosition]);
22419
+ }
22420
+ return this.compressedIndex
22421
+ }
22422
+
22423
+ //The Fasta-index gives a byte-position of the chromosomal sequences within the FASTA file.
22424
+ //These locations need to be remapped to the locations within the zipped reference genome, using the GZI index
22425
+ //This function provides this functionality by
22426
+ //1) taking the indicated start/stop byte locations within the UNCOMPRESSED FASTA file
22427
+ //2) remapping these byte locations to the correct blocks (and associated positions) within the COMPRESSED FASTA file
22428
+ //Subsequently, the calling method can then extract the correct blocks from the compressed FASTA files and uncompressed the data
22429
+ async getRelevantCompressedBlockNumbers(queryPositionStart, queryPositionEnd) {
22430
+ const UNCOMPRESSED_POSITION = 1;
22431
+ //Fallback for impossible values
22432
+ if (queryPositionStart < 0 || queryPositionEnd < 0 || queryPositionEnd < queryPositionStart) {
22433
+ console.log("Incompatible query positions for reference-genome. Start:" + queryPositionStart + " | End:" + queryPositionEnd);
22434
+ return []
22435
+ }
22436
+ //Ensure compressed index is loaded
22437
+ await this.getCompressedIndex();
22438
+ let result = [];
22439
+ //Now search for the correct block-numbers (going from 0 to length(compressed-index)) which overlap with the provided byte-positions
22440
+ const lowestBlockNumber = 0;
22441
+ const highestBlockNumber = this.compressedIndex.length - 1;
22442
+ //Failsafe if for some reason the compressed index wasn't loaded or doesn't contain any data
22443
+ if (this.compressedIndex.length == 0) {
22444
+ console.log("Compressed index does not contain any content");
22445
+ return []
22446
+ }
22447
+ //Failsafe: if the queryPositionStart is greater than the uncompressed-position of the final block,
22448
+ //then this final block is the only possible result
22449
+ if (queryPositionStart > (this.compressedIndex)[highestBlockNumber][UNCOMPRESSED_POSITION]) {
22450
+ return [highestBlockNumber]
22451
+ }
22452
+
22453
+ //Rather than doing a linear search over all blocks, a binary search is done for speed considerations
22454
+ //We are searching for the highest block number for which its position is smaller than the query start position
22455
+ //Afterwards we will simply expand the blocks until the entire query range is covered
22456
+ let searchLow = lowestBlockNumber;
22457
+ let searchHigh = highestBlockNumber;
22458
+ let searchPosition = Math.floor(this.compressedIndex.length / 2);
22459
+ let maxIterations = this.compressedIndex.length + 1;
22460
+ let solutionFound = false;
22461
+ //instead of doing a while(true), this for-loop prevents eternal loops in case of issues
22462
+ for (let iteration = 0; iteration < maxIterations; iteration++) {
22463
+ const searchUncompressedPosition = (this.compressedIndex)[searchPosition][UNCOMPRESSED_POSITION];
22464
+ const nextSearchUncompressedPosition = (searchPosition < (this.compressedIndex.length - 1)) ? (this.compressedIndex)[searchPosition + 1][UNCOMPRESSED_POSITION] : Infinity;
22465
+ //The query position lies within the current search block
22466
+ if (searchUncompressedPosition <= queryPositionStart && nextSearchUncompressedPosition > queryPositionStart) {
22467
+ solutionFound = true;
22468
+ break //searchPosition is the correct block number index
22469
+ }
22470
+ //Current block lies before the query position
22471
+ else if (searchUncompressedPosition < queryPositionStart) {
22472
+ searchLow = searchPosition + 1;
22473
+ }
22474
+ //Current block lies after the query position
22475
+ else {
22476
+ searchHigh = searchPosition - 1;
22477
+ }
22478
+ searchPosition = Math.ceil((searchHigh - searchLow) / 2) + searchLow;
22479
+ }
22480
+ //If for some reason the binary search did not reveal a correct block index, then we return the empty result
22481
+ if (!solutionFound) {
22482
+ console.log("No blocks within compressed index found that correspond with query positions " + queryPositionStart + "," + queryPositionEnd);
22483
+ console.log(this.compressedIndex);
22484
+ return []
22485
+ }
22486
+
22487
+ //Now extend the result by adding additional blocks until the entire query range is covered
22488
+ result.push(searchPosition);
22489
+ for (let blockIndex = searchPosition + 1; blockIndex < this.compressedIndex.length; blockIndex++) {
22490
+ result.push(blockIndex);
22491
+ const blockUncompressedPosition = (this.compressedIndex)[blockIndex][UNCOMPRESSED_POSITION];
22492
+ if (blockUncompressedPosition >= queryPositionEnd) {
22493
+ break
22494
+ }
22495
+ }
22496
+
22497
+ //It is possible that the query end position lies AFTER the start of the final block
22498
+ //If this is the case, we add a 'fake' negative index which will be interpreted by the loadAndUncompressBlocks method as an indicator
22499
+ //to read until the end of the file
22500
+ const finalRelevantBlock = result[result.length - 1];
22501
+ const finalIndexBlock = this.compressedIndex.length - 1;
22502
+ if (finalRelevantBlock === finalIndexBlock && (this.compressedIndex)[finalRelevantBlock][UNCOMPRESSED_POSITION] < queryPositionEnd) {
22503
+ result.push(-1);
22504
+ }
22505
+
22506
+ return result
22507
+ }
22508
+
22509
+
22510
+ //Load the content from the blockIndices.
22511
+ //This is done on a per-block basis
22512
+ //Content of the first block will be trimmed in order to match the expected offset
22513
+ async loadAndUncompressBlocks(blockIndices, startByte) {
22514
+ const COMPRESSED_POSITION = 0;
22515
+ const UNCOMPRESSED_POSITION = 1;
22516
+ //Normally the compressed index should already exist, we're just makeing sure here
22517
+ await this.getCompressedIndex();
22518
+
22519
+ if (blockIndices.length == 0) {
22520
+ return ""
22521
+ }
22522
+
22523
+ //Storing data in seperate array with indices in order to assert order due to async behaviour of loops
22524
+ let resultCache = Array(blockIndices.length - 1);
22525
+ for (let i = 0; i < blockIndices.length - 1; i++) {
22526
+ const currentBlockNumber = blockIndices[i];
22527
+ const currentBlockInfo = (this.compressedIndex)[currentBlockNumber];
22528
+ const currentBlockCompressedPosition = currentBlockInfo[COMPRESSED_POSITION];
22529
+
22530
+ const nextBlockNumber = blockIndices[i + 1];
22531
+ let compressedBytes = [];
22532
+ if (nextBlockNumber != -1) { //default : read current entire block only
22533
+ const nextBlockInfo = (this.compressedIndex)[nextBlockNumber];
22534
+ const nextBlockCompressedPosition = nextBlockInfo[COMPRESSED_POSITION];
22535
+ const compressedLength = nextBlockCompressedPosition - currentBlockCompressedPosition;
22536
+ compressedBytes = await igvxhr.loadArrayBuffer(this.file, buildOptions(this.config, {
22537
+ range: {
22538
+ start: currentBlockCompressedPosition,
22539
+ size: compressedLength
22540
+ }
22541
+ }));
22542
+ } else { // special case for query within final block: read until the end of the file
22543
+ compressedBytes = await igvxhr.loadArrayBuffer(this.file, buildOptions(this.config, {
22544
+ range: {
22545
+ start: currentBlockCompressedPosition
22546
+ }
22547
+ }));
22548
+ }
22549
+ //now unzip the compressed bytes, and store them in the resultCache
22550
+ const uncompressedBytes = await unbgzf(compressedBytes);
22551
+ resultCache[i] = uncompressedBytes;
22552
+ }
22553
+
22554
+ //Iterate over the result cache, create sequences from the data, and create a full sequence string from the data
22555
+ let result = "";
22556
+ for (let i = 0; i < resultCache.length; i++) {
22557
+ for (let j = 0; j < resultCache[i].length; j++) {
22558
+ const c = String.fromCharCode(resultCache[i][j]);
22559
+ result = result + c;
22560
+ }
22561
+ }
22562
+
22563
+ //postprocess this data: because entire blocks are read we need to remove the first N bases of the first used block,
22564
+ //which are not included in the original query positions
22565
+ const firstBlockInfo = (this.compressedIndex)[blockIndices[0]];
22566
+ const offset = startByte - firstBlockInfo[UNCOMPRESSED_POSITION];
22567
+ result = result.substring(offset);
22568
+
22569
+ return result
22570
+ }
22571
+
22572
+
22573
+ async readSequence(chr, qstart, qend) {
22157
22574
 
22158
22575
  await this.getIndex();
22576
+ await this.getCompressedIndex(); //This will work even if no compressed index file is set
22159
22577
 
22160
22578
  const idxEntry = this.index[chr];
22161
22579
  if (!idxEntry) {
22162
22580
  console.log("No index entry for chr: " + chr);
22163
-
22164
22581
  // Tag interval with null so we don't try again
22165
22582
  this.interval = new GenomicInterval(chr, qstart, qend, null);
22166
22583
  return null
22584
+ }
22167
22585
 
22168
- } else {
22586
+ const start = Math.max(0, qstart); // qstart should never be < 0
22587
+ const end = Math.min(idxEntry.size, qend);
22588
+ const bytesPerLine = idxEntry.bytesPerLine;
22589
+ const basesPerLine = idxEntry.basesPerLine;
22590
+ const position = idxEntry.position;
22591
+ const nEndBytes = bytesPerLine - basesPerLine;
22592
+ const startLine = Math.floor(start / basesPerLine);
22593
+ const endLine = Math.floor(end / basesPerLine);
22594
+ const base0 = startLine * basesPerLine; // Base at beginning of start line
22595
+ const offset = start - base0;
22596
+ const startByte = position + startLine * bytesPerLine + offset;
22597
+ const base1 = endLine * basesPerLine;
22598
+ const offset1 = end - base1;
22599
+ const endByte = position + endLine * bytesPerLine + offset1 - 1;
22600
+ const byteCount = endByte - startByte + 1;
22601
+
22602
+ if (byteCount <= 0) {
22603
+ console.error("No sequence for " + chr + ":" + qstart + "-" + qend);
22604
+ return null
22605
+ }
22169
22606
 
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
- }));
22607
+ //If the compressed index file is set, then we are dealing with a compressed genome sequence
22608
+ //The selection of startByte/endByte is done for the non-compressed genome sequence.
22609
+ //These need to be 'converted' to the correct byte positions in the compressed genome sequence,
22610
+ //by making use of the compressed index (GZI file)
22611
+ let allBytes;
22612
+ if (!this.compressedIndexFile) {
22613
+ allBytes = await igvxhr.load(this.file, buildOptions(this.config, {
22614
+ range: {
22615
+ start: startByte,
22616
+ size: byteCount
22617
+ }
22618
+ }));
22619
+ } else {
22620
+ let relevantBlockIndices = await this.getRelevantCompressedBlockNumbers(startByte, endByte);
22621
+ if (relevantBlockIndices.length === 0) {
22622
+ console.log("No blocks in the compressed index that correspond with the requested byte positions (" + startByte + "," + endByte + ")");
22623
+ return null
22196
22624
  }
22625
+ allBytes = await this.loadAndUncompressBlocks(relevantBlockIndices, startByte);
22626
+ }
22197
22627
 
22198
- if (!allBytes) {
22199
- return null
22200
- } else {
22201
- let nBases,
22202
- seqBytes = "",
22203
- srcPos = 0,
22204
- allBytesLength = allBytes.length;
22628
+ if (!allBytes) {
22629
+ return null
22630
+ }
22205
22631
 
22206
- if (offset > 0) {
22207
- nBases = Math.min(end - start, basesPerLine - offset);
22208
- seqBytes += allBytes.substr(srcPos, nBases);
22209
- srcPos += (nBases + nEndBytes);
22210
- }
22632
+ let nBases,
22633
+ seqBytes = "",
22634
+ srcPos = 0,
22635
+ allBytesLength = allBytes.length;
22211
22636
 
22212
- while (srcPos < allBytesLength) {
22213
- nBases = Math.min(basesPerLine, allBytesLength - srcPos);
22214
- seqBytes += allBytes.substr(srcPos, nBases);
22215
- srcPos += (nBases + nEndBytes);
22216
- }
22637
+ if (offset > 0) {
22638
+ nBases = Math.min(end - start, basesPerLine - offset);
22639
+ seqBytes += allBytes.substr(srcPos, nBases);
22640
+ srcPos += (nBases + nEndBytes);
22641
+ }
22217
22642
 
22218
- return seqBytes
22219
- }
22643
+ while (srcPos < allBytesLength) {
22644
+ nBases = Math.min(basesPerLine, allBytesLength - srcPos);
22645
+ seqBytes += allBytes.substr(srcPos, nBases);
22646
+ srcPos += (nBases + nEndBytes);
22220
22647
  }
22648
+
22649
+ return seqBytes
22650
+
22221
22651
  }
22222
22652
  }
22223
22653
 
@@ -22329,7 +22759,7 @@ const Cytoband = function (start, end, name, typestain) {
22329
22759
  }
22330
22760
  };
22331
22761
 
22332
- const _version = "2.10.5";
22762
+ const _version = "2.11.0";
22333
22763
  function version() {
22334
22764
  return _version
22335
22765
  }
@@ -22856,7 +23286,9 @@ class TrackViewport extends Viewport {
22856
23286
  }
22857
23287
 
22858
23288
  stopSpinner() {
22859
- this.$spinner.hide();
23289
+ if (this.$spinner) {
23290
+ this.$spinner.hide();
23291
+ }
22860
23292
  }
22861
23293
 
22862
23294
  checkZoomIn() {
@@ -23183,7 +23615,7 @@ class TrackViewport extends Viewport {
23183
23615
  const str = (this.trackView.track.name || this.trackView.track.id).replace(/\W/g, '');
23184
23616
 
23185
23617
  const index = this.browser.referenceFrameList.indexOf(this.referenceFrame);
23186
- const id = `${str}_referenceFrame_${index}_guid_${guid$1()}`;
23618
+ const id = `${str}_referenceFrame_${index}_guid_${guid$2()}`;
23187
23619
 
23188
23620
  this.drawSVGWithContext(context, width, height, id, 0, 0, 0);
23189
23621
 
@@ -23206,7 +23638,7 @@ class TrackViewport extends Viewport {
23206
23638
  const str = (this.trackView.track.name || this.trackView.track.id).replace(/\W/g, '');
23207
23639
 
23208
23640
  const index = this.browser.referenceFrameList.indexOf(this.referenceFrame);
23209
- const id = `${str}_referenceFrame_${index}_guid_${guid$1()}`;
23641
+ const id = `${str}_referenceFrame_${index}_guid_${guid$2()}`;
23210
23642
 
23211
23643
  const {top: yScrollDelta} = this.$content.position();
23212
23644
 
@@ -23225,7 +23657,7 @@ class TrackViewport extends Viewport {
23225
23657
  renderTrackLabelSVG(context, tx, ty, width, height) {
23226
23658
 
23227
23659
  const str = (this.trackView.track.name || this.trackView.track.id).replace(/\W/g, '');
23228
- const id = `${str}_track_label_guid_${guid$1()}`;
23660
+ const id = `${str}_track_label_guid_${guid$2()}`;
23229
23661
 
23230
23662
  context.saveWithTranslationAndClipRect(id, tx, ty, width, height, 0);
23231
23663
 
@@ -23552,28 +23984,16 @@ class TrackViewport extends Viewport {
23552
23984
  str = track.description();
23553
23985
  } else if (track.description) {
23554
23986
  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
23987
  }
23567
23988
 
23568
- if (this.popover) {
23569
- this.popover.dispose();
23989
+ if (str) {
23990
+ if (this.popover) {
23991
+ this.popover.dispose();
23992
+ }
23993
+ this.popover = new Popover(this.browser.columnContainer, (track.name || ''));
23994
+ this.popover.presentContentWithEvent(event, str);
23570
23995
  }
23571
-
23572
- this.popover = new Popover(this.browser.columnContainer, (track.name || 'unnamed'));
23573
-
23574
- this.popover.presentContentWithEvent(event, str);
23575
23996
  }
23576
-
23577
23997
  }
23578
23998
 
23579
23999
  removeTrackLabelClickHandler(trackLabel) {
@@ -28743,7 +29163,7 @@ class TrackBase {
28743
29163
  this.config = config;
28744
29164
  this.url = config.url;
28745
29165
  this.type = config.type;
28746
- this.description = config.description;
29166
+
28747
29167
  this.supportHiDPI = config.supportHiDPI === undefined ? true : config.supportHiDPI;
28748
29168
 
28749
29169
  if (config.name || config.label) {
@@ -28781,6 +29201,15 @@ class TrackBase {
28781
29201
  this.onclick = config.onclick;
28782
29202
  config.onclick = undefined; // functions cannot be saved in sessions, clear it here.
28783
29203
  }
29204
+
29205
+ if (config.description) {
29206
+ // Override description -- displayed when clicking on track label. Convert to function if neccessary
29207
+ if (typeof config.description === 'function') {
29208
+ this.description = config.description;
29209
+ } else {
29210
+ this.description = () => config.description;
29211
+ }
29212
+ }
28784
29213
  }
28785
29214
 
28786
29215
  get name() {
@@ -28823,7 +29252,7 @@ class TrackBase {
28823
29252
  for (let key of Object.keys(state)) {
28824
29253
  if (key.startsWith("_")) continue // transient property
28825
29254
  const value = this[key];
28826
- if (value && (isSimpleType(value) || typeof value === "boolean")) {
29255
+ if (value && (isSimpleType(value) || typeof value === "boolean" || key === "metadata")) {
28827
29256
  state[key] = value;
28828
29257
  }
28829
29258
  }
@@ -28837,16 +29266,18 @@ class TrackBase {
28837
29266
  state.max = this.dataRange.max;
28838
29267
  }
28839
29268
 
29269
+
28840
29270
  // Check for non-json-if-yable properties. Perhaps we should test what can be saved.
28841
29271
  for (let key of Object.keys(state)) {
28842
- if (typeof state[key] === 'function') {
29272
+ const value = state[key];
29273
+ if (typeof value === 'function') {
28843
29274
  throw Error(`Property '${key}' of track '${this.name} is a function. Functions cannot be saved in sessions.`)
28844
29275
  }
28845
- if (isFile(state[key])) {
29276
+ if (value instanceof File) { // Test specifically for File. Other types of File-like objects might be savable
28846
29277
  const str = `Track ${this.name} is a local file. Sessions cannot be saved with local file references.`;
28847
29278
  throw Error(str)
28848
29279
  }
28849
- if (state[key] instanceof Promise) {
29280
+ if (value instanceof Promise) {
28850
29281
  throw Error(`Property '${key}' of track '${this.name} is a Promise. Promises cannot be saved in sessions.`)
28851
29282
  }
28852
29283
  }
@@ -29092,6 +29523,49 @@ class TrackBase {
29092
29523
 
29093
29524
  }
29094
29525
 
29526
+ /**
29527
+ * Default track description -- displayed on click of track label. This can be overriden in the track
29528
+ * configuration, or in subclasses.
29529
+ */
29530
+ description() {
29531
+
29532
+ const wrapKeyValue = (k, v) => `<div class="igv-track-label-popup-shim"><b>${k}: </b>${v}</div>`;
29533
+
29534
+ let str = '<div class="igv-track-label-popup">';
29535
+ if (this.url) {
29536
+ if (isFile(this.url)) {
29537
+ str += wrapKeyValue('Filename', this.url.name);
29538
+ } else {
29539
+ str += wrapKeyValue('URL', this.url);
29540
+ }
29541
+ } else {
29542
+ str = this.name;
29543
+ }
29544
+ if (this.config) {
29545
+ if (this.config.metadata) {
29546
+ for (let key of Object.keys(this.config.metadata)) {
29547
+ const value = this.config.metadata[key];
29548
+ str += wrapKeyValue(key, value);
29549
+ }
29550
+ }
29551
+
29552
+ // Add any config properties that are capitalized
29553
+ for (let key of Object.keys(this.config)) {
29554
+ if (key.startsWith("_")) continue // transient property
29555
+ let first = key.substr(0, 1);
29556
+ if (first !== first.toLowerCase()) {
29557
+ const value = this.config[key];
29558
+ if (value && isSimpleType(value)) {
29559
+ str += wrapKeyValue(key, value);
29560
+ }
29561
+ }
29562
+ }
29563
+
29564
+ }
29565
+ str += '</div>';
29566
+ return str
29567
+ }
29568
+
29095
29569
  static getCravatLink(chr, position, ref, alt, genomeID) {
29096
29570
 
29097
29571
  if ("hg38" === genomeID || "GRCh38" === genomeID) {
@@ -29138,6 +29612,8 @@ function paintAxis(ctx, pixelWidth, pixelHeight) {
29138
29612
  return
29139
29613
  }
29140
29614
 
29615
+ let flipAxis = (undefined === this.flipAxis) ? false : this.flipAxis;
29616
+
29141
29617
  IGVGraphics.fillRect(ctx, 0, 0, pixelWidth, pixelHeight, {'fillStyle': "rgb(255, 255, 255)"});
29142
29618
 
29143
29619
  reference = 0.95 * pixelWidth;
@@ -29152,7 +29628,7 @@ function paintAxis(ctx, pixelWidth, pixelHeight) {
29152
29628
 
29153
29629
  // tick
29154
29630
  IGVGraphics.strokeLine(ctx, x1, y1, x2, y2, font);
29155
- IGVGraphics.fillText(ctx, prettyPrint(this.dataRange.max), x1 + 4, y1 + 12, font);
29631
+ IGVGraphics.fillText(ctx, prettyPrint(flipAxis ? this.dataRange.min : this.dataRange.max), x1 + 4, y1 + 12, font);
29156
29632
 
29157
29633
  //shim = 0.25 * 0.125;
29158
29634
  y1 = y2 = (1.0 - shim) * pixelHeight;
@@ -29161,7 +29637,7 @@ function paintAxis(ctx, pixelWidth, pixelHeight) {
29161
29637
 
29162
29638
  // tick
29163
29639
  IGVGraphics.strokeLine(ctx, x1, y1, x2, y2, font);
29164
- IGVGraphics.fillText(ctx, prettyPrint(this.dataRange.min), x1 + 4, y1 - 4, font);
29640
+ IGVGraphics.fillText(ctx, prettyPrint(flipAxis ? this.dataRange.max : this.dataRange.min), x1 + 4, y1 - 4, font);
29165
29641
 
29166
29642
  IGVGraphics.strokeLine(ctx, a.x, a.y, b.x, b.y, font);
29167
29643
 
@@ -30566,6 +31042,75 @@ const chrColorMap$1 = {
30566
31042
  "chr48": "rgb(20, 255, 177)",
30567
31043
  };
30568
31044
 
31045
+ class ChordSetManager {
31046
+
31047
+ constructor(config) {
31048
+ this.tracks = [];
31049
+ this.chordSets = [];
31050
+ }
31051
+
31052
+ addChordSet(chordSet) {
31053
+
31054
+ // If a chord set with this name exists replace it (same track, same region)
31055
+ this.chordSets = this.chordSets.filter(g => g.name !== chordSet.name);
31056
+ this.chordSets.push(chordSet);
31057
+
31058
+ let track = this.tracks.find(t => chordSet.trackName === t.name);
31059
+ if (track) {
31060
+ track.chordSets = track.chordSets.filter(cs => cs.name !== chordSet.name);
31061
+ track.chordSets.push(chordSet);
31062
+ }
31063
+ if (!track) {
31064
+ track = new IGVTrack(chordSet);
31065
+ this.tracks.push(track);
31066
+ }
31067
+ }
31068
+
31069
+ clearChords() {
31070
+ this.tracks = [];
31071
+ this.chordSets = [];
31072
+ }
31073
+
31074
+ getTrack(name) {
31075
+ return this.tracks.find(t => name === t.name)
31076
+ }
31077
+
31078
+ getChordset(name) {
31079
+ return this.chordSets.find(cs => name === cs.name)
31080
+ }
31081
+
31082
+ }
31083
+
31084
+ class IGVTrack {
31085
+ constructor(chordSet) {
31086
+ this.name = chordSet.trackName;
31087
+ this.color = chordSet.trackColor;
31088
+ this.visible = true;
31089
+ this.chordSets = [chordSet];
31090
+ this.id = guid$1();
31091
+ }
31092
+
31093
+ get chords() {
31094
+ if (this.chordSets.length === 1) {
31095
+ return this.chordSets[0].chords
31096
+ }
31097
+ const chords = [];
31098
+ for (let cs of this.chordSets) {
31099
+ for (let c of cs.chords) {
31100
+ chords.push(c);
31101
+ }
31102
+ }
31103
+ return chords
31104
+ }
31105
+ }
31106
+
31107
+
31108
+ function guid$1() {
31109
+ return ("0000" + (Math.random() * Math.pow(36, 4) << 0).toString(36)).slice(-4)
31110
+ }
31111
+
31112
+ const EXP5 = Math.exp(5);
31113
+
30569
31114
  class CircularView {
30570
31115
 
30571
31116
  static isInstalled() {
@@ -30590,6 +31135,8 @@ class CircularView {
30590
31135
  if (CircularView.isInstalled()) {
30591
31136
 
30592
31137
  this.parent = parent;
31138
+ this.groupByTrack = config.groupByTrack === true;
31139
+ this.chordManager = new ChordSetManager(config);
30593
31140
 
30594
31141
  // wrapper for toolbar and circular-view container
30595
31142
  const wrapper = document.createElement('div');
@@ -30597,7 +31144,8 @@ class CircularView {
30597
31144
  parent.appendChild(wrapper);
30598
31145
 
30599
31146
  // toolbar
30600
- this.createToolbarAndTrackPanel(wrapper);
31147
+ this.createControls(wrapper);
31148
+ this.resetControlPanel();
30601
31149
 
30602
31150
  // circular view container
30603
31151
  const element = document.createElement('div');
@@ -30609,8 +31157,6 @@ class CircularView {
30609
31157
  this.setAssembly(config.assembly);
30610
31158
  }
30611
31159
 
30612
- this.tracks = config.tracks || [];
30613
-
30614
31160
  this.width = config.width || 500;
30615
31161
  this.height = config.height || 500;
30616
31162
  this.setSize(this.width, this.height);
@@ -30620,55 +31166,47 @@ class CircularView {
30620
31166
  }
30621
31167
  }
30622
31168
 
30623
- createToolbarAndTrackPanel(parent) {
30624
-
30625
- let element;
31169
+ createControls(parent) {
30626
31170
 
30627
31171
  // toolbar
30628
- element = document.createElement('div');
30629
- element.className = 'igv-circview-toolbar';
30630
- parent.appendChild(element);
30631
- this.toolbar = element;
31172
+ const toolbarDiv = document.createElement('div');
31173
+ toolbarDiv.className = 'igv-circview-toolbar';
31174
+ parent.appendChild(toolbarDiv);
31175
+ this.toolbar = toolbarDiv;
30632
31176
 
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';
31177
+ // control panel
31178
+ const controlPanelDiv = document.createElement('div');
31179
+ controlPanelDiv.className = 'igv-circview-track-panel';
31180
+ parent.appendChild(controlPanelDiv);
31181
+ this.controlPanel = controlPanelDiv;
31182
+ this.controlPanel.style.display = 'none';
30641
31183
 
30642
31184
 
30643
31185
  // toolbar button container - Track Options - Clear All
30644
- let buttonContainer = document.createElement('div');
31186
+ const buttonContainer = document.createElement('div');
30645
31187
  buttonContainer.className = 'igv-circview-toolbar-button-container';
30646
31188
  this.toolbar.appendChild(buttonContainer);
30647
31189
 
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
-
31190
+ // Show Controls
31191
+ this.showControlsButton = document.createElement('div');
31192
+ this.showControlsButton.className = 'igv-circview-button';
31193
+ buttonContainer.appendChild(this.showControlsButton);
31194
+ this.showControlsButton.innerText = 'none' === this.controlPanel.style.display ? 'Show Controls' : 'Hide Controls';
31195
+ this.showControlsButton.addEventListener('click', (event) => {
31196
+ const trackPanelRows = this.controlPanel.querySelectorAll('div');
30657
31197
  if (trackPanelRows.length > 0) {
30658
31198
 
30659
- if ('none' === this.trackPanel.style.display) {
30660
- this.trackPanel.style.display = 'flex';
30661
- event.target.innerText = 'Hide Track Options';
31199
+ if ('none' === this.controlPanel.style.display) {
31200
+ this.controlPanel.style.display = 'flex';
31201
+ event.target.innerText = 'Hide Controls';
30662
31202
  } else {
30663
- this.trackPanel.style.display = 'none';
30664
- event.target.innerText = 'Show Track Options';
31203
+ this.controlPanel.style.display = 'none';
31204
+ event.target.innerText = 'Show Controls';
30665
31205
  }
30666
-
30667
31206
  }
30668
-
30669
31207
  });
30670
31208
 
30671
- // Clear All Chords
31209
+ // Clear All
30672
31210
  let button = document.createElement('div');
30673
31211
  button.className = 'igv-circview-button';
30674
31212
  buttonContainer.appendChild(button);
@@ -30677,14 +31215,8 @@ class CircularView {
30677
31215
  this.clearChords();
30678
31216
  });
30679
31217
 
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) {
31218
+ // Close
31219
+ if (false !== this.config.showCloseButton) {
30688
31220
  button = document.createElement('div');
30689
31221
  button.className = 'igv-circview-button';
30690
31222
  buttonContainer.appendChild(button);
@@ -30693,60 +31225,115 @@ class CircularView {
30693
31225
  this.visible = false;
30694
31226
  });
30695
31227
  }
31228
+ }
30696
31229
 
31230
+ resetControlPanel() {
31231
+ this.controlPanel.innerHTML = '';
31232
+ this.controlPanel.appendChild(this.createGroupByCB());
31233
+ const chordSets = this.groupByTrack ? this.chordManager.tracks : this.chordManager.chordSets;
31234
+ for(let cs of chordSets) {
31235
+ this.addToControlPanel(cs);
31236
+ }
30697
31237
  }
30698
31238
 
30699
- addToTrackPanel(track) {
31239
+ createGroupByCB() {
31240
+ const groupByCB = document.createElement('input');
31241
+ groupByCB.type = 'checkbox';
31242
+ groupByCB.id = 'groupByCB';
31243
+ groupByCB.style.width = '1.4em';
31244
+ groupByCB.style.height = '1.4em';
31245
+ groupByCB.checked = this.groupByTrack;
30700
31246
 
30701
- // single track row - container for hide-button | color-picker-swatch | track-name
31247
+ groupByCB.onclick = (evt) => {
31248
+ this.groupByTrack = evt.target.checked;
31249
+ this.resetControlPanel();
31250
+ this.render();
31251
+ };
31252
+
31253
+ const groupByLabel = document.createElement('label');
31254
+ groupByLabel.for = 'groupByCB';
31255
+ groupByLabel.innerText = 'Group by track';
31256
+ groupByLabel.style.color = 'black';
31257
+ groupByLabel.style.paddingLeft = '10px';
30702
31258
  const trackPanelRow = document.createElement('div');
30703
- this.trackPanel.appendChild(trackPanelRow);
31259
+ trackPanelRow.style.width = '100%';
31260
+ trackPanelRow.style.paddingTop = '5px';
31261
+ trackPanelRow.style.paddingBottom = '5px';
31262
+ trackPanelRow.style.background = 'rgb(216, 230, 234)';
31263
+ trackPanelRow.appendChild(groupByCB);
31264
+ trackPanelRow.appendChild(groupByLabel);
31265
+ return trackPanelRow
31266
+ }
30704
31267
 
31268
+ addToControlPanel(chordSet) {
31269
+
31270
+ // single track row - container for hide-button | color-picker-swatch | track-name
31271
+ const row = document.createElement('div');
31272
+ this.controlPanel.appendChild(row);
30705
31273
 
30706
- let element;
30707
31274
 
30708
31275
  // 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);
31276
+ const hideShowButton = document.createElement('div');
31277
+ hideShowButton.className = 'igv-circview-button';
31278
+ row.appendChild(hideShowButton);
31279
+ hideShowButton.innerText = true === chordSet.visible ? 'Hide' : 'Show';
31280
+ hideShowButton.addEventListener('click', event => {
31281
+ if (true === chordSet.visible) {
31282
+ this.hideTrack(chordSet.name);
30716
31283
  event.target.innerText = "Show";
30717
31284
  } else {
30718
- this.showTrack(track.id);
31285
+ this.showTrack(chordSet.name);
30719
31286
  event.target.innerText = "Hide";
30720
31287
  }
30721
-
30722
31288
  });
30723
31289
 
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);
31290
+ // The alpha range slider. Create this here so we can reference it from the color picker
31291
+ const alphaSlider = document.createElement('input');
31292
+ const valueToAlpha = (value) => Math.exp(value / 200) / EXP5;
31293
+ const alphaToValue = (alpha) => 200 * Math.log(alpha * EXP5);
31294
+
31295
+ // color
31296
+ const colorPickerButton = document.createElement('div');
31297
+ colorPickerButton.className = 'igv-circview-button';
31298
+ colorPickerButton.innerHTML = '&nbsp;&nbsp;&nbsp;&nbsp;'; // <- important for button to size properly
31299
+ row.appendChild(colorPickerButton);
31300
+ colorPickerButton.style.backgroundColor = setAlpha(chordSet.color, 1);
30731
31301
  const pickerConfig =
30732
31302
  {
30733
- parent: pickerButton,
31303
+ parent: colorPickerButton,
30734
31304
  popup: 'right',
30735
31305
  editorFormat: 'rgb',
30736
- color: track.color,
31306
+ color: chordSet.color,
30737
31307
  onChange: ({rgbaString}) => {
30738
- pickerButton.style.backgroundColor = setAlpha(rgbaString, 1);
30739
- this.setTrackColor(track.id, rgbaString);
31308
+ colorPickerButton.style.backgroundColor = setAlpha(rgbaString, 1);
31309
+ this.setTrackColor(chordSet.name, rgbaString);
31310
+ alphaSlider.value = alphaToValue(getAlpha(chordSet.color));
30740
31311
  }
30741
31312
  };
30742
-
30743
- new Picker(pickerConfig);
31313
+ const picker = new Picker(pickerConfig);
31314
+
31315
+ // alpha transparency
31316
+ alphaSlider.setAttribute('title', 'Adjust transparency of arcs');
31317
+ alphaSlider.type = 'range';
31318
+ //alphaSlider.className = 'igv-circview-alpha-slider'
31319
+ alphaSlider.style.width = '100px';
31320
+ alphaSlider.style.marginRight = '10px';
31321
+ alphaSlider.setAttribute('class', 'range');
31322
+ alphaSlider.setAttribute('min', '0');
31323
+ alphaSlider.setAttribute('max', '1000');
31324
+ alphaSlider.value = alphaToValue(getAlpha(chordSet.color));
31325
+ alphaSlider.oninput = () => {
31326
+ const v = valueToAlpha(alphaSlider.value);
31327
+ this.setTrackColor(chordSet.name, setAlpha(chordSet.color, v));
31328
+ picker.setColor(chordSet.color);
31329
+ };
31330
+ row.appendChild(alphaSlider);
30744
31331
 
30745
31332
  // track name
30746
- element = document.createElement('div');
30747
- element.style.color = 'black';
30748
- trackPanelRow.appendChild(element);
30749
- element.innerText = element.title = track.name;
31333
+ const trackNameDive = document.createElement('div');
31334
+ trackNameDive.style.color = 'black';
31335
+ row.appendChild(trackNameDive);
31336
+ trackNameDive.innerText = trackNameDive.title = chordSet.name;
30750
31337
 
30751
31338
  }
30752
31339
 
@@ -30760,7 +31347,7 @@ class CircularView {
30760
31347
  if (this.genomeId === igvGenome.id) {
30761
31348
  return
30762
31349
  }
30763
- this.tracks = [];
31350
+ this.chordManager.clearChords();
30764
31351
  this.genomeId = igvGenome.id;
30765
31352
  this.chrNames = new Set(igvGenome.chromosomes.map(chr => shortChrName$1(chr.name)));
30766
31353
 
@@ -30823,39 +31410,24 @@ class CircularView {
30823
31410
 
30824
31411
  addChords(newChords, options = {}) {
30825
31412
 
30826
- const name = options.track || options.name || "*";
31413
+ const tmp = options.track || options.name || "*";
31414
+ const trackName = tmp.split(' ')[0].replaceAll("%20", " ");
31415
+ const chordSetName = tmp.replaceAll("%20", " ");
31416
+
31417
+ const chordSet = {
31418
+ name: chordSetName,
31419
+ trackName: trackName,
31420
+ chords: newChords,
31421
+ color: options.color || "black",
31422
+ trackColor: options.trackColor || options.color || "black",
31423
+ visible: true,
31424
+ id: options.id || guid()
31425
+ };
30827
31426
 
30828
- let track = this.tracks.find(t => name === t.name);
31427
+ this.chordManager.addChordSet(chordSet);
30829
31428
 
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
- }
31429
+ this.resetControlPanel();
30848
31430
 
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
31431
  this.render();
30860
31432
  }
30861
31433
 
@@ -30881,8 +31453,9 @@ class CircularView {
30881
31453
  }
30882
31454
 
30883
31455
  clearChords() {
30884
- this.tracks = [];
30885
- this.trackPanel.innerHTML = '';
31456
+ //this.tracks = []
31457
+ this.chordManager.clearChords();
31458
+ this.resetControlPanel();
30886
31459
  this.render();
30887
31460
  }
30888
31461
 
@@ -30927,8 +31500,8 @@ class CircularView {
30927
31500
  this.parent.style.display = isVisible ? 'block' : 'none';
30928
31501
  }
30929
31502
 
30930
- hideTrack(trackID) {
30931
- let track = this.tracks.find(t => trackID === t.id);
31503
+ hideTrack(trackName) {
31504
+ let track = this.getTrack(trackName);
30932
31505
  if (track) {
30933
31506
  track.visible = false;
30934
31507
  this.render();
@@ -30937,34 +31510,44 @@ class CircularView {
30937
31510
  }
30938
31511
  }
30939
31512
 
30940
- showTrack(trackID) {
30941
- let idx = this.tracks.findIndex(t => trackID === t.id);
30942
- if (idx >= 0) {
30943
- const track = this.tracks[idx];
31513
+ showTrack(trackName) {
31514
+ let track = this.getTrack(trackName);
31515
+ if (track) {
30944
31516
  track.visible = true;
30945
- this.tracks.splice(idx, 1); // Change z-order
30946
- this.tracks.push(track);
30947
31517
  this.render();
30948
31518
  } else {
30949
- console.warn(`No track with name: ${name}`);
31519
+ console.warn(`No track with name: ${trackName}`);
30950
31520
  }
30951
31521
  }
30952
31522
 
31523
+ // showTrack(trackID) {
31524
+ // let idx = this.tracks.findIndex(t => trackID === t.id)
31525
+ // if (idx >= 0) {
31526
+ // const track = this.tracks[idx]
31527
+ // track.visible = true
31528
+ // this.tracks.splice(idx, 1) // Change z-order
31529
+ // this.tracks.push(track)
31530
+ // this.render()
31531
+ // } else {
31532
+ // console.warn(`No track with name: ${name}`)
31533
+ // }
31534
+ // }
31535
+
30953
31536
  // TODO -- remove corresponding row from track panel
30954
31537
  deleteTrack(trackID) {
30955
- let idx = this.tracks.findIndex(t => trackID === t.id);
31538
+ let idx = this.tracks.findIndex(t => trackID === t.name);
30956
31539
  if (idx >= 0) {
30957
31540
  this.tracks.splice(idx, 1);
30958
31541
  }
30959
31542
  this.render();
30960
31543
  }
30961
31544
 
30962
- getTrack(trackID) {
30963
- return this.tracks.find(t => trackID === t.id)
31545
+ getTrack(name) {
31546
+ return this.groupByTrack ? this.chordManager.getTrack(name) : this.chordManager.getChordset(name)
30964
31547
  }
30965
31548
 
30966
- setTrackColor(trackID, color) {
30967
- const t = this.getTrack(trackID);
31549
+ setTrackColor(name, color) {
31550
+ const t = this.getTrack(name);
30968
31551
  if (t) {
30969
31552
  t.color = color;
30970
31553
  const trackID = t.id;
@@ -30991,24 +31574,27 @@ class CircularView {
30991
31574
  // Remove all children from possible previous renders. React might do this for us when we render, but just in case.
30992
31575
  ReactDOM.unmountComponentAtNode(this.container);
30993
31576
 
30994
- const visibleTracks = this.tracks.filter(t => false !== t.visible);
31577
+
31578
+
31579
+ const visibleChordSets =
31580
+ (this.groupByTrack ? this.chordManager.tracks : this.chordManager.chordSets).filter(t => t.visible);
30995
31581
 
30996
31582
  const jbrowseTracks = [];
30997
31583
  const colors = [];
30998
31584
 
30999
- for (let trackConfig of visibleTracks) {
31585
+ for (let chordSet of visibleChordSets) {
31586
+
31000
31587
  jbrowseTracks.push({
31001
- trackId: trackConfig.id,
31002
- name: trackConfig.name,
31588
+ trackId: chordSet.id,
31589
+ name: chordSet.name,
31003
31590
  assemblyNames: ['forIGV'],
31004
31591
  type: 'VariantTrack',
31005
31592
  adapter: {
31006
31593
  type: 'FromConfigAdapter',
31007
- features: trackConfig.chords,
31594
+ features: chordSet.chords,
31008
31595
  }
31009
31596
  });
31010
- colors.push(trackConfig.color);
31011
-
31597
+ colors.push(chordSet.color);
31012
31598
  }
31013
31599
 
31014
31600
  this.viewState = createViewState({
@@ -31017,7 +31603,7 @@ class CircularView {
31017
31603
  });
31018
31604
 
31019
31605
  // Set view colors
31020
- for (let i = 0; i < visibleTracks.length; i++) {
31606
+ for (let i = 0; i < visibleChordSets.length; i++) {
31021
31607
  this.viewState.config.tracks[i].displays[0].renderer.strokeColor.set(colors[i]);
31022
31608
  //this.viewState.config.tracks[i].displays[0].renderer.strokeColor.set("jexl:get(feature, 'color') || 'black'");
31023
31609
  //this.viewState.config.tracks[i].displays[0].renderer.strokeColorSelected.set("jexl:get(feature, 'highlightColor') || 'red'");
@@ -31028,10 +31614,11 @@ class CircularView {
31028
31614
 
31029
31615
  ReactDOM.render(this.element, this.container);
31030
31616
 
31031
- for (let i = 0; i < visibleTracks.length; i++) {
31617
+ const onChordClick = this.config.onChordClick || defaultOnChordClick;
31618
+ for (let i = 0; i < visibleChordSets.length; i++) {
31032
31619
  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);
31620
+ if (onChordClick) {
31621
+ this.viewState.pluginManager.jexl.addFunction('onChordClick', onChordClick);
31035
31622
  this.viewState.config.tracks[i].displays[0].onChordClick.set(
31036
31623
  'jexl:onChordClick(feature, track, pluginManager)'
31037
31624
  );
@@ -31042,20 +31629,32 @@ class CircularView {
31042
31629
 
31043
31630
  function setAlpha(rgba, alpha) {
31044
31631
  const [a, b, c, ignore] = rgba.split(','); // rgba(r g b alpha)
31045
- return `${ a },${ b },${ c },${ alpha })`
31632
+ return `${a},${b},${c},${alpha})`
31633
+ }
31634
+
31635
+ function getAlpha(rgba) {
31636
+ if (rgba.startsWith("rgba(")) {
31637
+ return Number(rgba.split(',')[3].replace(')', ''))
31638
+ } else {
31639
+ return 1
31640
+ }
31046
31641
  }
31047
31642
 
31048
31643
  function shortChrName$1(chrName) {
31049
31644
  return chrName.startsWith("chr") ? chrName.substring(3) : chrName
31050
31645
  }
31051
31646
 
31647
+ function defaultOnChordClick(feature, chordTrack, pluginManager) {
31648
+ console.log(feature);
31649
+ }
31650
+
31052
31651
  function guid() {
31053
31652
  return ("0000" + (Math.random() * Math.pow(36, 4) << 0).toString(36)).slice(-4)
31054
31653
  }
31055
31654
 
31056
31655
  function embedCSS$1() {
31057
31656
 
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';
31657
+ 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
31658
 
31060
31659
  const style = document.createElement('style');
31061
31660
  style.setAttribute('type', 'text/css');
@@ -32417,59 +33016,61 @@ class AlignmentTrack {
32417
33016
  list.push('<hr/>');
32418
33017
 
32419
33018
  const clickedObject = this.getClickedObject(clickState);
33019
+
32420
33020
  if (clickedObject) {
32421
33021
 
32422
33022
  const showSoftClips = this.parent.showSoftClips;
32423
33023
  const clickedAlignment = (typeof clickedObject.alignmentContaining === 'function') ?
32424
33024
  clickedObject.alignmentContaining(clickState.genomicLocation, showSoftClips) :
32425
33025
  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}`);
33026
+ if (clickedAlignment) {
33027
+ if (clickedAlignment.isPaired() && clickedAlignment.isMateMapped()) {
33028
+ list.push({
33029
+ label: 'View mate in split screen',
33030
+ click: () => {
33031
+ if (clickedAlignment.mate) {
33032
+ const referenceFrame = clickState.viewport.referenceFrame;
33033
+ if (this.browser.genome.getChromosome(clickedAlignment.mate.chr)) {
33034
+ this.highlightedAlignmentReadNamed = clickedAlignment.readName;
33035
+ this.browser.presentMultiLocusPanel(clickedAlignment, referenceFrame);
33036
+ } else {
33037
+ Alert.presentAlert(`Reference does not contain chromosome: ${clickedAlignment.mate.chr}`);
33038
+ }
32438
33039
  }
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
- }
33040
+ },
33041
+ init: undefined
33042
+ });
32457
33043
  }
32458
- });
32459
33044
 
32460
- if (isSecureContext()) {
32461
33045
  list.push({
32462
- label: 'Copy read sequence',
33046
+ label: 'View read sequence',
32463
33047
  click: () => {
32464
33048
  const alignment = clickedAlignment;
32465
33049
  if (!alignment) return
33050
+
32466
33051
  const seqstring = alignment.seq; //.map(b => String.fromCharCode(b)).join("");
32467
- navigator.clipboard.writeText(seqstring);
33052
+ if (!seqstring || "*" === seqstring) {
33053
+ Alert.presentAlert("Read sequence: *");
33054
+ } else {
33055
+ Alert.presentAlert(seqstring);
33056
+ }
32468
33057
  }
32469
33058
  });
32470
- }
32471
33059
 
32472
- list.push('<hr/>');
33060
+ if (isSecureContext()) {
33061
+ list.push({
33062
+ label: 'Copy read sequence',
33063
+ click: () => {
33064
+ const alignment = clickedAlignment;
33065
+ if (!alignment) return
33066
+ const seqstring = alignment.seq; //.map(b => String.fromCharCode(b)).join("");
33067
+ navigator.clipboard.writeText(seqstring);
33068
+ }
33069
+ });
33070
+ }
33071
+
33072
+ list.push('<hr/>');
33073
+ }
32473
33074
  }
32474
33075
 
32475
33076
  // Experimental JBrowse feature
@@ -32739,7 +33340,7 @@ function getChrColor(chr) {
32739
33340
  chrColorMap[chr] = color;
32740
33341
  return color
32741
33342
  } else {
32742
- const color = IGVColor.randomRGB();
33343
+ const color = IGVColor.randomRGB(0, 255);
32743
33344
  chrColorMap[chr] = color;
32744
33345
  return color
32745
33346
  }
@@ -32747,7 +33348,7 @@ function getChrColor(chr) {
32747
33348
 
32748
33349
  const chrColorMap = {
32749
33350
  "chrX": "rgb(204, 153, 0)",
32750
- "chrY": "rgb(153, 204, 0",
33351
+ "chrY": "rgb(153, 204, 0)",
32751
33352
  "chrUn": "rgb(50, 50, 50)",
32752
33353
  "chr1": "rgb(80, 80, 255)",
32753
33354
  "chrI": "rgb(139, 155, 187)",
@@ -33001,143 +33602,68 @@ class RulerViewport extends TrackViewport {
33001
33602
 
33002
33603
  }
33003
33604
 
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
- }
33605
+ const viewportColumnManager =
33606
+ {
33607
+ createColumns: (columnContainer, count) => {
33076
33608
 
33077
- if (xPercentage + (chrCoveragePercentage / 2.0) > 1.0) {
33078
- xPercentage = 1.0 - chrCoveragePercentage / 2.0;
33609
+ for (let i = 0; i < count; i++) {
33610
+ if (0 === i) {
33611
+ createColumn(columnContainer, 'igv-column');
33612
+ } else {
33613
+ columnContainer.appendChild(div$1({class: 'igv-column-shim'}));
33614
+ createColumn(columnContainer, 'igv-column');
33615
+ }
33079
33616
  }
33080
33617
 
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
- }
33618
+ },
33091
33619
 
33092
- }
33620
+ removeColumnAtIndex: (i, column) => {
33621
+ const shim = 0 === i ? column.nextElementSibling : column.previousElementSibling;
33622
+ column.remove();
33623
+ shim.remove();
33624
+ },
33093
33625
 
33094
- removeViewportClickHandler(viewport) {
33095
- viewport.removeEventListener('click', this.boundClickHandler);
33096
- }
33626
+ insertAfter: referenceElement => {
33097
33627
 
33098
- setWidth(width) {
33099
- this.$viewport.width(width);
33100
- }
33628
+ const shim = div$1({class: 'igv-column-shim'});
33629
+ insertElementAfter(shim, referenceElement);
33101
33630
 
33102
- drawSVGWithContext(context, width, height, id, x, y, yClipOffset) {
33631
+ const column = div$1({class: 'igv-column'});
33632
+ insertElementAfter(column, shim);
33103
33633
 
33104
- context.saveWithTranslationAndClipRect(id, x, y, width, height, yClipOffset);
33634
+ return column
33635
+ },
33105
33636
 
33106
- this.trackView.track.draw({
33107
- context,
33108
- referenceFrame: this.referenceFrame,
33109
- pixelWidth: width,
33110
- pixelHeight: height
33111
- });
33637
+ insertBefore: (referenceElement, count) => {
33112
33638
 
33113
- context.restore();
33114
- }
33639
+ for (let i = 0; i < count; i++) {
33115
33640
 
33116
- async repaint() {
33117
- this.draw({referenceFrame: this.referenceFrame});
33118
- }
33641
+ const column = div$1({class: 'igv-column'});
33642
+ insertElementBefore(column, referenceElement);
33119
33643
 
33120
- draw({referenceFrame}) {
33644
+ if (count > 1 && i > 0) {
33645
+ const columnShim = div$1({class: 'igv-column-shim'});
33646
+ insertElementBefore(columnShim, column);
33647
+ }
33121
33648
 
33122
- this.$canvas.hide();
33649
+ }
33123
33650
 
33124
- IGVGraphics.configureHighDPICanvas(this.ideogram_ctx, this.$viewport.width(), this.$viewport.height());
33651
+ },
33125
33652
 
33126
- this.trackView.track.draw({
33127
- context: this.ideogram_ctx,
33128
- referenceFrame,
33129
- pixelWidth: this.$viewport.width(),
33130
- pixelHeight: this.$viewport.height()
33131
- });
33132
- }
33653
+ indexOfColumn: (columnContainer, column) => {
33133
33654
 
33134
- startSpinner() {
33135
- }
33655
+ const allColumns = columnContainer.querySelectorAll('.igv-column');
33136
33656
 
33137
- stopSpinner() {
33138
- }
33657
+ for (let i = 0; i < allColumns.length; i++) {
33658
+ const c = allColumns[ i ];
33659
+ if (c === column) {
33660
+ return i
33661
+ }
33662
+ }
33139
33663
 
33140
- }
33664
+ return undefined
33665
+ },
33666
+ };
33141
33667
 
33142
33668
  /*
33143
33669
  * The MIT License (MIT)
@@ -33164,185 +33690,157 @@ class IdeogramViewport extends TrackViewport {
33164
33690
  * THE SOFTWARE.
33165
33691
  */
33166
33692
 
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
- */
33693
+ class IdeogramViewport extends TrackViewport {
33173
33694
 
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
- }
33695
+ constructor(trackView, viewportColumn, referenceFrame, width) {
33696
+ super(trackView, viewportColumn, referenceFrame, width);
33697
+ }
33179
33698
 
33180
- function buildOptions(config, options) {
33699
+ initializationHelper() {
33181
33700
 
33182
- var defaultOptions = {
33183
- oauthToken: config.oauthToken,
33184
- headers: config.headers,
33185
- withCredentials: config.withCredentials,
33186
- filename: config.filename
33187
- };
33701
+ this.$ideogramCanvas = $$1('<canvas>', {class: 'igv-ideogram-canvas'});
33702
+ this.$ideogramCanvas.insertBefore(this.$canvas);
33188
33703
 
33189
- return Object.assign(defaultOptions, options)
33190
- }
33704
+ const canvas = this.$ideogramCanvas.get(0);
33705
+ this.ideogram_ctx = canvas.getContext('2d');
33191
33706
 
33192
- /**
33193
- * isMobile test from http://detectmobilebrowsers.com
33194
- * TODO -- improve UI design so this isn't neccessary
33195
- * @returns {boolean}
33196
- */
33707
+ this.$canvas.remove();
33708
+ this.canvas = undefined;
33709
+ this.ctx = undefined;
33197
33710
 
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
- // }
33711
+ this.addMouseHandlers();
33205
33712
 
33206
- const doAutoscale = function (features) {
33207
- var min, max;
33713
+ }
33208
33714
 
33209
- if (features.length > 0) {
33210
- min = Number.MAX_VALUE;
33211
- max = -Number.MAX_VALUE;
33715
+ addMouseHandlers() {
33716
+ this.addBrowserObserver();
33717
+ this.addViewportClickHandler(this.$viewport.get(0));
33718
+ }
33212
33719
 
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);
33720
+ removeMouseHandlers() {
33721
+ this.removeBrowserObserver();
33722
+ this.removeViewportClickHandler(this.$viewport.get(0));
33723
+
33724
+ }
33725
+
33726
+ addBrowserObserver() {
33727
+
33728
+ function observerHandler(referenceFrameList) {
33729
+ const column = this.$viewport.get(0).parentElement;
33730
+ if (null !== column) {
33731
+ const index = viewportColumnManager.indexOfColumn(this.browser.columnContainer, column);
33732
+ // console.log(`ideogram-viewport - locus-change-handler index(${ index }) ${ referenceFrameList[ index ].getLocusString() } ${ Date.now() } `)
33733
+ this.update(this.ideogram_ctx, this.$viewport.width(), this.$viewport.height(), referenceFrameList[ index ]);
33217
33734
  }
33218
- });
33735
+ }
33219
33736
 
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;
33737
+ this.boundObserverHandler = observerHandler.bind(this);
33738
+ this.browser.on('locuschange', this.boundObserverHandler);
33227
33739
  }
33228
33740
 
33229
- return {min: min, max: max}
33230
- };
33741
+ removeBrowserObserver() {
33742
+ this.browser.off('locuschange', this.boundObserverHandler);
33743
+ }
33231
33744
 
33232
- const validateLocusExtent = function (chromosomeLengthBP, extent, minimumBP) {
33745
+ addViewportClickHandler(viewport) {
33233
33746
 
33234
- let ss = extent.start;
33235
- let ee = extent.end;
33747
+ function clickHandler(event) {
33236
33748
 
33237
- if (undefined === ee) {
33749
+ const column = viewport.parentElement;
33750
+ const index = viewportColumnManager.indexOfColumn(this.browser.columnContainer, column);
33751
+ const referenceFrame = this.browser.referenceFrameList[ index ];
33238
33752
 
33239
- ss -= minimumBP / 2;
33240
- ee = ss + minimumBP;
33753
+ const {xNormalized, width} = translateMouseCoordinates$1(event, this.ideogram_ctx.canvas);
33754
+ const {bpLength} = this.browser.genome.getChromosome(referenceFrame.chr);
33755
+ const locusLength = referenceFrame.bpPerPixel * width;
33756
+ const chrCoveragePercentage = locusLength / bpLength;
33241
33757
 
33242
- if (ee > chromosomeLengthBP) {
33243
- ee = chromosomeLengthBP;
33244
- ss = ee - minimumBP;
33245
- } else if (ss < 0) {
33246
- ss = 0;
33247
- ee = minimumBP;
33248
- }
33758
+ let xPercentage = xNormalized;
33759
+ if (xPercentage - (chrCoveragePercentage / 2.0) < 0) {
33760
+ xPercentage = chrCoveragePercentage / 2.0;
33761
+ }
33249
33762
 
33250
- } else if (ee - ss < minimumBP) {
33763
+ if (xPercentage + (chrCoveragePercentage / 2.0) > 1.0) {
33764
+ xPercentage = 1.0 - chrCoveragePercentage / 2.0;
33765
+ }
33251
33766
 
33252
- const center = (ee + ss) / 2;
33767
+ const ss = Math.round((xPercentage - (chrCoveragePercentage / 2.0)) * bpLength);
33768
+ const ee = Math.round((xPercentage + (chrCoveragePercentage / 2.0)) * bpLength);
33253
33769
 
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
- }
33770
+ referenceFrame.start = ss;
33771
+ referenceFrame.end = ee;
33772
+ referenceFrame.bpPerPixel = (ee - ss) / width;
33265
33773
 
33266
- extent.start = Math.ceil(ss);
33267
- extent.end = Math.floor(ee);
33268
- };
33774
+ this.browser.updateViews(referenceFrame, this.browser.trackViews, true);
33269
33775
 
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
- */
33776
+ }
33777
+
33778
+ this.boundClickHandler = clickHandler.bind(this);
33779
+ viewport.addEventListener('click', this.boundClickHandler);
33276
33780
 
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
33781
  }
33284
- return false
33285
- };
33286
33782
 
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)
33783
+ removeViewportClickHandler(viewport) {
33784
+ viewport.removeEventListener('click', this.boundClickHandler);
33297
33785
  }
33298
- }
33299
33786
 
33300
- function prettyBasePairNumber(raw) {
33787
+ setWidth(width) {
33788
+ this.$viewport.width(width);
33789
+ }
33301
33790
 
33302
- var denom,
33303
- units,
33304
- value,
33305
- floored;
33791
+ drawSVGWithContext(context, width, height, id, x, y, yClipOffset) {
33306
33792
 
33307
- if (raw > 1e7) {
33308
- denom = 1e6;
33309
- units = " mb";
33310
- } else if (raw > 1e4) {
33793
+ context.saveWithTranslationAndClipRect(id, x, y, width, height, yClipOffset);
33311
33794
 
33312
- denom = 1e3;
33313
- units = " kb";
33795
+ this.trackView.track.draw({
33796
+ context,
33797
+ referenceFrame: this.referenceFrame,
33798
+ pixelWidth: width,
33799
+ pixelHeight: height
33800
+ });
33314
33801
 
33315
- value = raw / denom;
33316
- floored = Math.floor(value);
33317
- return numberFormatter$1(floored) + units
33318
- } else {
33319
- return numberFormatter$1(raw) + " bp"
33802
+ context.restore();
33320
33803
  }
33321
33804
 
33322
- value = raw / denom;
33323
- floored = Math.floor(value);
33324
-
33325
- return floored.toString() + units
33326
- }
33805
+ update(context, pixelWidth, pixelHeight, referenceFrame) {
33806
+ this.$canvas.hide();
33807
+ IGVGraphics.configureHighDPICanvas(context, pixelWidth, pixelHeight);
33808
+ this.trackView.track.draw({ context, referenceFrame, pixelWidth, pixelHeight });
33809
+ }
33327
33810
 
33811
+ startSpinner() {
33812
+ }
33328
33813
 
33329
- function isDataURL(obj) {
33330
- return (isString$3(obj) && obj.startsWith("data:"))
33331
- }
33814
+ stopSpinner() {
33815
+ }
33332
33816
 
33333
- function createColumn(columnContainer, className) {
33334
- const column = div$1({class: className});
33335
- columnContainer.appendChild(column);
33336
33817
  }
33337
33818
 
33819
+ /*
33820
+ * The MIT License (MIT)
33821
+ *
33822
+ * Copyright (c) 2014 Broad Institute
33823
+ *
33824
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
33825
+ * of this software and associated documentation files (the "Software"), to deal
33826
+ * in the Software without restriction, including without limitation the rights
33827
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
33828
+ * copies of the Software, and to permit persons to whom the Software is
33829
+ * furnished to do so, subject to the following conditions:
33830
+ *
33831
+ * The above copyright notice and this permission notice shall be included in
33832
+ * all copies or substantial portions of the Software.
33833
+ *
33834
+ *
33835
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33836
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33837
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
33838
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33839
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
33840
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
33841
+ * THE SOFTWARE.
33842
+ */
33338
33843
 
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
33844
 
33347
33845
  function createViewport(trackView, column, referenceFrame, width) {
33348
33846
 
@@ -33355,13 +33853,6 @@ function createViewport(trackView, column, referenceFrame, width) {
33355
33853
  }
33356
33854
  }
33357
33855
 
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
33856
  const maxFontSize = 10;
33366
33857
 
33367
33858
  const fontConfigureTemplate =
@@ -33377,7 +33868,7 @@ class SampleNameViewport {
33377
33868
 
33378
33869
  constructor(trackView, column, unused, width) {
33379
33870
 
33380
- this.guid = guid$1();
33871
+ this.guid = guid$2();
33381
33872
  this.trackView = trackView;
33382
33873
 
33383
33874
  this.browser = trackView.browser;
@@ -33495,7 +33986,7 @@ class SampleNameViewport {
33495
33986
  const {width, height} = this.viewport.getBoundingClientRect();
33496
33987
 
33497
33988
  const str = (this.trackView.track.name || this.trackView.track.id).replace(/\W/g, '');
33498
- const id = `${str}_sample_names_guid_${guid$1()}`;
33989
+ const id = `${str}_sample_names_guid_${guid$2()}`;
33499
33990
 
33500
33991
  context.saveWithTranslationAndClipRect(id, deltaX, deltaY + yScrollDelta, width, height, -yScrollDelta);
33501
33992
 
@@ -33879,7 +34370,7 @@ class TrackView {
33879
34370
 
33880
34371
  constructor(browser, columnContainer, track) {
33881
34372
 
33882
- this.namespace = `trackview-${guid$1()}`;
34373
+ this.namespace = `trackview-${guid$2()}`;
33883
34374
 
33884
34375
  this.browser = browser;
33885
34376
  this.track = track;
@@ -34234,11 +34725,13 @@ class TrackView {
34234
34725
  for (let viewport of reloadableViewports) {
34235
34726
  await viewport.loadFeatures();
34236
34727
  }
34728
+
34729
+ if (this.disposed) return // Track was removed during load
34237
34730
 
34238
34731
  // Very special case for variant tracks in multilocus view. The # of rows to allocate to the variant (site)
34239
34732
  // section depends on data from all the views. We only need to adjust this however if any data was loaded
34240
34733
  // (i.e. reloadableViewports.length > 0)
34241
- if (typeof this.track.variantRowCount === 'function' && reloadableViewports.length > 0) {
34734
+ if (this.track && typeof this.track.variantRowCount === 'function' && reloadableViewports.length > 0) {
34242
34735
  let maxRow = 0;
34243
34736
  for (let viewport of this.viewports) {
34244
34737
  if (viewport.tile && viewport.tile.features) {
@@ -34254,7 +34747,6 @@ class TrackView {
34254
34747
  }
34255
34748
  }
34256
34749
 
34257
- if (this.disposed) return // Track was removed during load
34258
34750
 
34259
34751
  if (this.track.autoscale) {
34260
34752
  let allFeatures = [];
@@ -34702,7 +35194,7 @@ function renderSVGAxis(context, track, axisCanvas, deltaX, deltaY) {
34702
35194
  const {y, width, height} = axisCanvas.getBoundingClientRect();
34703
35195
 
34704
35196
  const str = (track.name || track.id).replace(/\W/g, '');
34705
- const id = `${str}_axis_guid_${guid$1()}`;
35197
+ const id = `${str}_axis_guid_${guid$2()}`;
34706
35198
 
34707
35199
  context.saveWithTranslationAndClipRect(id, deltaX, y + deltaY, width, height, 0);
34708
35200
 
@@ -34982,6 +35474,9 @@ class GFFFeature {
34982
35474
 
34983
35475
  pd.push({name: 'Type', value: this.type});
34984
35476
  pd.push({name: 'Source', value: this.source});
35477
+ if (this.score !== undefined) {
35478
+ pd.push({name: 'Score', value: this.score});
35479
+ }
34985
35480
 
34986
35481
  if (this.attributeString) {
34987
35482
  const atts = parseAttributeString(this.attributeString, this.delim);
@@ -35230,7 +35725,7 @@ function decode(tokens, header) {
35230
35725
  chr: tokens[0],
35231
35726
  start: parseInt(tokens[3]) - 1,
35232
35727
  end: parseInt(tokens[4]),
35233
- score: "." === tokens[5] ? 0 : Number(tokens[5]),
35728
+ score: "." === tokens[5] ? undefined : Number(tokens[5]),
35234
35729
  strand: tokens[6],
35235
35730
  phase: "." === tokens[7] ? 0 : parseInt(tokens[7]),
35236
35731
  attributeString: tokens[8],
@@ -36586,244 +37081,29 @@ class SegFeature {
36586
37081
  * THE SOFTWARE.
36587
37082
  */
36588
37083
 
36589
-
36590
- const knownAltBases = new Set(["A", "C", "T", "G"].map(c => c.charCodeAt(0)));
36591
-
37084
+ /**
37085
+ * Create a variant from an array of tokens representing a line in a "VCF" file
37086
+ * @param tokens
37087
+ */
36592
37088
  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 = {};
37089
+ const variant = new Variant();
37090
+ variant.chr = tokens[0]; // TODO -- use genome aliases
37091
+ variant.pos = parseInt(tokens[1]);
37092
+ variant.names = tokens[2]; // id in VCF
37093
+ variant.referenceBases = tokens[3];
37094
+ variant.alternateBases = tokens[4];
37095
+ variant.quality = tokens[5];
37096
+ variant.filter = tokens[6];
37097
+ variant.info = {};
37098
+ const infoStr = tokens[7];
36771
37099
  if (infoStr) {
36772
- infoStr.split(';').forEach(function (elem) {
37100
+ for (let elem of infoStr.split(';')) {
36773
37101
  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
37102
+ variant.info[element[0]] = element[1];
36785
37103
  }
36786
37104
  }
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)
37105
+ variant.init();
37106
+ return variant;
36827
37107
  }
36828
37108
 
36829
37109
 
@@ -36887,6 +37167,217 @@ function createGAVariant(json) {
36887
37167
 
36888
37168
  }
36889
37169
 
37170
+
37171
+ class Variant {
37172
+
37173
+ init() {
37174
+
37175
+ const ref = this.referenceBases;
37176
+ const altBases = this.alternateBases;
37177
+
37178
+ if (this.info) {
37179
+ if (this.info["VT"]) {
37180
+ this.type = this.info["VT"];
37181
+ } else if (this.info["SVTYPE"]) {
37182
+ this.type = "SV";
37183
+ } else if (this.info["PERIOD"]) {
37184
+ this.type = "STR";
37185
+ }
37186
+ }
37187
+ if (this.type === undefined) {
37188
+ this.type = determineType(ref, altBases);
37189
+ }
37190
+ if (this.type === "NONVARIANT") {
37191
+ this.heterozygosity = 0;
37192
+ }
37193
+
37194
+ // Determine start/end coordinates -- these are the coordinates representing the actual variant,
37195
+ // not the leading or trailing reference
37196
+ if (this.info["END"]) {
37197
+ this.start = this.pos - 1;
37198
+ if (this.info["CHR2"] && this.info["CHR2"] !== this.chr) {
37199
+ this.end = this.start + 1;
37200
+ } else {
37201
+ this.end = Number.parseInt(this.info["END"]);
37202
+ }
37203
+ } else {
37204
+ if (this.type === "NONVARIANT") {
37205
+ this.start = this.pos - 1; // convert to 0-based coordinate convention
37206
+ this.end = this.start + ref.length;
37207
+ } else {
37208
+
37209
+ const altTokens = altBases.split(",").filter(token => token.length > 0);
37210
+ this.alleles = [];
37211
+ this.start = undefined;
37212
+ this.end = undefined;
37213
+
37214
+ for (let alt of altTokens) {
37215
+
37216
+ this.alleles.push(alt);
37217
+
37218
+ // We don't yet handle SV and other special alt representations
37219
+ if ("SV" !== this.type && isKnownAlt(alt)) {
37220
+
37221
+ let altLength = alt.length;
37222
+ let lengthOnRef = ref.length;
37223
+ const lmin = Math.min(altLength, lengthOnRef);
37224
+
37225
+ // Trim off matching bases. Try first match, then right -> left, then any remaining left -> right
37226
+ let s = 0;
37227
+
37228
+ while (s < lmin && (ref.charCodeAt(s) === alt.charCodeAt(s))) {
37229
+ s++;
37230
+ altLength--;
37231
+ lengthOnRef--;
37232
+ }
37233
+
37234
+ // right -> left from end
37235
+ while (altLength > 0 && lengthOnRef > 0) {
37236
+ const altIdx = s + altLength - 1;
37237
+ const refIdx = s + lengthOnRef - 1;
37238
+ if (alt.charCodeAt(altIdx) === ref.charCodeAt(refIdx)) {
37239
+ altLength--;
37240
+ lengthOnRef--;
37241
+ } else {
37242
+ break
37243
+ }
37244
+ }
37245
+
37246
+ // if any remaining, left -> right
37247
+ while (altLength > 0 && lengthOnRef > 0) {
37248
+ const altIdx = s;
37249
+ const refIdx = s;
37250
+ if (alt.charCodeAt(altIdx) === ref.charCodeAt(refIdx)) {
37251
+ s++;
37252
+ altLength--;
37253
+ lengthOnRef--;
37254
+ } else {
37255
+ break
37256
+ }
37257
+ }
37258
+
37259
+ const alleleStart = this.pos + s - 1; // -1 for zero based coordinates
37260
+ const alleleEnd = alleleStart + lengthOnRef;
37261
+ this.start = this.start === undefined ? alleleStart : Math.min(this.start, alleleStart);
37262
+ this.end = this.end === undefined ? alleleEnd : Math.max(this.end, alleleEnd);
37263
+ }
37264
+ }
37265
+
37266
+ // Default to single base representation @ position for variant types not otherwise handled
37267
+ if (this.start === undefined) {
37268
+ this.start = this.pos - 1;
37269
+ this.end = this.pos;
37270
+ }
37271
+ }
37272
+ }
37273
+ }
37274
+
37275
+
37276
+ popupData(genomicLocation, genomeId) {
37277
+
37278
+
37279
+ const posString = `${numberFormatter$1(this.pos)}`;
37280
+ const locString = this.start === this.end ?
37281
+ `${numberFormatter$1(this.start)} | ${numberFormatter$1(this.start + 1)}` :
37282
+ `${numberFormatter$1(this.start + 1)}-${numberFormatter$1(this.end)}`;
37283
+ const fields = [
37284
+ {name: "Chr", value: this.chr},
37285
+ {name: "Pos", value: posString},
37286
+ {name: "Loc", value: locString},
37287
+ {name: "Names", value: this.names ? this.names : ""},
37288
+ {name: "Ref", value: this.referenceBases},
37289
+ {name: "Alt", value: this.alternateBases.replace("<", "&lt;")},
37290
+ {name: "Qual", value: this.quality},
37291
+ {name: "Filter", value: this.filter}
37292
+ ];
37293
+
37294
+ if ("SNP" === this.type) {
37295
+ let ref = this.referenceBases;
37296
+ if (ref.length === 1) {
37297
+ let altArray = this.alternateBases.split(",");
37298
+ for (let alt of altArray) {
37299
+ if (alt.length === 1) {
37300
+ let l = TrackBase.getCravatLink(this.chr, this.pos, ref, alt, genomeId);
37301
+ if (l) {
37302
+ fields.push('<hr/>');
37303
+ fields.push({html: l});
37304
+ }
37305
+ }
37306
+ }
37307
+ }
37308
+ }
37309
+
37310
+ if (this.hasOwnProperty("heterozygosity")) {
37311
+ fields.push({name: "Heterozygosity", value: this.heterozygosity});
37312
+ }
37313
+
37314
+ if (this.info) {
37315
+ fields.push({html: '<hr style="border-top: dotted 1px;border-color: #c9c3ba" />'});
37316
+ for (let key of Object.keys(this.info)) {
37317
+ fields.push({name: key, value: arrayToString(decodeURIComponent(this.info[key]))});
37318
+ }
37319
+ }
37320
+
37321
+ return fields
37322
+
37323
+ };
37324
+
37325
+ isRefBlock() {
37326
+ return "NONVARIANT" === this.type
37327
+ }
37328
+
37329
+ }
37330
+
37331
+ const knownAltBases = new Set(["A", "C", "T", "G"].map(c => c.charCodeAt(0)));
37332
+
37333
+ function isKnownAlt(alt) {
37334
+ for (let i = 0; i < alt.length; i++) {
37335
+ if (!knownAltBases.has(alt.charCodeAt(i))) {
37336
+ return false
37337
+ }
37338
+ }
37339
+ return true
37340
+ }
37341
+
37342
+
37343
+ function determineType(ref, altAlleles) {
37344
+ const refLength = ref.length;
37345
+ if (altAlleles === undefined) {
37346
+ return "UNKNOWN"
37347
+ } else if (altAlleles.trim().length === 0 ||
37348
+ altAlleles === "<NON_REF>" ||
37349
+ altAlleles === "<*>" ||
37350
+ altAlleles === ".") {
37351
+ return "NONVARIANT"
37352
+ } else {
37353
+ const alleles = altAlleles.split(",");
37354
+ const types = alleles.map(function (a) {
37355
+ if (refLength === 1 && a.length === 1) {
37356
+ return "SNP"
37357
+ } else {
37358
+ return "<NON_REF>" === a ? "NONVARIANT" : "OTHER"
37359
+ }
37360
+ });
37361
+ let type = types[0];
37362
+ for (let t of types) {
37363
+ if (t !== type) {
37364
+ return "MIXED"
37365
+ }
37366
+ }
37367
+ return type
37368
+ }
37369
+ }
37370
+
37371
+ function arrayToString(value, delim) {
37372
+
37373
+ if (delim === undefined) delim = ",";
37374
+
37375
+ if (!(Array.isArray(value))) {
37376
+ return value
37377
+ }
37378
+ return value.join(delim)
37379
+ }
37380
+
36890
37381
  /*
36891
37382
  * The MIT License (MIT)
36892
37383
  *
@@ -37031,6 +37522,7 @@ class VcfParser {
37031
37522
  if (tokens.length === nExpectedColumns) {
37032
37523
  const variant = createVCFVariant(tokens);
37033
37524
  variant.header = this.header; // Keep a pointer to the header to interpret fields for popup text
37525
+ //variant.line = line // Uncomment for debugging
37034
37526
  allFeatures.push(variant);
37035
37527
 
37036
37528
  if (tokens.length > 9) {
@@ -39153,6 +39645,7 @@ class HtsgetVariantReader extends HtsgetReader {
39153
39645
  * THE SOFTWARE.
39154
39646
  */
39155
39647
 
39648
+ const DEFAULT_MAX_WG_COUNT = 10000;
39156
39649
 
39157
39650
  /**
39158
39651
  * feature source for "bed like" files (tab or whitespace delimited files with 1 feature per line: bed, gff, vcf, etc)
@@ -39167,6 +39660,7 @@ class TextFeatureSource {
39167
39660
  this.config = config || {};
39168
39661
  this.genome = genome;
39169
39662
  this.sourceType = (config.sourceType === undefined ? "file" : config.sourceType);
39663
+ this.maxWGCount = config.maxWGCount || DEFAULT_MAX_WG_COUNT;
39170
39664
 
39171
39665
  const queryableFormats = new Set(["bigwig", "bw", "bigbed", "bb", "tdf"]);
39172
39666
 
@@ -39384,10 +39878,22 @@ class TextFeatureSource {
39384
39878
  // TODO -- filter by pixel size
39385
39879
  getWGFeatures(allFeatures) {
39386
39880
 
39881
+ const makeWGFeature = (f) => {
39882
+ const wg = Object.assign({}, f);
39883
+ wg.chr = "all";
39884
+ wg.start = genome.getGenomeCoordinate(f.chr, f.start);
39885
+ wg.end = genome.getGenomeCoordinate(f.chr, f.end);
39886
+ wg._f = f;
39887
+ // Don't draw exons in whole genome view
39888
+ if (wg["exons"]) delete wg["exons"];
39889
+ return wg
39890
+ };
39891
+
39387
39892
  const genome = this.genome;
39388
39893
  const wgChromosomeNames = new Set(genome.wgChromosomeNames);
39389
39894
  const wgFeatures = [];
39390
-
39895
+ let count = 0;
39896
+ const max = this.maxWGCount;
39391
39897
  for (let c of genome.wgChromosomeNames) {
39392
39898
 
39393
39899
  const features = allFeatures[c];
@@ -39396,19 +39902,18 @@ class TextFeatureSource {
39396
39902
  for (let f of features) {
39397
39903
  let queryChr = genome.getChromosomeName(f.chr);
39398
39904
  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);
39905
+ if (wgFeatures.length < max) {
39906
+ wgFeatures.push(makeWGFeature(f));
39907
+ } else {
39908
+ //Reservoir sampling
39909
+ const samplingProb = max / (count + 1);
39910
+ if (Math.random() < samplingProb) {
39911
+ const idx = Math.floor(Math.random() * (max - 1));
39912
+ wgFeatures[idx] = makeWGFeature(f);
39913
+ }
39914
+ }
39411
39915
  }
39916
+ count++;
39412
39917
  }
39413
39918
  }
39414
39919
  }
@@ -42062,7 +42567,7 @@ class FeatureTrack extends TrackBase {
42062
42567
  desc += "</html>";
42063
42568
  return desc
42064
42569
  } else {
42065
- return this.name
42570
+ return super.description();
42066
42571
  }
42067
42572
 
42068
42573
  };
@@ -42144,8 +42649,12 @@ class WigTrack extends TrackBase {
42144
42649
 
42145
42650
  const format = config.format ? config.format.toLowerCase() : config.format;
42146
42651
  if ("bigwig" === format) {
42652
+ this.flipAxis = config.flipAxis ? config.flipAxis : false;
42653
+ this.logScale = config.logScale ? config.logScale : false;
42147
42654
  this.featureSource = new BWSource(config, this.browser.genome);
42148
42655
  } else if ("tdf" === format) {
42656
+ this.flipAxis = config.flipAxis ? config.flipAxis : false;
42657
+ this.logScale = config.logScale ? config.logScale : false;
42149
42658
  this.featureSource = new TDFSource(config, this.browser.genome);
42150
42659
  } else {
42151
42660
  this.featureSource = FeatureSource(config, this.browser.genome);
@@ -42194,7 +42703,20 @@ class WigTrack extends TrackBase {
42194
42703
  }
42195
42704
 
42196
42705
  menuItemList() {
42197
- return MenuUtils.numericDataMenuItems(this.trackView)
42706
+ let items = [];
42707
+ if (this.flipAxis !== undefined) {
42708
+ items.push({
42709
+ label:"Flip y-axis",
42710
+ click: () => {
42711
+ this.flipAxis = !this.flipAxis;
42712
+ this.trackView.repaintViews();
42713
+ }
42714
+ });
42715
+ }
42716
+
42717
+ items = items.concat(MenuUtils.numericDataMenuItems(this.trackView));
42718
+
42719
+ return items
42198
42720
  }
42199
42721
 
42200
42722
  async getHeader() {
@@ -42205,6 +42727,27 @@ class WigTrack extends TrackBase {
42205
42727
  return this.header
42206
42728
  }
42207
42729
 
42730
+ // TODO: refactor to igvUtils.js
42731
+ getScaleFactor(min, max, height, logScale) {
42732
+ const scale = logScale ? height / (Math.log10(max + 1) - (min <= 0 ? 0 : Math.log10(min + 1))) : height / (max - min);
42733
+ return scale
42734
+ }
42735
+
42736
+ computeYPixelValue(yValue, yScaleFactor) {
42737
+ return (this.flipAxis ? (yValue - this.dataRange.min) : (this.dataRange.max - yValue)) * yScaleFactor
42738
+ }
42739
+
42740
+ computeYPixelValueInLogScale(yValue, yScaleFactor) {
42741
+ let maxValue = this.dataRange.max;
42742
+ let minValue = this.dataRange.min;
42743
+ if (maxValue <= 0) return 0 // TODO:
42744
+ if (minValue <= -1) minValue = 0;
42745
+ minValue = (minValue <= 0) ? 0 : Math.log10(minValue + 1);
42746
+ maxValue = Math.log10(maxValue + 1);
42747
+ yValue = Math.log10(yValue + 1);
42748
+ return ((this.flipAxis ? (yValue - minValue) : (maxValue - yValue)) * yScaleFactor)
42749
+ }
42750
+
42208
42751
  draw(options) {
42209
42752
 
42210
42753
  const features = options.features;
@@ -42212,7 +42755,7 @@ class WigTrack extends TrackBase {
42212
42755
  const bpPerPixel = options.bpPerPixel;
42213
42756
  const bpStart = options.bpStart;
42214
42757
  const pixelWidth = options.pixelWidth;
42215
- const pixelHeight = options.pixelHeight;
42758
+ options.pixelHeight;
42216
42759
  const bpEnd = bpStart + pixelWidth * bpPerPixel + 1;
42217
42760
  let lastPixelEnd = -1;
42218
42761
  let lastValue = -1;
@@ -42224,9 +42767,10 @@ class WigTrack extends TrackBase {
42224
42767
  baselineColor = IGVColor.addAlpha(posColor, 0.1);
42225
42768
  }
42226
42769
 
42227
- const yScale = (yValue) => {
42228
- return ((this.dataRange.max - yValue) / (this.dataRange.max - this.dataRange.min)) * pixelHeight
42229
- };
42770
+ const scaleFactor = this.getScaleFactor(this.dataRange.min, this.dataRange.max, options.pixelHeight, this.logScale);
42771
+ const yScale = (yValue) => this.logScale
42772
+ ? this.computeYPixelValueInLogScale(yValue, scaleFactor)
42773
+ : this.computeYPixelValue(yValue, scaleFactor);
42230
42774
 
42231
42775
  if (features && features.length > 0) {
42232
42776
 
@@ -42236,7 +42780,7 @@ class WigTrack extends TrackBase {
42236
42780
  // nothing to paint.
42237
42781
  if (this.dataRange.max > this.dataRange.min) {
42238
42782
 
42239
- const y0 = this.dataRange.min == 0 ? pixelHeight : yScale(0);
42783
+ const y0 = yScale(0);
42240
42784
  for (let f of features) {
42241
42785
 
42242
42786
  if (f.end < bpStart) continue
@@ -42260,9 +42804,6 @@ class WigTrack extends TrackBase {
42260
42804
 
42261
42805
  } else {
42262
42806
  let height = y - y0;
42263
- if ((Math.abs(height)) < 1) {
42264
- height = height < 0 ? -1 : 1;
42265
- }
42266
42807
  const pixelEnd = x + width;
42267
42808
  if (pixelEnd > lastPixelEnd || (f.value >= 0 && f.value > lastValue) || (f.value < 0 && f.value < lastNegValue)) {
42268
42809
  IGVGraphics.fillRect(ctx, x, y0, width, height, {fillStyle: color});
@@ -42359,6 +42900,21 @@ class WigTrack extends TrackBase {
42359
42900
  dispose() {
42360
42901
  this.trackView = undefined;
42361
42902
  }
42903
+
42904
+ /**
42905
+ * Return the current state of the track. Used to create sessions and bookmarks.
42906
+ *
42907
+ * @returns {*|{}}
42908
+ */
42909
+ getState() {
42910
+
42911
+ const config = super.getState();
42912
+
42913
+ if (this.flipAxis !== undefined) config.flipAxis = this.flipAxis;
42914
+ if (this.logScale !== undefined) config.logScale = this.logScale;
42915
+
42916
+ return config
42917
+ }
42362
42918
  }
42363
42919
 
42364
42920
  /**
@@ -42523,7 +43079,11 @@ class SegTrack extends TrackBase {
42523
43079
 
42524
43080
  // this.featureSource = config.sourceType === "bigquery" ?
42525
43081
  // new igv.BigQueryFeatureSource(this.config) :
42526
- this.featureSource = FeatureSource(this.config, this.browser.genome);
43082
+
43083
+ // Disable whole genome downsampling unless explicitly.
43084
+ const configCopy = Object.assign({}, this.config);
43085
+ configCopy.maxWGCount = configCopy.maxWGCount || Number.MAX_SAFE_INTEGER;
43086
+ this.featureSource = FeatureSource(configCopy, this.browser.genome);
42527
43087
 
42528
43088
  this.initialSort = config.sort;
42529
43089
  }
@@ -43173,6 +43733,19 @@ function autoscale(chr, featureArrays) {
43173
43733
  * THE SOFTWARE.
43174
43734
  */
43175
43735
 
43736
+ function getArcType(config) {
43737
+ if (!config.arcType) {
43738
+ return "nested"
43739
+ }
43740
+ switch (config.arcType) {
43741
+ case "chiapet":
43742
+ return "inView"
43743
+ case "chiapetoutbound":
43744
+ return "partialInView"
43745
+ default:
43746
+ return config.arcType
43747
+ }
43748
+ }
43176
43749
 
43177
43750
  class InteractionTrack extends TrackBase {
43178
43751
 
@@ -43187,13 +43760,14 @@ class InteractionTrack extends TrackBase {
43187
43760
  this.sinTheta = Math.sin(this.theta);
43188
43761
  this.cosTheta = Math.cos(this.theta);
43189
43762
  this.height = config.height || 250;
43190
- this.arcType = config.arcType || "nested"; // nested | proportional
43763
+ this.arcType = getArcType(config); // nested | proportional | inView | partialInView
43191
43764
  this.arcOrientation = (config.arcOrientation === undefined ? true : config.arcOrientation); // true for up, false for down
43192
43765
  this.showBlocks = config.showBlocks === undefined ? true : config.showBlocks;
43193
43766
  this.blockHeight = config.blockHeight || 3;
43194
43767
  this.thickness = config.thickness || 1;
43195
43768
  this.color = config.color || "rgb(180,25,137)";
43196
- this.alpha = config.alpha || 0.15;
43769
+ this.alpha = config.alpha || 0.02; // was: 0.15
43770
+ this.painter = {flipAxis: !this.arcOrientation, dataRange: this.dataRange, paintAxis: paintAxis};
43197
43771
 
43198
43772
  if (config.valueColumn) {
43199
43773
  this.valueColumn = config.valueColumn;
@@ -43258,6 +43832,8 @@ class InteractionTrack extends TrackBase {
43258
43832
 
43259
43833
  if (this.arcType === "proportional") {
43260
43834
  this.drawProportional(options);
43835
+ } else if (this.arcType === "inView" || this.arcType === "partialInView") {
43836
+ this.drawProportional(options);
43261
43837
  } else {
43262
43838
  this.drawNested(options);
43263
43839
  }
@@ -43289,6 +43865,9 @@ class InteractionTrack extends TrackBase {
43289
43865
 
43290
43866
  for (let feature of featureList) {
43291
43867
 
43868
+ // Reset transient property drawState. An undefined value => feature has not been drawn.
43869
+ feature.drawState = undefined;
43870
+
43292
43871
  let color = feature.color || this.color;
43293
43872
  if (color && this.config.useScore) {
43294
43873
  color = getAlphaColor(color, scoreShade(feature.score));
@@ -43358,7 +43937,10 @@ class InteractionTrack extends TrackBase {
43358
43937
  }
43359
43938
  const otherChr = feature.chr === feature.chr1 ? feature.chr2 : feature.chr1;
43360
43939
  ctx.strokeStyle = color;
43361
- ctx.fillStyle = color;
43940
+ // get a sense of trans "spread"
43941
+ ctx.fillStyle = getAlphaColor(getChrColor(otherChr), 0.5);
43942
+ // ctx.fillStyle = color
43943
+
43362
43944
  if (direction) {
43363
43945
  // UP
43364
43946
  ctx.fillRect(pixelStart, this.height / 2, w, this.height / 2);
@@ -43392,6 +43974,11 @@ class InteractionTrack extends TrackBase {
43392
43974
  }
43393
43975
  }
43394
43976
 
43977
+ getScaleFactor(min, max, height, logScale) {
43978
+ const scale = logScale ? height / (Math.log10(max + 1) - (min <= 0 ? 0 : Math.log10(min + 1))) : height / (max - min);
43979
+ return scale
43980
+ }
43981
+
43395
43982
  drawProportional(options) {
43396
43983
 
43397
43984
  const ctx = options.context;
@@ -43400,12 +43987,9 @@ class InteractionTrack extends TrackBase {
43400
43987
  const bpPerPixel = options.bpPerPixel;
43401
43988
  const bpStart = options.bpStart;
43402
43989
  const xScale = bpPerPixel;
43990
+ const refStart = options.referenceFrame.start;
43991
+ const refEnd = options.referenceFrame.end;
43403
43992
 
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
43993
 
43410
43994
  IGVGraphics.fillRect(ctx, 0, options.pixelTop, pixelWidth, pixelHeight, {'fillStyle': "rgb(255, 255, 255)"});
43411
43995
 
@@ -43413,37 +43997,65 @@ class InteractionTrack extends TrackBase {
43413
43997
 
43414
43998
  if (featureList && featureList.length > 0) {
43415
43999
 
43416
- const yScale = this.logScale ?
43417
- options.pixelHeight / Math.log10(this.dataRange.max + 1) :
43418
- options.pixelHeight / (this.dataRange.max - this.dataRange.min);
43419
-
44000
+ // we use the min as a filter but not moving the axis
44001
+ const effectiveMin = 0;
44002
+ const yScale = this.getScaleFactor(effectiveMin, this.dataRange.max, options.pixelHeight - 1, this.logScale);
43420
44003
  const y = this.arcOrientation ? options.pixelHeight : 0;
43421
44004
 
43422
44005
  for (let feature of featureList) {
43423
44006
 
44007
+ // Reset transient property drawState. An undefined value => feature has not been drawn.
44008
+ feature.drawState = undefined;
44009
+
43424
44010
  const value = this.valueColumn ? feature[this.valueColumn] : feature.score;
43425
44011
  if (value === undefined || Number.isNaN(value)) continue
43426
44012
 
43427
- const radiusY = this.logScale ?
43428
- Math.log10(value + 1) * yScale :
43429
- value * yScale;
44013
+ const radiusY = Math.round((this.logScale ? Math.log10(value + 1) : value) * yScale);
43430
44014
 
43431
44015
  if (feature.chr1 === feature.chr2 || feature.chr === 'all') {
43432
44016
 
43433
44017
  const {m1, m2} = getMidpoints(feature, this.browser.genome);
43434
44018
 
43435
- let pixelStart = (m1 - bpStart) / xScale;
43436
- let pixelEnd = (m2 - bpStart) / xScale;
44019
+ let pixelStart = Math.round((m1 - bpStart) / xScale);
44020
+ let pixelEnd = Math.round((m2 - bpStart) / xScale);
43437
44021
  let w = (pixelEnd - pixelStart);
43438
44022
  if (w < 3) {
43439
44023
  w = 3;
43440
44024
  pixelStart--;
43441
44025
  }
43442
44026
 
43443
- if (pixelEnd < 0 || pixelStart > pixelWidth || value < this.dataRange.min) continue
44027
+ // Various filters
44028
+ if (value < this.dataRange.min || value > this.dataRange.max) continue
44029
+ if ("proportional" !== this.arcType) {
44030
+ const showOutbound = (this.arcType === "partialInView");
44031
+ const within = (m1 >= refStart && m2 <= refEnd);
44032
+ let outBound = false;
44033
+ let inBound = false;
44034
+ if (!within && showOutbound) {
44035
+ outBound = (refStart <= m1 && m1 <= refEnd);
44036
+ if (!outBound) inBound = (refStart <= m2 && m2 <= refEnd);
44037
+ }
44038
+ if (!(within || outBound || inBound)) continue
44039
+ }
44040
+
43444
44041
 
43445
44042
  const radiusX = w / 2;
43446
44043
  const xc = pixelStart + w / 2;
44044
+ feature.drawState = {xc, yc: y, radiusX, radiusY};
44045
+
44046
+ // const arcKey = ((pixelStart << 16) | pixelEnd)
44047
+ // let arc = arcCaches.get(arcKey)
44048
+ // if (arc !== undefined) {
44049
+ // if (arc.has(radiusY)) {
44050
+ // continue
44051
+ // }
44052
+ // arc.add(radiusY)
44053
+ // } else {
44054
+ // let arcHeights = new Set()
44055
+ // arcHeights.add(radiusY)
44056
+ // arcCaches.set(arcKey, arcHeights)
44057
+ // }
44058
+
43447
44059
  const counterClockwise = this.arcOrientation ? true : false;
43448
44060
  const color = feature.color || this.color;
43449
44061
  ctx.strokeStyle = color;
@@ -43457,6 +44069,15 @@ class InteractionTrack extends TrackBase {
43457
44069
  ctx.stroke();
43458
44070
  }
43459
44071
 
44072
+ if (this.alpha) {
44073
+ ctx.fillStyle = getAlphaColor(color, this.alpha);
44074
+ if (true === ctx.isSVG) {
44075
+ ctx.fillEllipse(xc, y, radiusX, radiusY, 0, 0, Math.PI, counterClockwise);
44076
+ } else {
44077
+ ctx.fill();
44078
+ }
44079
+ }
44080
+
43460
44081
  if (this.showBlocks && feature.chr !== 'all') {
43461
44082
  ctx.fillStyle = color;
43462
44083
  const s1 = (feature.start1 - bpStart) / xScale;
@@ -43468,21 +44089,11 @@ class InteractionTrack extends TrackBase {
43468
44089
  ctx.fillRect(s2, y, e2 - s2, hb);
43469
44090
  }
43470
44091
 
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
44092
  } else {
44093
+ // Inter chromosome
43483
44094
  let pixelStart = Math.round((feature.start - bpStart) / xScale);
43484
44095
  let pixelEnd = Math.round((feature.end - bpStart) / xScale);
43485
- if (pixelEnd < 0 || pixelStart > pixelWidth || value < this.dataRange.min) continue
44096
+ if (pixelEnd < 0 || pixelStart > pixelWidth || value < this.dataRange.min || value > this.dataRange.max) continue
43486
44097
 
43487
44098
  const h = Math.min(radiusY, this.height - 13); // Leave room for text
43488
44099
  let w = (pixelEnd - pixelStart);
@@ -43493,6 +44104,8 @@ class InteractionTrack extends TrackBase {
43493
44104
  const otherChr = feature.chr === feature.chr1 ? feature.chr2 : feature.chr1;
43494
44105
  ctx.font = "8px sans-serif";
43495
44106
  ctx.textAlign = "center";
44107
+ // get a sense of trans "spread"
44108
+ ctx.fillStyle = getAlphaColor(getChrColor(otherChr), 0.5);
43496
44109
  if (this.arcOrientation) {
43497
44110
  // UP
43498
44111
  const y = this.height - h;
@@ -43509,6 +44122,26 @@ class InteractionTrack extends TrackBase {
43509
44122
  }
43510
44123
  }
43511
44124
 
44125
+ clearAxis(ctx, pixelWidth, pixelHeight) {
44126
+ IGVGraphics.fillRect(ctx, 0, 0, pixelWidth, pixelHeight, {'fillStyle': "rgb(255, 255, 255)"});
44127
+ }
44128
+
44129
+ paintAxis(ctx, pixelWidth, pixelHeight) {
44130
+ // dataRane is interpreted differently for interactino tracks -- all arcs are drawn from "zero", irrespective of dataRange.min
44131
+ const axisRange = {min: 0, max: this.dataRange.max};
44132
+ if (this.arcType === "proportional") {
44133
+ this.painter.flipAxis = !this.arcOrientation;
44134
+ this.painter.dataRange = axisRange;
44135
+ this.painter.paintAxis(ctx, pixelWidth, pixelHeight);
44136
+ } else if (this.arcType === "inView" || this.arcType === "partialInView") {
44137
+ this.painter.flipAxis = !this.arcOrientation;
44138
+ this.painter.dataRange = axisRange;
44139
+ this.painter.paintAxis(ctx, pixelWidth, pixelHeight);
44140
+ } else {
44141
+ this.clearAxis(ctx, pixelWidth, pixelHeight);
44142
+ }
44143
+ }
44144
+
43512
44145
  menuItemList() {
43513
44146
 
43514
44147
  let items = [
@@ -43525,10 +44158,13 @@ class InteractionTrack extends TrackBase {
43525
44158
  if (this.hasValue) {
43526
44159
  const lut =
43527
44160
  {
43528
- "nested": "Nested Arcs",
43529
- "proportional": "Proportional Arcs"
44161
+ "nested": "Nested",
44162
+ "proportional": "Proportional - All",
44163
+ "inView": "Proportional - Both Ends in View",
44164
+ "partialInView": "Proportional - One End in View"
43530
44165
  };
43531
- for (let arcType of ["nested", "proportional"]) {
44166
+ items.push("<b>Arc Type</b>");
44167
+ for (let arcType of ["nested", "proportional", "inView", "partialInView"]) {
43532
44168
  items.push(
43533
44169
  {
43534
44170
  object: $$1(createCheckbox$1(lut[arcType], arcType === this.arcType)),
@@ -43539,45 +44175,37 @@ class InteractionTrack extends TrackBase {
43539
44175
  });
43540
44176
  }
43541
44177
  }
44178
+ items.push("<hr/>");
43542
44179
 
43543
44180
  items.push({
43544
- object: $$1(createCheckbox$1("Show Blocks", this.showBlocks)),
44181
+ name: "Toggle arc direction",
43545
44182
  click: () => {
43546
- this.showBlocks = !this.showBlocks;
44183
+ this.arcOrientation = !this.arcOrientation;
43547
44184
  this.trackView.repaintViews();
43548
44185
  }
43549
44186
  });
43550
44187
  items.push({
43551
- name: "Toggle arc direction",
44188
+ name: this.showBlocks ? "Hide Blocks" : "Show Blocks",
43552
44189
  click: () => {
43553
- this.arcOrientation = !this.arcOrientation;
44190
+ this.showBlocks = !this.showBlocks;
43554
44191
  this.trackView.repaintViews();
43555
44192
  }
43556
44193
  });
43557
44194
 
43558
- if (this.arcType === "proportional") {
43559
- items.push("<HR>");
44195
+
44196
+ if (this.arcType === "proportional" || this.arcType === "inView" || this.arcType === "partialInView") {
44197
+ // MenuUtils.numericDataMenuItems(this.trackView).forEach(item => items.push(item))
43560
44198
  items = items.concat(MenuUtils.numericDataMenuItems(this.trackView));
43561
44199
  }
43562
44200
 
43563
44201
  if (this.browser.circularView && true === this.browser.circularViewVisible) {
44202
+ items.push('<hr/>');
43564
44203
  items.push({
43565
44204
  label: 'Add interactions to circular view',
43566
44205
  click: () => {
43567
-
43568
- const inView = [];
43569
44206
  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
- }
44207
+ this.addChordsForViewport(viewport.referenceFrame);
43576
44208
  }
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
44209
  }
43582
44210
  });
43583
44211
  }
@@ -43596,22 +44224,43 @@ class InteractionTrack extends TrackBase {
43596
44224
  label: 'Add interactions to circular view',
43597
44225
  click: () => {
43598
44226
  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});
44227
+ // first pass: to get all the relevant features
44228
+ this.addChordsForViewport(refFrame);
43606
44229
  }
43607
44230
  });
43608
44231
 
43609
44232
  list.push('<hr/>');
43610
44233
  return list
43611
44234
  }
43612
-
43613
44235
  }
43614
44236
 
44237
+ /**
44238
+ * Add chords to the circular view for the given viewport, represented by its reference frame
44239
+ * @param refFrame
44240
+ */
44241
+ addChordsForViewport(refFrame) {
44242
+ const cachedFeatures = "all" === refFrame.chr ?
44243
+ this.featureSource.getAllFeatures() :
44244
+ this.featureSource.featureCache.queryFeatures(refFrame.chr, refFrame.start, refFrame.end);
44245
+
44246
+ // inView features are simply features that have been drawn, i.e. have a drawState
44247
+ const inView = cachedFeatures.filter(f => f.drawState);
44248
+ if(inView.length === 0) erturn;
44249
+
44250
+ this.browser.circularViewVisible = true;
44251
+ const chords = makeBedPEChords(inView);
44252
+
44253
+ // for filtered set, distinguishing the chromosomes is more critical than tracks
44254
+ const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? this.color : getChrColor(refFrame.chr), 0.5);
44255
+ const trackColor = IGVColor.addAlpha(this.color, 0.5);
44256
+
44257
+ // name the chord set to include filtering information
44258
+ const encodedName = this.name.replaceAll(' ', '%20');
44259
+ const chordSetName = "all" === refFrame.chr ?
44260
+ encodedName :
44261
+ `${encodedName} (${refFrame.chr}:${refFrame.start}-${refFrame.end} ; range:${this.dataRange.min}-${this.dataRange.max})`;
44262
+ this.browser.circularView.addChords(chords, {track: chordSetName, color: chordSetColor, trackColor: trackColor});
44263
+ }
43615
44264
 
43616
44265
  doAutoscale(features) {
43617
44266
 
@@ -43678,10 +44327,12 @@ class InteractionTrack extends TrackBase {
43678
44327
  const featureList = features || clickState.viewport.getCachedFeatures();
43679
44328
  const candidates = [];
43680
44329
  if (featureList) {
43681
- const proportional = this.arcType === "proportional";
44330
+ const proportional = (this.arcType === "proportional" || this.arcType === "inView" || this.arcType === "partialInView");
43682
44331
 
43683
44332
  for (let feature of featureList) {
44333
+
43684
44334
  if (!feature.drawState) continue
44335
+
43685
44336
  if (feature.chr1 === feature.chr2 || feature.chr === 'all') {
43686
44337
  if (proportional) {
43687
44338
  //(x-xc)^2/radiusX^2 + (y-yc)^2/radiusY^2 <= 1
@@ -43719,6 +44370,26 @@ class InteractionTrack extends TrackBase {
43719
44370
  }
43720
44371
  return candidates.map((c) => c.feature)
43721
44372
  }
44373
+
44374
+ /**
44375
+ * Return the current state of the track. Used to create sessions and bookmarks.
44376
+ *
44377
+ * @returns {*|{}}
44378
+ */
44379
+ getState() {
44380
+
44381
+ const config = super.getState();
44382
+
44383
+ // if (this.height !== undefined) config.height = this.height;
44384
+ if (this.arcType !== undefined) config.arcType = this.arcType;
44385
+ if (this.arcOrientation !== undefined) config.arcOrientation = this.arcOrientation;
44386
+ if (this.showBlocks !== undefined) config.showBlocks = this.showBlocks;
44387
+ if (this.blockHeight !== undefined) config.blockHeight = this.blockHeight;
44388
+ if (this.thickness !== undefined) config.thickness = this.thickness;
44389
+ if (this.alpha !== undefined) config.alpha = this.alpha;
44390
+
44391
+ return config
44392
+ }
43722
44393
  }
43723
44394
 
43724
44395
  function getMidpoints(feature, genome) {
@@ -43782,29 +44453,93 @@ function getAlphaColor(color, alpha) {
43782
44453
 
43783
44454
 
43784
44455
  /**
43785
- * Called in the context of FeatureSource
44456
+ * Called in the context of FeatureSource (i.e. this == the feature source (a TextFeatureSource) for the track
44457
+ *
43786
44458
  * @param allFeatures
43787
44459
  * @returns {[]}
43788
44460
  */
43789
44461
  function getWGFeatures(allFeatures) {
43790
44462
 
44463
+ const makeWGFeature = (f) => {
44464
+ const wg = Object.assign({}, f);
44465
+ wg.chr = "all";
44466
+ wg.start = genome.getGenomeCoordinate(f.chr1, f.start1);
44467
+ wg.end = genome.getGenomeCoordinate(f.chr2, f.end2);
44468
+ return wg
44469
+ };
44470
+
43791
44471
  const genome = this.genome;
43792
- const wgFeatures = [];
44472
+
44473
+ // First pass -- find the max score feature
44474
+ let maxScoreFeature;
44475
+ let totalFeatureCount = 0;
43793
44476
  for (let c of genome.wgChromosomeNames) {
43794
- const chrFeatures = allFeatures[c];
44477
+ let chrFeatures = allFeatures[c];
43795
44478
  if (chrFeatures) {
43796
44479
  for (let f of chrFeatures) {
43797
44480
  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);
44481
+ totalFeatureCount++;
44482
+ if (f.score && (!maxScoreFeature || f.score > maxScoreFeature.score)) {
44483
+ maxScoreFeature = f;
44484
+ }
44485
+ }
44486
+ }
44487
+ }
44488
+ }
44489
+
44490
+ const maxCount = this.maxWGCount;
44491
+ const nBins = maxScoreFeature && maxScoreFeature.score > 0 && totalFeatureCount > maxCount ? 5 : 1; // TODO make a function of total # of features & maxCount?
44492
+ const featuresPerBin = Math.floor(maxCount / nBins);
44493
+ const binSize = maxScoreFeature && maxScoreFeature.score > 0 ? Math.log(maxScoreFeature.score) / nBins : Number.MAX_SAFE_INTEGER;
44494
+
44495
+ let binnedFeatures = [];
44496
+ let counts = [];
44497
+ for (let i = 0; i < nBins; i++) {
44498
+ counts.push([0]);
44499
+ binnedFeatures.push([]);
44500
+ }
44501
+
44502
+ for (let c of genome.wgChromosomeNames) {
44503
+ let chrFeatures = allFeatures[c];
44504
+ if (chrFeatures) {
44505
+ for (let f of chrFeatures) {
44506
+ if (!f.dup) {
44507
+ const bin = f.score ? Math.min(nBins - 1, Math.floor(Math.log(f.score) / binSize)) : 0;
44508
+ if (binnedFeatures[bin].length < featuresPerBin) {
44509
+ binnedFeatures[bin].push(makeWGFeature(f));
44510
+ } else {
44511
+ //Reservoir sampling
44512
+ const samplingProb = featuresPerBin / (counts[bin] + 1);
44513
+ if (Math.random() < samplingProb) {
44514
+ const idx = Math.floor(Math.random() * (featuresPerBin - 1));
44515
+ binnedFeatures[bin][idx] = makeWGFeature(f);
44516
+ }
44517
+ }
44518
+ counts[bin]++;
43803
44519
  }
43804
44520
  }
43805
44521
  }
43806
44522
  }
43807
44523
 
44524
+ let wgFeatures;
44525
+ if (nBins === 1) {
44526
+ wgFeatures = binnedFeatures[0];
44527
+ } else {
44528
+ wgFeatures = [];
44529
+ for (let bf of binnedFeatures) {
44530
+ for (let f of bf) wgFeatures.push(f);
44531
+ }
44532
+ // Keep the feature with max score
44533
+ if (maxScoreFeature) {
44534
+ wgFeatures.push(makeWGFeature(maxScoreFeature));
44535
+ }
44536
+ wgFeatures.sort(function (a, b) {
44537
+ return a.start - b.start
44538
+ });
44539
+ console.log(wgFeatures.length);
44540
+ }
44541
+
44542
+
43808
44543
  return wgFeatures
43809
44544
  }
43810
44545
 
@@ -44876,7 +45611,6 @@ class GWASTrack extends TrackBase {
44876
45611
  this.divider = config.divider || "rgb(225,225,225)";
44877
45612
  this.dotSize = config.dotSize || 3;
44878
45613
  this.popoverWindow = (config.popoverWindow === undefined ? DEFAULT_POPOVER_WINDOW : config.popoverWindow);
44879
- this.description = config.description; // might be null
44880
45614
 
44881
45615
  this.colorScales = config.color ?
44882
45616
  new ConstantColorScale(config.color) :
@@ -46428,12 +47162,6 @@ class SpliceJunctionTrack extends TrackBase {
46428
47162
  return data
46429
47163
  }
46430
47164
 
46431
-
46432
- description() {
46433
- return this.name
46434
-
46435
- }
46436
-
46437
47165
  /**
46438
47166
  * Called when the track is removed. Do any needed cleanup here
46439
47167
  */
@@ -47000,7 +47728,7 @@ class ReferenceFrame {
47000
47728
  this.end = end;
47001
47729
 
47002
47730
  this.bpPerPixel = bpPerPixel;
47003
- this.id = guid$1();
47731
+ this.id = guid$2();
47004
47732
  }
47005
47733
 
47006
47734
  calculateEnd(pixels) {
@@ -47722,7 +48450,7 @@ class CursorGuide {
47722
48450
 
47723
48451
  this.addMouseHandler(browser);
47724
48452
 
47725
- this.setVisibility(browser.config.showCursorTrackingGuide);
48453
+ this.setVisibility(browser.config.showCursorGuide);
47726
48454
 
47727
48455
  }
47728
48456
 
@@ -47739,35 +48467,40 @@ class CursorGuide {
47739
48467
  const target = document.elementFromPoint(event.clientX, event.clientY);
47740
48468
 
47741
48469
  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
48470
 
47748
- if (viewport && browser.getRulerTrackView()) {
48471
+ if (target.parentElement) {
47749
48472
 
47750
- this.verticalGuide.style.left = `${x}px`;
48473
+ if (target.parentElement.classList.contains('igv-viewport-content')) {
48474
+ viewport = target.parentElement.parentElement;
48475
+ } else if (target.parentElement.classList.contains('igv-viewport') && target.classList.contains('igv-viewport-content')) {
48476
+ viewport = target.parentElement;
48477
+ }
48478
+
48479
+ if (viewport && browser.getRulerTrackView()) {
48480
+
48481
+ this.verticalGuide.style.left = `${x}px`;
47751
48482
 
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;
48483
+ const columns = browser.root.querySelectorAll('.igv-column');
48484
+ let index = undefined;
48485
+ const viewportParent = viewport.parentElement;
48486
+ for (let i = 0; i < columns.length; i++) {
48487
+ if (undefined === index && viewportParent === columns[i]) {
48488
+ index = i;
48489
+ }
47758
48490
  }
47759
- }
47760
48491
 
47761
- const rulerViewport = browser.getRulerTrackView().viewports[index];
47762
- const result = rulerViewport.mouseMove(event);
48492
+ const rulerViewport = browser.getRulerTrackView().viewports[index];
48493
+ const result = rulerViewport.mouseMove(event);
47763
48494
 
47764
- if (result) {
48495
+ if (result) {
47765
48496
 
47766
- const {start, bp, end} = result;
47767
- const interpolant = (bp - start) / (end - start);
48497
+ const {start, bp, end} = result;
48498
+ const interpolant = (bp - start) / (end - start);
48499
+
48500
+ if (this.customMouseHandler) {
48501
+ this.customMouseHandler({start, bp, end, interpolant});
48502
+ }
47768
48503
 
47769
- if (this.customMouseHandler) {
47770
- this.customMouseHandler({start, bp, end, interpolant});
47771
48504
  }
47772
48505
 
47773
48506
  }
@@ -47781,8 +48514,8 @@ class CursorGuide {
47781
48514
  this.columnContainer.removeEventListener('mousemove', this.boundMouseMoveHandler);
47782
48515
  }
47783
48516
 
47784
- setVisibility(showCursorTrackingGuide) {
47785
- if (true === showCursorTrackingGuide) {
48517
+ setVisibility(showCursorGuide) {
48518
+ if (true === showCursorGuide) {
47786
48519
  this.show();
47787
48520
  } else {
47788
48521
  this.hide();
@@ -47953,50 +48686,52 @@ class CenterLineButton {
47953
48686
  * THE SOFTWARE.
47954
48687
  */
47955
48688
 
47956
- const TrackLabelControl = function (parent, browser) {
48689
+ class TrackLabelControl {
47957
48690
 
47958
- this.button = div$1({class: 'igv-navbar-button'});
47959
- parent.appendChild(this.button);
47960
- this.button.textContent = 'track labels';
48691
+ constructor(parent, browser) {
47961
48692
 
47962
- this.button.addEventListener('click', () => {
47963
- browser.trackLabelsVisible = !browser.trackLabelsVisible;
47964
- this.setState(browser.trackLabelsVisible);
47965
- browser.setTrackLabelVisibility(browser.trackLabelsVisible);
47966
- });
48693
+ this.button = div$1({class: 'igv-navbar-button'});
48694
+ parent.appendChild(this.button);
48695
+ this.button.textContent = 'track labels';
47967
48696
 
47968
- this.browser = browser;
48697
+ this.button.addEventListener('click', () => {
48698
+ browser.trackLabelsVisible = !browser.trackLabelsVisible;
48699
+ this.setState(browser.trackLabelsVisible);
48700
+ browser.setTrackLabelVisibility(browser.trackLabelsVisible);
48701
+ });
47969
48702
 
47970
- this.setVisibility(browser.config.showTrackLabelButton);
48703
+ this.browser = browser;
47971
48704
 
47972
- this.setState(browser.trackLabelsVisible);
48705
+ this.setVisibility(browser.config.showTrackLabelButton);
47973
48706
 
47974
- };
48707
+ this.setState(browser.trackLabelsVisible);
48708
+ }
47975
48709
 
47976
- TrackLabelControl.prototype.setVisibility = function (showTrackLabelButton) {
47977
- if (true === showTrackLabelButton) {
47978
- this.show();
47979
- } else {
47980
- this.hide();
48710
+ setVisibility(showTrackLabelButton) {
48711
+ if (true === showTrackLabelButton) {
48712
+ this.show();
48713
+ } else {
48714
+ this.hide();
48715
+ }
47981
48716
  }
47982
- };
47983
48717
 
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');
48718
+ setState(trackLabelsVisible) {
48719
+ if (true === trackLabelsVisible) {
48720
+ this.button.classList.add('igv-navbar-button-clicked');
48721
+ } else {
48722
+ this.button.classList.remove('igv-navbar-button-clicked');
48723
+ }
47989
48724
  }
47990
- };
47991
48725
 
47992
- TrackLabelControl.prototype.show = function () {
47993
- this.button.style.display = 'block';
47994
- this.setState(this.browser.trackLabelsVisible);
47995
- };
48726
+ show() {
48727
+ this.button.style.display = 'block';
48728
+ this.setState(this.browser.trackLabelsVisible);
48729
+ }
47996
48730
 
47997
- TrackLabelControl.prototype.hide = function () {
47998
- this.button.style.display = 'none';
47999
- };
48731
+ hide() {
48732
+ this.button.style.display = 'none';
48733
+ }
48734
+ }
48000
48735
 
48001
48736
  /*
48002
48737
  * The MIT License (MIT)
@@ -48281,55 +49016,6 @@ const SVGSaveControl = function (parent, browser) {
48281
49016
  button.addEventListener('click', () => browser.saveSVGtoFile({}));
48282
49017
  };
48283
49018
 
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
49019
  /*
48334
49020
  * The MIT License (MIT)
48335
49021
  *
@@ -48711,6 +49397,18 @@ CircularViewControl.prototype.hide = function () {
48711
49397
  this.button.style.display = 'none';
48712
49398
  };
48713
49399
 
49400
+ /**
49401
+ * User supplied button for the navbar
49402
+ */
49403
+
49404
+ const CustomButton = function (parent, browser, b) {
49405
+
49406
+ const button = div$1({class: 'igv-navbar-button'});
49407
+ parent.append(button);
49408
+ button.textContent = b.label;
49409
+ button.addEventListener('click', () => b.callback(browser));
49410
+ };
49411
+
48714
49412
  /*
48715
49413
  * The MIT License (MIT)
48716
49414
  *
@@ -48756,7 +49454,7 @@ class Browser {
48756
49454
  constructor(config, parentDiv) {
48757
49455
 
48758
49456
  this.config = config;
48759
- this.guid = guid$1();
49457
+ this.guid = guid$2();
48760
49458
  this.namespace = '.browser_' + this.guid;
48761
49459
 
48762
49460
  this.parent = parentDiv;
@@ -48808,7 +49506,7 @@ class Browser {
48808
49506
 
48809
49507
  this.isCenterLineVisible = config.showCenterGuide;
48810
49508
 
48811
- this.cursorGuideVisible = config.showCursorTrackingGuide;
49509
+ this.cursorGuideVisible = config.showCursorGuide;
48812
49510
 
48813
49511
  this.showSampleNames = config.showSampleNames;
48814
49512
  this.showSampleNameButton = config.showSampleNameButton;
@@ -48921,6 +49619,12 @@ class Browser {
48921
49619
  this.svgSaveControl = new SVGSaveControl($toggle_button_container.get(0), this);
48922
49620
  }
48923
49621
 
49622
+ if(config.customButtons) {
49623
+ for(let b of config.customButtons) {
49624
+ new CustomButton($toggle_button_container.get(0), this, b);
49625
+ }
49626
+ }
49627
+
48924
49628
  this.zoomWidget = new ZoomWidget(this, $navbarRightContainer.get(0));
48925
49629
 
48926
49630
  if (false === config.showNavigation) {
@@ -48928,13 +49632,13 @@ class Browser {
48928
49632
  }
48929
49633
 
48930
49634
  this.inputDialog = new InputDialog(this.root);
48931
- this.inputDialog.container.id = `igv-input-dialog-${guid$1()}`;
49635
+ this.inputDialog.container.id = `igv-input-dialog-${guid$2()}`;
48932
49636
 
48933
49637
  this.dataRangeDialog = new DataRangeDialog($$1(this.root));
48934
- this.dataRangeDialog.$container.get(0).id = `igv-data-range-dialog-${guid$1()}`;
49638
+ this.dataRangeDialog.$container.get(0).id = `igv-data-range-dialog-${guid$2()}`;
48935
49639
 
48936
49640
  this.genericColorPicker = new GenericColorPicker({parent: this.columnContainer, width: 432});
48937
- this.genericColorPicker.container.id = `igv-track-color-picker-${guid$1()}`;
49641
+ this.genericColorPicker.container.id = `igv-track-color-picker-${guid$2()}`;
48938
49642
 
48939
49643
  return $navBar
48940
49644
 
@@ -49963,46 +50667,6 @@ class Browser {
49963
50667
 
49964
50668
  }
49965
50669
 
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
50670
  async selectMultiLocusPanel(referenceFrame) {
50007
50671
 
50008
50672
  const referenceFrameIndex = this.referenceFrameList.indexOf(referenceFrame);
@@ -50226,6 +50890,7 @@ class Browser {
50226
50890
  // Build locus array (multi-locus view). Use the first track to extract the loci, any track could be used.
50227
50891
  const locus = [];
50228
50892
  const gtexSelections = {};
50893
+ let hasGtexSelections = false;
50229
50894
  let anyTrackView = this.trackViews[0];
50230
50895
  for (let {referenceFrame} of anyTrackView.viewports) {
50231
50896
  const locusString = referenceFrame.getLocusString();
@@ -50236,12 +50901,11 @@ class Browser {
50236
50901
  snp: referenceFrame.selection.snp
50237
50902
  };
50238
50903
  gtexSelections[locusString] = selection;
50904
+ hasGtexSelections = true;
50239
50905
  }
50240
50906
  }
50241
50907
  json["locus"] = locus.length === 1 ? locus[0] : locus;
50242
-
50243
- const gtexKeys = Object.getOwnPropertyNames(gtexSelections);
50244
- if (gtexKeys.length > 0) {
50908
+ if (hasGtexSelections) {
50245
50909
  json["gtexSelections"] = gtexSelections;
50246
50910
  }
50247
50911
 
@@ -50265,27 +50929,21 @@ class Browser {
50265
50929
  trackJson.push(config);
50266
50930
  }
50267
50931
  } catch (e) {
50268
- errors.push(e);
50932
+ console.error(`Track: ${track.name}: ${e}`);
50933
+ errors.push(`Track: ${track.name}: ${e}`);
50269
50934
  }
50270
50935
  }
50271
50936
 
50272
50937
  if (errors.length > 0) {
50273
50938
  let n = 1;
50274
- let message = 'Errors encountered saving session:';
50939
+ let message = 'Errors encountered saving session: </br>';
50275
50940
  for (let e of errors) {
50276
- message += ` (${n++}) ${e.toString()}.`;
50941
+ message += ` (${n++}) ${e.toString()} <br/>`;
50277
50942
  }
50278
50943
  throw Error(message)
50279
50944
  }
50280
50945
 
50281
50946
 
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
50947
 
50290
50948
  json["tracks"] = trackJson;
50291
50949
 
@@ -50673,7 +51331,7 @@ async function createBrowser(parentDiv, config) {
50673
51331
 
50674
51332
  setDefaults(config);
50675
51333
 
50676
- if (config.queryParametersSupported !== false) {
51334
+ if (config.queryParametersSupported) {
50677
51335
  extractQuery(config);
50678
51336
  }
50679
51337
  if (config.apiKey) {
@@ -50737,10 +51395,6 @@ async function visibilityChange() {
50737
51395
 
50738
51396
  function setDefaults(config) {
50739
51397
 
50740
- if (undefined === config.promisified) {
50741
- config.promisified = false;
50742
- }
50743
-
50744
51398
  if (undefined === config.minimumBases) {
50745
51399
  config.minimumBases = 40;
50746
51400
  }
@@ -50769,8 +51423,9 @@ function setDefaults(config) {
50769
51423
  config.showCursorTrackingGuideButton = true;
50770
51424
  }
50771
51425
 
50772
- if (undefined === config.showCursorTrackingGuide) {
50773
- config.showCursorTrackingGuide = false;
51426
+
51427
+ if (undefined === config.showCursorGuide) {
51428
+ config.showCursorGuide = config.showCursorTrackingGuide || false; // showCursorTrackingGuide is a synonym
50774
51429
  }
50775
51430
 
50776
51431
  if (undefined === config.showCenterGuideButton) {
@@ -50861,6 +51516,8 @@ function extractQuery(config) {
50861
51516
  config[key] = value;
50862
51517
  }
50863
51518
  i = j + 1;
51519
+ } else {
51520
+ i++;
50864
51521
  }
50865
51522
  }
50866
51523
  }
@@ -50897,7 +51554,7 @@ async function createTrack(config, browser) {
50897
51554
 
50898
51555
  function embedCSS() {
50899
51556
 
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';
51557
+ 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
51558
 
50902
51559
  var style = document.createElement('style');
50903
51560
  style.setAttribute('type', 'text/css');