igv 2.11.0 → 2.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/igv.js CHANGED
@@ -17943,7 +17943,7 @@
17943
17943
  * @constructor
17944
17944
  */
17945
17945
 
17946
- class FeatureCache {
17946
+ class FeatureCache$1 {
17947
17947
  constructor(featureList, genome, range) {
17948
17948
  featureList = featureList || [];
17949
17949
  this.treeMap = this.buildTreeMap(featureList, genome);
@@ -19385,7 +19385,7 @@
19385
19385
  }
19386
19386
 
19387
19387
  function embedCSS$2() {
19388
- 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';
19388
+ var css = '.igv-ui-popover {\n cursor: default;\n position: absolute;\n z-index: 2048;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: 1px;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n background-color: white; }\n .igv-ui-popover > div:first-child {\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee; }\n .igv-ui-popover > div:first-child > div:first-child {\n margin-left: 4px; }\n .igv-ui-popover > div:first-child > div:last-child {\n margin-right: 4px;\n height: 12px;\n width: 12px;\n color: #7F7F7F; }\n .igv-ui-popover > div:first-child > div:last-child:hover {\n cursor: pointer;\n color: #444; }\n .igv-ui-popover > div:last-child {\n overflow-y: auto;\n overflow-x: hidden;\n max-height: 400px;\n max-width: 800px;\n background-color: white; }\n .igv-ui-popover > div:last-child > div {\n -webkit-user-select: text;\n -moz-user-select: text;\n -ms-user-select: text;\n user-select: text;\n margin-left: 4px;\n margin-right: 4px;\n min-width: 220px;\n overflow-x: hidden;\n text-overflow: ellipsis;\n white-space: nowrap; }\n .igv-ui-popover > div:last-child > div > span {\n font-weight: bolder; }\n .igv-ui-popover > div:last-child hr {\n width: 100%; }\n\n.igv-ui-alert-dialog-container {\n box-sizing: content-box;\n position: absolute;\n z-index: 2048;\n top: 50%;\n left: 50%;\n width: 400px;\n height: 200px;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n outline: none;\n font-family: \"Open Sans\", sans-serif;\n font-size: 15px;\n font-weight: 400;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center; }\n .igv-ui-alert-dialog-container > div:first-child {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee; }\n .igv-ui-alert-dialog-container > div:first-child div:first-child {\n padding-left: 8px; }\n .igv-ui-alert-dialog-container .igv-ui-alert-dialog-body {\n -webkit-user-select: text;\n -moz-user-select: text;\n -ms-user-select: text;\n user-select: text;\n color: #373737;\n width: 100%;\n height: calc(100% - 24px - 64px);\n overflow-y: scroll; }\n .igv-ui-alert-dialog-container .igv-ui-alert-dialog-body .igv-ui-alert-dialog-body-copy {\n margin: 16px;\n width: auto;\n height: auto;\n overflow-wrap: break-word;\n word-break: break-word;\n background-color: white;\n border: unset; }\n .igv-ui-alert-dialog-container > div:last-child {\n width: 100%;\n margin-bottom: 10px;\n background-color: white;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: center;\n align-items: center; }\n .igv-ui-alert-dialog-container > div:last-child div {\n margin: unset;\n width: 40px;\n height: 30px;\n line-height: 30px;\n text-align: center;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n font-weight: 400;\n border-color: #2B81AF;\n border-style: solid;\n border-width: thin;\n border-radius: 4px;\n background-color: #2B81AF; }\n .igv-ui-alert-dialog-container > div:last-child div:hover {\n cursor: pointer;\n border-color: #25597f;\n background-color: #25597f; }\n\n.igv-ui-color-swatch {\n position: relative;\n box-sizing: content-box;\n display: flex;\n flex-flow: row;\n flex-wrap: wrap;\n justify-content: center;\n align-items: center;\n width: 32px;\n height: 32px;\n border-style: solid;\n border-width: 2px;\n border-color: white;\n border-radius: 4px; }\n\n.igv-ui-color-swatch:hover {\n border-color: dimgray; }\n\n.igv-ui-colorpicker-menu-close-button {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 32px;\n margin-top: 4px;\n margin-bottom: 4px;\n padding-right: 8px; }\n .igv-ui-colorpicker-menu-close-button i.fa {\n display: block;\n margin-left: 4px;\n margin-right: 4px;\n color: #5f5f5f; }\n .igv-ui-colorpicker-menu-close-button i.fa:hover,\n .igv-ui-colorpicker-menu-close-button i.fa:focus,\n .igv-ui-colorpicker-menu-close-button i.fa:active {\n cursor: pointer;\n color: #0f0f0f; }\n\n.igv-ui-generic-dialog-container {\n box-sizing: content-box;\n position: fixed;\n top: 0;\n left: 0;\n width: 300px;\n height: 200px;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n z-index: 2048;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-header {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-header div {\n margin-right: 4px;\n margin-bottom: 2px;\n height: 12px;\n width: 12px;\n color: #7F7F7F; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-header div:hover {\n cursor: pointer;\n color: #444; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-one-liner {\n color: #373737;\n width: 95%;\n height: 24px;\n line-height: 24px;\n text-align: left;\n margin-top: 8px;\n padding-left: 8px;\n overflow-wrap: break-word;\n background-color: white; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input {\n margin-top: 8px;\n width: 95%;\n height: 24px;\n color: #373737;\n line-height: 24px;\n padding-left: 8px;\n background-color: white;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input div {\n width: 30%;\n height: 100%;\n font-size: 16px;\n text-align: right;\n padding-right: 8px;\n background-color: white; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input input {\n display: block;\n height: 100%;\n width: 100%;\n padding-left: 4px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n color: #373737;\n text-align: left;\n outline: none;\n border-style: solid;\n border-width: thin;\n border-color: #7F7F7F;\n background-color: white; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input input {\n width: 50%;\n font-size: 16px; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-input {\n margin-top: 8px;\n width: calc(100% - 16px);\n height: 24px;\n color: #373737;\n line-height: 24px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-input input {\n display: block;\n height: 100%;\n width: 100%;\n padding-left: 4px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n color: #373737;\n text-align: left;\n outline: none;\n border-style: solid;\n border-width: thin;\n border-color: #7F7F7F;\n background-color: white; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-input input {\n font-size: 16px; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel {\n width: 100%;\n height: 28px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div {\n margin-top: 32px;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n width: 75px;\n height: 28px;\n line-height: 28px;\n text-align: center;\n border-color: transparent;\n border-style: solid;\n border-width: thin;\n border-radius: 2px; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div:first-child {\n margin-left: 32px;\n margin-right: 0;\n background-color: #5ea4e0; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div:last-child {\n margin-left: 0;\n margin-right: 32px;\n background-color: #c4c4c4; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div:first-child:hover {\n cursor: pointer;\n background-color: #3b5c7f; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div:last-child:hover {\n cursor: pointer;\n background-color: #7f7f7f; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok {\n width: 100%;\n height: 36px;\n margin-top: 32px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok div {\n width: 98px;\n height: 36px;\n line-height: 36px;\n text-align: center;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n border-color: white;\n border-style: solid;\n border-width: thin;\n border-radius: 4px;\n background-color: #2B81AF; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok div:hover {\n cursor: pointer;\n background-color: #25597f; }\n\n.igv-ui-generic-container {\n box-sizing: content-box;\n position: absolute;\n z-index: 2048;\n background-color: white;\n cursor: pointer;\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n justify-content: flex-start;\n align-items: center; }\n .igv-ui-generic-container > div:first-child {\n cursor: move;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n height: 24px;\n width: 100%;\n background-color: #dddddd; }\n .igv-ui-generic-container > div:first-child > div {\n display: block;\n color: #5f5f5f;\n cursor: pointer;\n width: 14px;\n height: 14px;\n margin-right: 8px;\n margin-bottom: 4px; }\n\n.igv-ui-dialog {\n z-index: 2048;\n position: fixed;\n width: fit-content;\n height: fit-content;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n background-color: white;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400; }\n .igv-ui-dialog .igv-ui-dialog-header {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee; }\n .igv-ui-dialog .igv-ui-dialog-header div {\n margin-right: 4px;\n margin-bottom: 2px;\n height: 12px;\n width: 12px;\n color: #7F7F7F; }\n .igv-ui-dialog .igv-ui-dialog-header div:hover {\n cursor: pointer;\n color: #444; }\n .igv-ui-dialog .igv-ui-dialog-one-liner {\n width: 95%;\n height: 24px;\n line-height: 24px;\n text-align: left;\n margin: 8px;\n overflow-wrap: break-word;\n background-color: white;\n font-weight: bold; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel {\n width: 100%;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel div {\n margin: 16px;\n margin-top: 32px;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n width: 75px;\n height: 28px;\n line-height: 28px;\n text-align: center;\n border-color: transparent;\n border-style: solid;\n border-width: thin;\n border-radius: 2px; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel div:first-child {\n background-color: #5ea4e0; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel div:last-child {\n background-color: #c4c4c4; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel div:first-child:hover {\n cursor: pointer;\n background-color: #3b5c7f; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel div:last-child:hover {\n cursor: pointer;\n background-color: #7f7f7f; }\n .igv-ui-dialog .igv-ui-dialog-ok {\n width: 100%;\n height: 36px;\n margin-top: 32px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-ui-dialog .igv-ui-dialog-ok div {\n width: 98px;\n height: 36px;\n line-height: 36px;\n text-align: center;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n border-color: white;\n border-style: solid;\n border-width: thin;\n border-radius: 4px;\n background-color: #2B81AF; }\n .igv-ui-dialog .igv-ui-dialog-ok div:hover {\n cursor: pointer;\n background-color: #25597f; }\n\n.igv-ui-panel, .igv-ui-panel-column, .igv-ui-panel-row {\n z-index: 2048;\n background-color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n display: flex;\n justify-content: flex-start;\n align-items: flex-start; }\n\n.igv-ui-panel-column {\n display: flex;\n flex-direction: column; }\n\n.igv-ui-panel-row {\n display: flex;\n flex-direction: row; }\n\n.igv-ui-textbox {\n background-color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n display: flex;\n justify-content: flex-start;\n align-items: flex-start; }\n\n/*# sourceMappingURL=igv-ui.css.map */\n';
19389
19389
  var style = document.createElement('style');
19390
19390
  style.setAttribute('type', 'text/css');
19391
19391
  style.innerHTML = css;
@@ -19911,7 +19911,7 @@
19911
19911
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19912
19912
  * THE SOFTWARE.
19913
19913
  */
19914
- const knownFileExtensions = new Set(["narrowpeak", "broadpeak", "regionpeak", "peaks", "bedgraph", "wig", "gff3", "gff", "gtf", "fusionjuncspan", "refflat", "seg", "aed", "bed", "vcf", "bb", "bigbed", "bw", "bigwig", "bam", "tdf", "refgene", "genepred", "genepredext", "bedpe", "bp", "snp", "rmsk", "cram", "gwas", "maf", "mut"]);
19914
+ const knownFileExtensions = new Set(["narrowpeak", "broadpeak", "regionpeak", "peaks", "bedgraph", "wig", "gff3", "gff", "gtf", "fusionjuncspan", "refflat", "seg", "aed", "bed", "vcf", "bb", "bigbed", "biginteract", "bw", "bigwig", "bam", "tdf", "refgene", "genepred", "genepredext", "bedpe", "bp", "snp", "rmsk", "cram", "gwas", "maf", "mut"]);
19915
19915
  /**
19916
19916
  * Return a custom format object with the given name.
19917
19917
  * @param name
@@ -20043,6 +20043,7 @@
20043
20043
  case "bed":
20044
20044
  case "bigbed":
20045
20045
  case "bb":
20046
+ case "biginteract":
20046
20047
  return "bedtype";
20047
20048
 
20048
20049
  default:
@@ -20732,14 +20733,13 @@
20732
20733
  console.log('Viewport - draw(drawConfiguration, features, roiFeatures)');
20733
20734
  }
20734
20735
 
20735
- checkContentHeight() {
20736
+ checkContentHeight(features) {
20736
20737
  let track = this.trackView.track;
20738
+ features = features || this.cachedFeatures;
20737
20739
 
20738
20740
  if ("FILL" === track.displayMode) {
20739
20741
  this.setContentHeight(this.$viewport.height());
20740
20742
  } else if (typeof track.computePixelHeight === 'function') {
20741
- let features = this.cachedFeatures;
20742
-
20743
20743
  if (features && features.length > 0) {
20744
20744
  let requiredContentHeight = track.computePixelHeight(features);
20745
20745
  let currentContentHeight = this.$content.height();
@@ -20759,7 +20759,7 @@
20759
20759
  // Maximum height of a canvas is ~32,000 pixels on Chrome, possibly smaller on other platforms
20760
20760
  contentHeight = Math.min(contentHeight, 32000);
20761
20761
  this.$content.height(contentHeight);
20762
- if (this.tile) this.tile.invalidate = true;
20762
+ if (this.canvas._data) this.canvas._data.invalidate = true;
20763
20763
  }
20764
20764
 
20765
20765
  isLoading() {
@@ -20774,8 +20774,6 @@
20774
20774
 
20775
20775
  setWidth(width) {
20776
20776
  this.$viewport.width(width);
20777
- this.canvas.style.width = `${width}px`;
20778
- this.canvas.setAttribute('width', width);
20779
20777
  }
20780
20778
 
20781
20779
  getWidth() {
@@ -23060,7 +23058,7 @@
23060
23058
  }
23061
23059
  };
23062
23060
 
23063
- const _version = "2.11.0";
23061
+ const _version = "2.11.1";
23064
23062
 
23065
23063
  function version$1() {
23066
23064
  return _version;
@@ -23577,16 +23575,20 @@
23577
23575
 
23578
23576
  checkZoomIn() {
23579
23577
  const showZoomInNotice = () => {
23580
- const referenceFrame = this.referenceFrame;
23581
-
23582
23578
  if (this.referenceFrame.chr.toLowerCase() === "all" && !this.trackView.track.supportsWholeGenome()) {
23583
23579
  return true;
23584
23580
  } else {
23585
23581
  const visibilityWindow = this.trackView.track.visibilityWindow;
23586
- return visibilityWindow !== undefined && visibilityWindow > 0 && referenceFrame.bpPerPixel * this.$viewport.width() > visibilityWindow;
23582
+ return visibilityWindow !== undefined && visibilityWindow > 0 && this.referenceFrame.bpPerPixel * this.$viewport.width() > visibilityWindow;
23587
23583
  }
23588
23584
  };
23589
23585
 
23586
+ if (this.trackView.track && "sequence" === this.trackView.track.type && this.referenceFrame.bpPerPixel > 1) {
23587
+ if (this.canvas) ;
23588
+
23589
+ return false;
23590
+ }
23591
+
23590
23592
  if (!this.viewIsReady()) {
23591
23593
  return false;
23592
23594
  }
@@ -23596,7 +23598,8 @@
23596
23598
  // Out of visibility window
23597
23599
  if (this.canvas) {
23598
23600
  this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
23599
- this.tile = undefined;
23601
+ this.canvas._data = undefined;
23602
+ this.featureCache = undefined;
23600
23603
  }
23601
23604
 
23602
23605
  this.$zoomInNotice.show();
@@ -23615,14 +23618,17 @@
23615
23618
 
23616
23619
  return true;
23617
23620
  }
23621
+ /**
23622
+ * Adjust the canvas to the current genomic state.
23623
+ */
23624
+
23618
23625
 
23619
23626
  shift() {
23620
- const self = this;
23621
- const referenceFrame = self.referenceFrame;
23627
+ const referenceFrame = this.referenceFrame;
23622
23628
 
23623
- if (self.canvas && self.tile && self.tile.chr === self.referenceFrame.chr && self.tile.bpPerPixel === referenceFrame.bpPerPixel) {
23624
- const pixelOffset = Math.round((self.tile.startBP - referenceFrame.start) / referenceFrame.bpPerPixel);
23625
- self.canvas.style.left = pixelOffset + "px";
23629
+ if (this.canvas && this.canvas._data && this.canvas._data.chr === this.referenceFrame.chr && this.canvas._data.bpPerPixel === referenceFrame.bpPerPixel) {
23630
+ const pixelOffset = Math.round((this.canvas._data.startBP - referenceFrame.start) / referenceFrame.bpPerPixel);
23631
+ this.canvas.style.left = pixelOffset + "px";
23626
23632
  }
23627
23633
  }
23628
23634
 
@@ -23648,9 +23654,10 @@
23648
23654
  this.startSpinner();
23649
23655
 
23650
23656
  try {
23651
- const features = await this.getFeatures(this.trackView.track, chr, bpStart, bpEnd, referenceFrame.bpPerPixel);
23657
+ const track = this.trackView.track;
23658
+ const features = await this.getFeatures(track, chr, bpStart, bpEnd, referenceFrame.bpPerPixel);
23652
23659
  let roiFeatures = [];
23653
- const roi = mergeArrays(this.browser.roi, this.trackView.track.roi);
23660
+ const roi = mergeArrays(this.browser.roi, track.roi);
23654
23661
 
23655
23662
  if (roi) {
23656
23663
  for (let r of roi) {
@@ -23662,11 +23669,13 @@
23662
23669
  }
23663
23670
  }
23664
23671
 
23665
- this.tile = new Tile(chr, bpStart, bpEnd, referenceFrame.bpPerPixel, features, roiFeatures);
23672
+ const mr = track && ("wig" === track.type || "merged" === track.type); // wig tracks are potentially multiresolution (e.g. bigwig)
23673
+
23674
+ this.featureCache = new FeatureCache(chr, bpStart, bpEnd, referenceFrame.bpPerPixel, features, roiFeatures, mr);
23666
23675
  this.loading = false;
23667
23676
  this.hideMessage();
23668
23677
  this.stopSpinner();
23669
- return this.tile;
23678
+ return this.featureCache;
23670
23679
  } catch (error) {
23671
23680
  // Track might have been removed during load
23672
23681
  if (this.trackView && this.trackView.disposed !== true) {
@@ -23679,30 +23688,34 @@
23679
23688
  this.stopSpinner();
23680
23689
  }
23681
23690
  }
23691
+ /**
23692
+ * Repaint the canvas for the current genomic state.
23693
+ *
23694
+ * @returns {Promise<void>}
23695
+ */
23682
23696
 
23683
- async repaint() {
23684
- if (undefined === this.tile) {
23697
+
23698
+ repaint() {
23699
+ if (undefined === this.featureCache) {
23685
23700
  return;
23686
23701
  }
23687
23702
 
23688
23703
  let {
23689
23704
  features,
23690
- roiFeatures,
23691
- bpPerPixel,
23692
- startBP,
23693
- endBP
23694
- } = this.tile; // const isWGV = GenomeUtils.isWholeGenomeView(this.browser.referenceFrameList[0].chr)
23705
+ roiFeatures
23706
+ } = this.featureCache; //this.tile.bpPerPixel = this.referenceFrame.bpPerPixel
23707
+ // const isWGV = GenomeUtils.isWholeGenomeView(this.browser.referenceFrameList[0].chr)
23695
23708
 
23696
23709
  const isWGV = GenomeUtils.isWholeGenomeView(this.referenceFrame.chr);
23697
23710
  let pixelWidth;
23711
+ const startBP = this.featureCache.startBP;
23712
+ const endBP = this.featureCache.endBP;
23713
+ let bpPerPixel = this.referenceFrame.bpPerPixel;
23698
23714
 
23699
23715
  if (isWGV) {
23700
- bpPerPixel = this.referenceFrame.end / this.$viewport.width();
23701
- startBP = 0;
23702
- endBP = this.referenceFrame.end;
23703
23716
  pixelWidth = this.$viewport.width();
23704
23717
  } else {
23705
- pixelWidth = Math.ceil((endBP - startBP) / bpPerPixel);
23718
+ pixelWidth = 3 * this.$viewport.width();
23706
23719
  } // For deep tracks we paint a canvas == 3*viewportHeight centered on the current vertical scroll position
23707
23720
 
23708
23721
 
@@ -23755,21 +23768,33 @@
23755
23768
  viewport: this,
23756
23769
  viewportWidth: this.$viewport.width()
23757
23770
  };
23758
- this.draw(drawConfiguration, features, roiFeatures);
23759
- this.canvasVerticalRange = {
23771
+ this.draw(drawConfiguration, features, roiFeatures); // Attach metadata to canvas
23772
+
23773
+ newCanvas._data = {
23774
+ chr: this.referenceFrame.chr,
23775
+ startBP,
23776
+ endBP,
23777
+ bpPerPixel,
23760
23778
  top: canvasTop,
23761
23779
  bottom: canvasTop + pixelHeight
23762
23780
  };
23763
23781
 
23764
- if (this.$canvas) {
23765
- this.$canvas.remove();
23782
+ if (this.canvas) {
23783
+ $$1(this.canvas).remove();
23766
23784
  }
23767
23785
 
23768
- this.$canvas = $$1(newCanvas);
23769
- this.$content.append(this.$canvas);
23770
23786
  this.canvas = newCanvas;
23771
23787
  this.ctx = ctx;
23788
+ this.$content.append($$1(newCanvas));
23772
23789
  }
23790
+ /**
23791
+ * Draw the associated track.
23792
+ *
23793
+ * @param drawConfiguration
23794
+ * @param features
23795
+ * @param roiFeatures
23796
+ */
23797
+
23773
23798
 
23774
23799
  draw(drawConfiguration, features, roiFeatures) {
23775
23800
  // console.log(`${ Date.now() } viewport draw(). track ${ this.trackView.track.type }. content-css-top ${ this.$content.css('top') }. canvas-top ${ drawConfiguration.pixelTop }.`)
@@ -23784,51 +23809,6 @@
23784
23809
  r.track.draw(drawConfiguration);
23785
23810
  }
23786
23811
  }
23787
- } // TODO: Nolonger used. Will discard
23788
-
23789
-
23790
- async toSVG(tile) {
23791
- // Nothing to do if zoomInNotice is active
23792
- if (this.$zoomInNotice && this.$zoomInNotice.is(":visible")) {
23793
- return;
23794
- }
23795
-
23796
- const referenceFrame = this.referenceFrame;
23797
- const bpPerPixel = tile.bpPerPixel;
23798
- const features = tile.features;
23799
- const roiFeatures = tile.roiFeatures;
23800
- const pixelWidth = this.$viewport.width();
23801
- const pixelHeight = this.$viewport.height();
23802
- const bpStart = referenceFrame.start;
23803
- const bpEnd = referenceFrame.start + pixelWidth * referenceFrame.bpPerPixel;
23804
- const ctx$1 = new ctx({
23805
- // svg
23806
- width: pixelWidth,
23807
- height: pixelHeight,
23808
- viewbox: {
23809
- x: 0,
23810
- y: -this.$content.position().top,
23811
- width: pixelWidth,
23812
- height: pixelHeight
23813
- }
23814
- });
23815
- const drawConfiguration = {
23816
- viewport: this,
23817
- context: ctx$1,
23818
- top: -this.$content.position().top,
23819
- pixelTop: 0,
23820
- // for compatibility with canvas draw
23821
- pixelWidth,
23822
- pixelHeight,
23823
- bpStart,
23824
- bpEnd,
23825
- bpPerPixel,
23826
- referenceFrame: this.referenceFrame,
23827
- selection: this.selection,
23828
- viewportWidth: pixelWidth
23829
- };
23830
- this.draw(drawConfiguration, features, roiFeatures);
23831
- return ctx$1.getSerializedSvg(true);
23832
23812
  }
23833
23813
 
23834
23814
  containsPosition(chr, position) {
@@ -23843,9 +23823,10 @@
23843
23823
  return this.loading;
23844
23824
  }
23845
23825
 
23846
- saveImage() {
23826
+ savePNG() {
23847
23827
  if (!this.ctx) return;
23848
- const canvasTop = this.canvasVerticalRange ? this.canvasVerticalRange.top : 0;
23828
+ const canvasMetadata = this.canvas._data;
23829
+ const canvasTop = canvasMetadata ? canvasMetadata.top : 0;
23849
23830
  const devicePixelRatio = window.devicePixelRatio;
23850
23831
  const w = this.$viewport.width() * devicePixelRatio;
23851
23832
  const h = this.$viewport.height() * devicePixelRatio;
@@ -23967,23 +23948,22 @@
23967
23948
  viewportWidth: width,
23968
23949
  selection: this.selection
23969
23950
  };
23970
- const features = this.tile ? this.tile.features : [];
23971
- const roiFeatures = this.tile ? this.tile.roiFeatures : undefined;
23951
+ const features = this.featureCache ? this.featureCache.features : [];
23952
+ const roiFeatures = this.featureCache ? this.featureCache.roiFeatures : undefined;
23972
23953
  this.draw(config, features, roiFeatures);
23973
23954
  context.restore();
23974
23955
  }
23975
23956
 
23976
- getCachedFeatures() {
23977
- return this.tile ? this.tile.features : [];
23957
+ get cachedFeatures() {
23958
+ return this.featureCache ? this.featureCache.features : [];
23978
23959
  }
23979
23960
 
23980
23961
  async getFeatures(track, chr, start, end, bpPerPixel) {
23981
- if (this.tile && this.tile.containsRange(chr, start, end, bpPerPixel)) {
23982
- return this.tile.features;
23962
+ if (this.featureCache && this.featureCache.containsRange(chr, start, end, bpPerPixel)) {
23963
+ return this.featureCache.features;
23983
23964
  } else if (typeof track.getFeatures === "function") {
23984
23965
  const features = await track.getFeatures(chr, start, end, bpPerPixel, this);
23985
- this.cachedFeatures = features;
23986
- this.checkContentHeight();
23966
+ this.checkContentHeight(features);
23987
23967
  return features;
23988
23968
  } else {
23989
23969
  return undefined;
@@ -24263,11 +24243,6 @@
24263
24243
  const viewportCoords = translateMouseCoordinates$1(event, viewport.contentDiv);
24264
24244
  const canvasCoords = translateMouseCoordinates$1(event, viewport.canvas);
24265
24245
  const genomicLocation = referenceFrame.start + referenceFrame.toBP(viewportCoords.x);
24266
-
24267
- if (undefined === genomicLocation || null === viewport.tile) {
24268
- return undefined;
24269
- }
24270
-
24271
24246
  return {
24272
24247
  event,
24273
24248
  viewport,
@@ -24321,22 +24296,28 @@
24321
24296
  return rows.join('');
24322
24297
  }
24323
24298
 
24324
- var Tile = function (chr, tileStart, tileEnd, bpPerPixel, features, roiFeatures) {
24325
- this.chr = chr;
24326
- this.startBP = tileStart;
24327
- this.endBP = tileEnd;
24328
- this.bpPerPixel = bpPerPixel;
24329
- this.features = features;
24330
- this.roiFeatures = roiFeatures;
24331
- };
24299
+ class FeatureCache {
24300
+ constructor(chr, tileStart, tileEnd, bpPerPixel, features, roiFeatures, multiresolution) {
24301
+ this.chr = chr;
24302
+ this.startBP = tileStart;
24303
+ this.endBP = tileEnd;
24304
+ this.bpPerPixel = bpPerPixel;
24305
+ this.features = features;
24306
+ this.roiFeatures = roiFeatures;
24307
+ this.multiresolution = multiresolution;
24308
+ }
24332
24309
 
24333
- Tile.prototype.containsRange = function (chr, start, end, bpPerPixel) {
24334
- return this.bpPerPixel === bpPerPixel && start >= this.startBP && end <= this.endBP && chr === this.chr;
24335
- };
24310
+ containsRange(chr, start, end, bpPerPixel) {
24311
+ // For multi-resolution tracks allow for a 2X change in bpPerPixel
24312
+ const r = this.multiresolution ? this.bpPerPixel / bpPerPixel : 1;
24313
+ return start >= this.startBP && end <= this.endBP && chr === this.chr && r > 0.5 && r < 2;
24314
+ }
24336
24315
 
24337
- Tile.prototype.overlapsRange = function (chr, start, end) {
24338
- return this.chr === chr && end >= this.startBP && start <= this.endBP;
24339
- };
24316
+ overlapsRange(chr, start, end) {
24317
+ return this.chr === chr && end >= this.startBP && start <= this.endBP;
24318
+ }
24319
+
24320
+ }
24340
24321
  /**
24341
24322
  * Merge 2 arrays. a and/or b can be undefined. If both are undefined, return undefined
24342
24323
  * @param a An array or undefined
@@ -25480,6 +25461,18 @@
25480
25461
  return true; // By definition
25481
25462
  }
25482
25463
 
25464
+ isMateMapped() {
25465
+ return true; // By definition
25466
+ }
25467
+
25468
+ isProperPair() {
25469
+ return this.firstAlignment.isProperPair();
25470
+ }
25471
+
25472
+ get tlen() {
25473
+ return Math.abs(this.firstAlignment.fragmentLength);
25474
+ }
25475
+
25483
25476
  firstOfPairStrand() {
25484
25477
  if (this.firstAlignment.isFirstOfPair()) {
25485
25478
  return this.firstAlignment.strand;
@@ -25846,7 +25839,15 @@
25846
25839
  */
25847
25840
 
25848
25841
  class AlignmentContainer {
25849
- constructor(chr, start, end, samplingWindowSize, samplingDepth, pairsSupported, alleleFreqThreshold) {
25842
+ // this.config.samplingWindowSize, this.config.samplingDepth,
25843
+ // this.config.pairsSupported, this.config.alleleFreqThreshold)
25844
+ constructor(chr, start, end, _ref) {
25845
+ let {
25846
+ samplingWindowSize,
25847
+ samplingDepth,
25848
+ pairsSupported,
25849
+ alleleFreqThreshold
25850
+ } = _ref;
25850
25851
  this.chr = chr;
25851
25852
  this.start = Math.floor(start);
25852
25853
  this.end = Math.ceil(end);
@@ -25869,17 +25870,10 @@
25869
25870
  // TODO -- pass this in
25870
25871
  return alignment.isMapped() && !alignment.isFailsVendorQualityCheck();
25871
25872
  };
25872
-
25873
- this.pairedEndStats = new PairedEndStats();
25874
25873
  }
25875
25874
 
25876
25875
  push(alignment) {
25877
25876
  if (this.filter(alignment) === false) return;
25878
-
25879
- if (alignment.isPaired()) {
25880
- this.pairedEndStats.push(alignment);
25881
- }
25882
-
25883
25877
  this.coverageMap.incCounts(alignment); // Count coverage before any downsampling
25884
25878
 
25885
25879
  if (this.pairsSupported && this.downsampledReads.has(alignment.readName)) {
@@ -25908,7 +25902,6 @@
25908
25902
  });
25909
25903
  this.pairsCache = undefined;
25910
25904
  this.downsampledReads = undefined;
25911
- this.pairedEndStats.compute();
25912
25905
  }
25913
25906
 
25914
25907
  contains(chr, start, end) {
@@ -26203,62 +26196,6 @@
26203
26196
 
26204
26197
  }
26205
26198
 
26206
- class PairedEndStats {
26207
- constructor(lowerPercentile, upperPercentile) {
26208
- this.totalCount = 0;
26209
- this.frCount = 0;
26210
- this.rfCount = 0;
26211
- this.ffCount = 0;
26212
- this.sumF = 0;
26213
- this.sumF2 = 0; //this.lp = lowerPercentile === undefined ? 0.005 : lowerPercentile;
26214
- //this.up = upperPercentile === undefined ? 0.995 : upperPercentile;
26215
- //this.digest = new Digest();
26216
- }
26217
-
26218
- push(alignment) {
26219
- if (alignment.isProperPair()) {
26220
- var fragmentLength = Math.abs(alignment.fragmentLength); //this.digest.push(fragmentLength);
26221
-
26222
- this.sumF += fragmentLength;
26223
- this.sumF2 += fragmentLength * fragmentLength;
26224
- var po = alignment.pairOrientation;
26225
-
26226
- if (typeof po === "string" && po.length === 4) {
26227
- var tmp = '' + po.charAt(0) + po.charAt(2);
26228
-
26229
- switch (tmp) {
26230
- case 'FF':
26231
- case 'RR':
26232
- this.ffCount++;
26233
- break;
26234
-
26235
- case "FR":
26236
- this.frCount++;
26237
- break;
26238
-
26239
- case "RF":
26240
- this.rfCount++;
26241
- }
26242
- }
26243
-
26244
- this.totalCount++;
26245
- }
26246
- }
26247
-
26248
- compute() {
26249
- if (this.totalCount > 100) {
26250
- if (this.ffCount / this.totalCount > 0.9) this.orienation = "ff";else if (this.frCount / this.totalCount > 0.9) this.orienation = "fr";else if (this.rfCount / this.totalCount > 0.9) this.orienation = "rf";
26251
- var fMean = this.sumF / this.totalCount;
26252
- var stdDev = Math.sqrt((this.totalCount * this.sumF2 - this.sumF * this.sumF) / (this.totalCount * this.totalCount));
26253
- this.lowerFragmentLength = fMean - 3 * stdDev;
26254
- this.upperFragmentLength = fMean + 3 * stdDev; //this.lowerFragmentLength = this.digest.percentile(this.lp);
26255
- //this.upperFragmentLength = this.digest.percentile(this.up);
26256
- //this.digest = undefined;
26257
- }
26258
- }
26259
-
26260
- }
26261
-
26262
26199
  class SupplementaryAlignment {
26263
26200
  constructor(rec) {
26264
26201
  const tokens = rec.split(',');
@@ -27462,7 +27399,7 @@
27462
27399
  const header = this.header;
27463
27400
  const queryChr = header.chrAliasTable.hasOwnProperty(chr) ? header.chrAliasTable[chr] : chr;
27464
27401
  const qAlignments = this.alignmentCache.queryFeatures(queryChr, bpStart, bpEnd);
27465
- const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.samplingWindowSize, this.samplingDepth, this.pairsSupported, this.alleleFreqThreshold);
27402
+ const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config);
27466
27403
 
27467
27404
  for (let a of qAlignments) {
27468
27405
  alignmentContainer.push(a);
@@ -27489,13 +27426,13 @@
27489
27426
  const alignments = [];
27490
27427
  this.header = BamUtils.decodeBamHeader(data);
27491
27428
  BamUtils.decodeBamRecords(data, this.header.size, alignments, this.header.chrNames);
27492
- this.alignmentCache = new FeatureCache(alignments, this.genome);
27429
+ this.alignmentCache = new FeatureCache$1(alignments, this.genome);
27493
27430
  }
27494
27431
 
27495
27432
  fetchAlignments(chr, bpStart, bpEnd) {
27496
27433
  const queryChr = this.header.chrAliasTable.hasOwnProperty(chr) ? this.header.chrAliasTable[chr] : chr;
27497
27434
  const features = this.alignmentCache.queryFeatures(queryChr, bpStart, bpEnd);
27498
- const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.samplingWindowSize, this.samplingDepth, this.pairsSupported);
27435
+ const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config);
27499
27436
 
27500
27437
  for (let feature of features) {
27501
27438
  alignmentContainer.push(feature);
@@ -28495,7 +28432,7 @@
28495
28432
  const chrToIndex = await this.getChrIndex();
28496
28433
  const queryChr = this.chrAliasTable.hasOwnProperty(chr) ? this.chrAliasTable[chr] : chr;
28497
28434
  const chrId = chrToIndex[queryChr];
28498
- const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config.samplingWindowSize, this.config.samplingDepth, this.config.pairsSupported, this.config.alleleFreqThreshold);
28435
+ const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config);
28499
28436
 
28500
28437
  if (chrId === undefined) {
28501
28438
  return alignmentContainer;
@@ -28644,7 +28581,7 @@
28644
28581
 
28645
28582
  async readAlignments(chr, start, end) {
28646
28583
  if (!this.bamReaders.hasOwnProperty(chr)) {
28647
- return new AlignmentContainer(chr, start, end);
28584
+ return new AlignmentContainer(chr, start, end, this.config);
28648
28585
  } else {
28649
28586
  let reader = this.bamReaders[chr];
28650
28587
  const a = await reader.readAlignments(chr, start, end);
@@ -28716,7 +28653,7 @@
28716
28653
  return igvxhr.loadString(url, buildOptions(self.config)).then(function (sam) {
28717
28654
  var alignmentContainer;
28718
28655
  header.chrToIndex[queryChr];
28719
- alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, self.samplingWindowSize, self.samplingDepth, self.pairsSupported, self.alleleFreqThreshold);
28656
+ alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, self.config);
28720
28657
  BamUtils.decodeSamRecords(sam, alignmentContainer, queryChr, bpStart, bpEnd, self.filter);
28721
28658
  return alignmentContainer;
28722
28659
  });
@@ -28926,7 +28863,7 @@
28926
28863
 
28927
28864
  const ba = unbgzf(compressedData.buffer);
28928
28865
  const chrIdx = this.header.chrToIndex[chr];
28929
- const alignmentContainer = new AlignmentContainer(chr, start, end, this.samplingWindowSize, this.samplingDepth, this.pairsSupported, this.alleleFreqThreshold);
28866
+ const alignmentContainer = new AlignmentContainer(chr, start, end, this.config);
28930
28867
  BamUtils.decodeBamRecords(ba, this.header.size, alignmentContainer, this.header.chrNames, chrIdx, start, end);
28931
28868
  alignmentContainer.finish();
28932
28869
  return alignmentContainer;
@@ -44464,7 +44401,7 @@
44464
44401
  const header = await this.getHeader();
44465
44402
  const queryChr = header.chrAliasTable.hasOwnProperty(chr) ? header.chrAliasTable[chr] : chr;
44466
44403
  const chrIdx = header.chrToIndex[queryChr];
44467
- const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.samplingWindowSize, this.samplingDepth, this.pairsSupported, this.alleleFreqThreshold);
44404
+ const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config);
44468
44405
 
44469
44406
  if (chrIdx === undefined) {
44470
44407
  return alignmentContainer;
@@ -45073,7 +45010,7 @@
45073
45010
  "pageSize": "10000"
45074
45011
  },
45075
45012
  decode: decodeGa4ghReads,
45076
- results: new AlignmentContainer(chr, bpStart, bpEnd, self.samplingWindowSize, self.samplingDepth, self.pairsSupported, self.alleleFreqThreshold)
45013
+ results: new AlignmentContainer(chr, bpStart, bpEnd, self.config)
45077
45014
  });
45078
45015
  });
45079
45016
 
@@ -45380,7 +45317,6 @@
45380
45317
  const genome = browser.genome;
45381
45318
  this.config = config;
45382
45319
  this.genome = genome;
45383
- this.alignmentContainer = undefined;
45384
45320
 
45385
45321
  if (isDataURL(config.url)) {
45386
45322
  if ("cram" === config.format) {
@@ -45431,58 +45367,44 @@
45431
45367
  }
45432
45368
 
45433
45369
  setViewAsPairs(bool) {
45434
-
45435
- if (this.viewAsPairs !== bool) {
45436
- this.viewAsPairs = bool; // if (this.alignmentContainer) {
45437
- // this.alignmentContainer.setViewAsPairs(bool);
45438
- // }
45439
- }
45370
+ this.viewAsPairs = bool;
45440
45371
  }
45441
45372
 
45442
45373
  setShowSoftClips(bool) {
45443
- if (this.showSoftClips !== bool) {
45444
- this.showSoftClips = bool;
45445
- }
45374
+ this.showSoftClips = bool;
45446
45375
  }
45447
45376
 
45448
45377
  async getAlignments(chr, bpStart, bpEnd) {
45449
45378
  const genome = this.genome;
45450
45379
  const showSoftClips = this.showSoftClips;
45380
+ const alignmentContainer = await this.bamReader.readAlignments(chr, bpStart, bpEnd);
45381
+ let alignments = alignmentContainer.alignments;
45451
45382
 
45452
- if (this.alignmentContainer && this.alignmentContainer.contains(chr, bpStart, bpEnd)) {
45453
- return this.alignmentContainer;
45454
- } else {
45455
- const alignmentContainer = await this.bamReader.readAlignments(chr, bpStart, bpEnd);
45456
- let alignments = alignmentContainer.alignments;
45457
-
45458
- if (!this.viewAsPairs) {
45459
- alignments = unpairAlignments([{
45460
- alignments: alignments
45461
- }]);
45462
- }
45463
-
45464
- const hasAlignments = alignments.length > 0;
45465
- alignmentContainer.packedAlignmentRows = packAlignmentRows(alignments, alignmentContainer.start, alignmentContainer.end, showSoftClips);
45466
- alignmentContainer.alignments = undefined; // Don't need to hold onto these anymore
45383
+ if (!this.viewAsPairs) {
45384
+ alignments = unpairAlignments([{
45385
+ alignments: alignments
45386
+ }]);
45387
+ }
45467
45388
 
45468
- this.alignmentContainer = alignmentContainer;
45389
+ const hasAlignments = alignments.length > 0;
45390
+ alignmentContainer.packedAlignmentRows = packAlignmentRows(alignments, alignmentContainer.start, alignmentContainer.end, showSoftClips);
45391
+ this.alignmentContainer = alignmentContainer;
45469
45392
 
45470
- if (hasAlignments) {
45471
- const sequence = await genome.sequence.getSequence(chr, alignmentContainer.start, alignmentContainer.end);
45393
+ if (hasAlignments) {
45394
+ const sequence = await genome.sequence.getSequence(chr, alignmentContainer.start, alignmentContainer.end);
45472
45395
 
45473
- if (sequence) {
45474
- alignmentContainer.coverageMap.refSeq = sequence; // TODO -- fix this
45396
+ if (sequence) {
45397
+ alignmentContainer.coverageMap.refSeq = sequence; // TODO -- fix this
45475
45398
 
45476
- alignmentContainer.sequence = sequence; // TODO -- fix this
45399
+ alignmentContainer.sequence = sequence; // TODO -- fix this
45477
45400
 
45478
- return alignmentContainer;
45479
- } else {
45480
- console.error("No sequence for: " + chr + ":" + alignmentContainer.start + "-" + alignmentContainer.end);
45481
- }
45401
+ return alignmentContainer;
45402
+ } else {
45403
+ console.error("No sequence for: " + chr + ":" + alignmentContainer.start + "-" + alignmentContainer.end);
45482
45404
  }
45483
-
45484
- return alignmentContainer;
45485
45405
  }
45406
+
45407
+ return alignmentContainer;
45486
45408
  }
45487
45409
 
45488
45410
  }
@@ -45827,7 +45749,7 @@
45827
45749
  clickedFeatures(clickState, features) {
45828
45750
  // We use the cached features rather than method to avoid async load. If the
45829
45751
  // feature is not already loaded this won't work, but the user wouldn't be mousing over it either.
45830
- if (!features) features = clickState.viewport.getCachedFeatures();
45752
+ if (!features) features = clickState.viewport.cachedFeatures;
45831
45753
 
45832
45754
  if (!features || features.length === 0) {
45833
45755
  return [];
@@ -48466,52 +48388,119 @@
48466
48388
  return regions;
48467
48389
  }
48468
48390
 
48391
+ function sendChords(chords, track, refFrame, alpha) {
48392
+ const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? track.color : getChrColor(refFrame.chr), alpha);
48393
+ const trackColor = IGVColor.addAlpha(track.color || 'rgb(0,0,255)', alpha); // name the chord set to include locus and filtering information
48394
+
48395
+ const encodedName = track.name.replaceAll(' ', '%20');
48396
+ const chordSetName = "all" === refFrame.chr ? encodedName : `${encodedName} ${refFrame.chr}:${refFrame.start}-${refFrame.end}`;
48397
+ track.browser.circularView.addChords(chords, {
48398
+ track: chordSetName,
48399
+ color: chordSetColor,
48400
+ trackColor: trackColor
48401
+ });
48402
+ }
48403
+
48469
48404
  function createCircularView(el, browser) {
48470
48405
  const circularView = new CircularView(el, {
48471
48406
  onChordClick: (feature, chordTrack, pluginManager) => {
48472
48407
  const f1 = feature.data;
48473
48408
  const f2 = f1.mate;
48474
- const flanking = 2000;
48475
- const l1 = new Locus({
48476
- chr: browser.genome.getChromosomeName(f1.refName),
48477
- start: f1.start,
48478
- end: f1.end
48479
- });
48480
- const l2 = new Locus({
48481
- chr: browser.genome.getChromosomeName(f2.refName),
48482
- start: f2.start,
48483
- end: f2.end
48484
- });
48485
- let loci; // If there is overlap with current loci
48486
-
48487
- loci = browser.currentLoci().map(str => Locus.fromLocusString(str));
48488
-
48489
- if (loci.some(locus => locus.contains(l1)) || loci.some(locus => locus.contains(l2))) {
48490
- for (let l of [l1, l2]) {
48491
- if (!loci.some(locus => {
48492
- return locus.contains(l);
48493
- })) {
48494
- // add flanking
48495
- l.start = Math.max(0, l.start - flanking);
48496
- l.end += flanking;
48497
- loci.push(l);
48409
+ addFrameForFeature(f1);
48410
+ addFrameForFeature(f2);
48411
+
48412
+ function addFrameForFeature(feature) {
48413
+ feature.chr = browser.genome.getChromosomeName(feature.refName);
48414
+
48415
+ for (let referenceFrame of browser.currentReferenceFrames()) {
48416
+ const l = Locus.fromLocusString(referenceFrame.getLocusString());
48417
+
48418
+ if (l.contains(feature)) {
48419
+ break;
48420
+ } else if (l.overlaps(feature)) {
48421
+ referenceFrame.extend(feature);
48422
+ break;
48423
+ } else {
48424
+ const flanking = 2000;
48425
+ const center = (feature.start + feature.end) / 2;
48426
+ browser.addMultiLocusPanel(feature.chr, center - flanking, center + flanking);
48427
+ break;
48498
48428
  }
48499
48429
  }
48500
- } else {
48501
- l1.start = Math.max(0, l1.start - flanking);
48502
- l1.end += flanking;
48503
- l2.start = Math.max(0, l2.start - flanking);
48504
- l2.end += flanking;
48505
- loci = [l1, l2];
48506
48430
  }
48507
-
48508
- const searchString = loci.map(l => l.getLocusString()).join(" ");
48509
- browser.search(searchString);
48510
48431
  }
48511
48432
  });
48512
48433
  return circularView;
48513
48434
  }
48514
48435
 
48436
+ class PairedEndStats {
48437
+ constructor(alignments, _ref) {
48438
+ let {
48439
+ minTLENPercentile,
48440
+ maxTLENPercentile
48441
+ } = _ref;
48442
+ this.totalCount = 0;
48443
+ this.frCount = 0;
48444
+ this.rfCount = 0;
48445
+ this.ffCount = 0;
48446
+ this.sumF = 0;
48447
+ this.sumF2 = 0;
48448
+ this.lp = minTLENPercentile === undefined ? 0.1 : minTLENPercentile;
48449
+ this.up = maxTLENPercentile === undefined ? 99.5 : maxTLENPercentile;
48450
+ this.isizes = [];
48451
+ this.compute(alignments);
48452
+ }
48453
+
48454
+ compute(alignments) {
48455
+ for (let alignment of alignments) {
48456
+ if (alignment.isProperPair()) {
48457
+ var tlen = Math.abs(alignment.fragmentLength);
48458
+ this.sumF += tlen;
48459
+ this.sumF2 += tlen * tlen;
48460
+ this.isizes.push(tlen);
48461
+ var po = alignment.pairOrientation;
48462
+
48463
+ if (typeof po === "string" && po.length === 4) {
48464
+ var tmp = '' + po.charAt(0) + po.charAt(2);
48465
+
48466
+ switch (tmp) {
48467
+ case 'FF':
48468
+ case 'RR':
48469
+ this.ffCount++;
48470
+ break;
48471
+
48472
+ case "FR":
48473
+ this.frCount++;
48474
+ break;
48475
+
48476
+ case "RF":
48477
+ this.rfCount++;
48478
+ }
48479
+ }
48480
+
48481
+ this.totalCount++;
48482
+ }
48483
+ }
48484
+
48485
+ if (this.ffCount / this.totalCount > 0.9) this.orienation = "ff";else if (this.frCount / this.totalCount > 0.9) this.orienation = "fr";else if (this.rfCount / this.totalCount > 0.9) this.orienation = "rf";
48486
+ this.minTLEN = this.lp === 0 ? 0 : percentile(this.isizes, this.lp);
48487
+ this.maxTLEN = percentile(this.isizes, this.up); // var fMean = this.sumF / this.totalCount
48488
+ // var stdDev = Math.sqrt((this.totalCount * this.sumF2 - this.sumF * this.sumF) / (this.totalCount * this.totalCount))
48489
+ // this.minTLEN = fMean - 3 * stdDev
48490
+ // this.maxTLEN = fMean + 3 * stdDev
48491
+ }
48492
+
48493
+ }
48494
+
48495
+ function percentile(array, p) {
48496
+ if (array.length === 0) return undefined;
48497
+ var k = Math.floor(array.length * (p / 100));
48498
+ array.sort(function (a, b) {
48499
+ return a - b;
48500
+ });
48501
+ return array[k];
48502
+ }
48503
+
48515
48504
  /*
48516
48505
  * The MIT License (MIT)
48517
48506
  *
@@ -48571,10 +48560,7 @@
48571
48560
  this.showInsertions = false !== config.showInsertions;
48572
48561
  this.showMismatches = false !== config.showMismatches;
48573
48562
  this.color = config.color;
48574
- this.coverageColor = config.coverageColor;
48575
- this.minFragmentLength = config.minFragmentLength; // Optional, might be undefined
48576
-
48577
- this.maxFragmentLength = config.maxFragmentLength || 1000; // The sort object can be an array in the case of multi-locus view, however if multiple sort positions
48563
+ this.coverageColor = config.coverageColor; // The sort object can be an array in the case of multi-locus view, however if multiple sort positions
48578
48564
  // are present for a given reference frame the last one will take precedence
48579
48565
 
48580
48566
  if (config.sort) {
@@ -48602,12 +48588,22 @@
48602
48588
  return this._height;
48603
48589
  }
48604
48590
 
48591
+ get minTemplateLength() {
48592
+ const configMinTLEN = this.config.minTLEN !== undefined ? this.config.minTLEN : this.config.minFragmentLength;
48593
+ return configMinTLEN !== undefined ? configMinTLEN : this._pairedEndStats ? this._pairedEndStats.minTLEN : 0;
48594
+ }
48595
+
48596
+ get maxTemplateLength() {
48597
+ const configMaxTLEN = this.config.maxTLEN !== undefined ? this.config.maxTLEN : this.config.maxFragmentLength;
48598
+ return configMaxTLEN !== undefined ? configMaxTLEN : this._pairedEndStats ? this._pairedEndStats.maxTLEN : 1000;
48599
+ }
48600
+
48605
48601
  sort(options) {
48606
48602
  options = this.assignSort(options);
48607
48603
 
48608
48604
  for (let vp of this.trackView.viewports) {
48609
48605
  if (vp.containsPosition(options.chr, options.position)) {
48610
- const alignmentContainer = vp.getCachedFeatures();
48606
+ const alignmentContainer = vp.cachedFeatures;
48611
48607
 
48612
48608
  if (alignmentContainer) {
48613
48609
  sortAlignmentRows(options, alignmentContainer);
@@ -48642,16 +48638,16 @@
48642
48638
  async getFeatures(chr, bpStart, bpEnd, bpPerPixel, viewport) {
48643
48639
  const alignmentContainer = await this.featureSource.getAlignments(chr, bpStart, bpEnd);
48644
48640
 
48645
- if (alignmentContainer.alignments && alignmentContainer.alignments.length > 99) {
48646
- if (undefined === this.minFragmentLength) {
48647
- this.minFragmentLength = alignmentContainer.pairedEndStats.lowerFragmentLength;
48648
- }
48641
+ if (alignmentContainer.paired && !this._pairedEndStats && !this.config.maxFragmentLength) {
48642
+ const pairedEndStats = new PairedEndStats(alignmentContainer.alignments, this.config);
48649
48643
 
48650
- if (undefined === this.maxFragmentLength) {
48651
- this.maxFragmentLength = alignmentContainer.pairedEndStats.upperFragmentLength;
48644
+ if (pairedEndStats.totalCount > 99) {
48645
+ this._pairedEndStats = pairedEndStats;
48652
48646
  }
48653
48647
  }
48654
48648
 
48649
+ alignmentContainer.alignments = undefined; // Don't need to hold onto these anymore
48650
+
48655
48651
  const sort = this.sortObject;
48656
48652
 
48657
48653
  if (sort) {
@@ -48756,7 +48752,7 @@
48756
48752
  label: 'pair orientation'
48757
48753
  });
48758
48754
  colorByMenuItems.push({
48759
- key: 'fragmentLength',
48755
+ key: 'tlen',
48760
48756
  label: 'insert size (TLEN)'
48761
48757
  });
48762
48758
  colorByMenuItems.push({
@@ -48867,7 +48863,7 @@
48867
48863
  this.trackView.repaintViews();
48868
48864
  }
48869
48865
  });
48870
- } // Experimental JBrowse feature
48866
+ } // Add chords to JBrowse circular view, if present
48871
48867
 
48872
48868
 
48873
48869
  if (this.browser.circularView && true === this.browser.circularViewVisible && (this.alignmentTrack.hasPairs || this.alignmentTrack.hasSupplemental)) {
@@ -48877,26 +48873,9 @@
48877
48873
  menuItems.push({
48878
48874
  label: 'Add discordant pairs to circular view',
48879
48875
  click: () => {
48880
- const maxFragmentLength = this.maxFragmentLength;
48881
- const inView = [];
48882
-
48883
48876
  for (let viewport of this.trackView.viewports) {
48884
- for (let a of viewport.getCachedFeatures().allAlignments()) {
48885
- const referenceFrame = viewport.referenceFrame;
48886
-
48887
- if (a.end >= referenceFrame.start && a.start <= referenceFrame.end && a.mate && a.mate.chr && (a.mate.chr !== a.chr || Math.max(a.fragmentLength) > maxFragmentLength)) {
48888
- inView.push(a);
48889
- }
48890
- }
48877
+ this.addPairedChordsForViewport(viewport);
48891
48878
  }
48892
-
48893
- this.browser.circularViewVisible = true;
48894
- const chords = makePairedAlignmentChords(inView);
48895
- const color = IGVColor.addAlpha(this.color || 'rgb(0,0,255)', 0.02);
48896
- this.browser.circularView.addChords(chords, {
48897
- track: this.name,
48898
- color: color
48899
- });
48900
48879
  }
48901
48880
  });
48902
48881
  }
@@ -48905,25 +48884,9 @@
48905
48884
  menuItems.push({
48906
48885
  label: 'Add split reads to circular view',
48907
48886
  click: () => {
48908
- const inView = [];
48909
-
48910
48887
  for (let viewport of this.trackView.viewports) {
48911
- for (let a of viewport.getCachedFeatures().allAlignments()) {
48912
- const referenceFrame = viewport.referenceFrame;
48913
- const sa = a.hasTag('SA');
48914
-
48915
- if (a.end >= referenceFrame.start && a.start <= referenceFrame.end && sa) {
48916
- inView.push(a);
48917
- }
48918
- }
48888
+ this.addSplitChordsForViewport(viewport);
48919
48889
  }
48920
-
48921
- const chords = makeSupplementalAlignmentChords(inView);
48922
- const color = IGVColor.addAlpha(this.color || 'rgb(0,0,255)', 0.1);
48923
- this.browser.circularView.addChords(chords, {
48924
- track: this.name,
48925
- color: color
48926
- });
48927
48890
  }
48928
48891
  });
48929
48892
  }
@@ -49042,7 +49005,7 @@
49042
49005
  }
49043
49006
 
49044
49007
  getCachedAlignmentContainers() {
49045
- return this.trackView.viewports.map(vp => vp.getCachedFeatures());
49008
+ return this.trackView.viewports.map(vp => vp.cachedFeatures);
49046
49009
  }
49047
49010
 
49048
49011
  get dataRange() {
@@ -49068,6 +49031,56 @@
49068
49031
  set autoscale(autoscale) {
49069
49032
  this.coverageTrack.autoscale = autoscale;
49070
49033
  }
49034
+ /**
49035
+ * Add chords to the circular view for the given viewport, represented by its reference frame
49036
+ * @param refFrame
49037
+ */
49038
+
49039
+
49040
+ addPairedChordsForViewport(viewport) {
49041
+ const maxTemplateLength = this.maxTemplateLength;
49042
+ const inView = [];
49043
+ const refFrame = viewport.referenceFrame;
49044
+
49045
+ for (let a of viewport.cachedFeatures.allAlignments()) {
49046
+ if (a.end >= refFrame.start && a.start <= refFrame.end && a.mate && a.mate.chr && (a.mate.chr !== a.chr || Math.max(a.fragmentLength) > maxTemplateLength)) {
49047
+ inView.push(a);
49048
+ }
49049
+ }
49050
+
49051
+ const chords = makePairedAlignmentChords(inView);
49052
+ sendChords(chords, this, refFrame, 0.02); // const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? this.color : getChrColor(refFrame.chr), 0.02)
49053
+ // const trackColor = IGVColor.addAlpha(this.color || 'rgb(0,0,255)', 0.02)
49054
+ //
49055
+ // // name the chord set to include track name and locus
49056
+ // const encodedName = this.name.replaceAll(' ', '%20')
49057
+ // const chordSetName = "all" === refFrame.chr ? encodedName :
49058
+ // `${encodedName} (${refFrame.chr}:${refFrame.start}-${refFrame.end}`
49059
+ // this.browser.circularView.addChords(chords, {name: chordSetName, color: chordSetColor, trackColor: trackColor})
49060
+ }
49061
+
49062
+ addSplitChordsForViewport(viewport) {
49063
+ const inView = [];
49064
+ const refFrame = viewport.referenceFrame;
49065
+
49066
+ for (let a of viewport.cachedFeatures.allAlignments()) {
49067
+ const sa = a.hasTag('SA');
49068
+
49069
+ if (a.end >= refFrame.start && a.start <= refFrame.end && sa) {
49070
+ inView.push(a);
49071
+ }
49072
+ }
49073
+
49074
+ const chords = makeSupplementalAlignmentChords(inView);
49075
+ sendChords(chords, this, refFrame, 0.02); // const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? this.color : getChrColor(refFrame.chr), 0.02)
49076
+ // const trackColor = IGVColor.addAlpha(this.color || 'rgb(0,0,255)', 0.02)
49077
+ //
49078
+ // // name the chord set to include track name and locus
49079
+ // const encodedName = this.name.replaceAll(' ', '%20')
49080
+ // const chordSetName = "all" === refFrame.chr ? encodedName :
49081
+ // `${encodedName} (${refFrame.chr}:${refFrame.start}-${refFrame.end}`
49082
+ // this.browser.circularView.addChords(chords, {name: chordSetName, color: chordSetColor, trackColor: trackColor})
49083
+ }
49071
49084
 
49072
49085
  }
49073
49086
 
@@ -49178,7 +49191,7 @@
49178
49191
  }
49179
49192
 
49180
49193
  getClickedObject(clickState) {
49181
- let features = clickState.viewport.getCachedFeatures();
49194
+ let features = clickState.viewport.cachedFeatures;
49182
49195
  if (!features || features.length === 0) return;
49183
49196
  const genomicLocation = Math.floor(clickState.genomicLocation);
49184
49197
  const coverageMap = features.coverageMap;
@@ -49264,14 +49277,14 @@
49264
49277
  this.deletionColor = config.deletionColor || "black";
49265
49278
  this.skippedColor = config.skippedColor || "rgb(150, 170, 170)";
49266
49279
  this.pairConnectorColor = config.pairConnectorColor;
49267
- this.smallFragmentLengthColor = config.smallFragmentLengthColor || "rgb(0, 0, 150)";
49268
- this.largeFragmentLengthColor = config.largeFragmentLengthColor || "rgb(200, 0, 0)";
49280
+ this.smallTLENColor = config.smallTLENColor || config.smallFragmentLengthColor || "rgb(0, 0, 150)";
49281
+ this.largeTLENColor = config.largeTLENColor || config.largeFragmentLengthColor || "rgb(200, 0, 0)";
49269
49282
  this.pairOrientation = config.pairOrienation || 'fr';
49270
49283
  this.pairColors = {};
49271
49284
  this.pairColors["RL"] = config.rlColor || "rgb(0, 150, 0)";
49272
49285
  this.pairColors["RR"] = config.rrColor || "rgb(20, 50, 200)";
49273
49286
  this.pairColors["LL"] = config.llColor || "rgb(0, 150, 150)";
49274
- this.colorBy = config.colorBy || "pairOrientation";
49287
+ this.colorBy = config.colorBy || "unexpectedPair";
49275
49288
  this.colorByTag = config.colorByTag ? config.colorByTag.toUpperCase() : undefined;
49276
49289
  this.bamColorTag = config.bamColorTag === undefined ? "YC" : config.bamColorTag;
49277
49290
  this.hideSmallIndels = config.hideSmallIndels;
@@ -49626,7 +49639,7 @@
49626
49639
  direction: direction
49627
49640
  };
49628
49641
  this.parent.sortObject = newSortObject;
49629
- sortAlignmentRows(newSortObject, viewport.getCachedFeatures());
49642
+ sortAlignmentRows(newSortObject, viewport.cachedFeatures);
49630
49643
  viewport.repaint();
49631
49644
  };
49632
49645
 
@@ -49678,7 +49691,7 @@
49678
49691
  };
49679
49692
  this.sortByTag = tag;
49680
49693
  this.parent.sortObject = newSortObject;
49681
- sortAlignmentRows(newSortObject, viewport.getCachedFeatures());
49694
+ sortAlignmentRows(newSortObject, viewport.cachedFeatures);
49682
49695
  viewport.repaint();
49683
49696
  }
49684
49697
  }
@@ -49702,8 +49715,12 @@
49702
49715
  const referenceFrame = clickState.viewport.referenceFrame;
49703
49716
 
49704
49717
  if (this.browser.genome.getChromosome(clickedAlignment.mate.chr)) {
49705
- this.highlightedAlignmentReadNamed = clickedAlignment.readName;
49706
- this.browser.presentMultiLocusPanel(clickedAlignment, referenceFrame);
49718
+ this.highlightedAlignmentReadNamed = clickedAlignment.readName; //this.browser.presentMultiLocusPanel(clickedAlignment, referenceFrame)
49719
+
49720
+ const bpWidth = referenceFrame.end - referenceFrame.start;
49721
+ const frameStart = clickedAlignment.mate.position - bpWidth / 2;
49722
+ const frameEnd = clickedAlignment.mate.position + bpWidth / 2;
49723
+ this.browser.addMultiLocusPanel(clickedAlignment.mate.chr, frameStart, frameEnd, referenceFrame);
49707
49724
  } else {
49708
49725
  Alert.presentAlert(`Reference does not contain chromosome: ${clickedAlignment.mate.chr}`);
49709
49726
  }
@@ -49751,20 +49768,7 @@
49751
49768
  list.push({
49752
49769
  label: 'Add discordant pairs to circular view',
49753
49770
  click: () => {
49754
- const maxFragmentLength = this.parent.maxFragmentLength;
49755
- const {
49756
- referenceFrame
49757
- } = viewport;
49758
- const inView = viewport.getCachedFeatures().allAlignments().filter(a => {
49759
- return a.end >= referenceFrame.start && a.start <= referenceFrame.end && a.mate && a.mate.chr && (a.mate.chr !== a.chr || Math.max(a.fragmentLength) > maxFragmentLength);
49760
- });
49761
- this.browser.circularViewVisible = true;
49762
- const chords = makePairedAlignmentChords(inView);
49763
- const color = IGVColor.addAlpha(this.parent.color || 'rgb(0,0,255)', 0.02);
49764
- this.browser.circularView.addChords(chords, {
49765
- track: this.parent.name,
49766
- color: color
49767
- });
49771
+ this.parent.addPairedChordsForViewport(viewport);
49768
49772
  }
49769
49773
  });
49770
49774
  }
@@ -49773,23 +49777,7 @@
49773
49777
  list.push({
49774
49778
  label: 'Add split reads to circular view',
49775
49779
  click: () => {
49776
- const inView = [];
49777
-
49778
- for (let a of viewport.getCachedFeatures().allAlignments()) {
49779
- const referenceFrame = viewport.referenceFrame;
49780
- const sa = a.hasTag('SA');
49781
-
49782
- if (a.end >= referenceFrame.start && a.start <= referenceFrame.end && sa) {
49783
- inView.push(a);
49784
- }
49785
- }
49786
-
49787
- const chords = makeSupplementalAlignmentChords(inView);
49788
- const color = IGVColor.addAlpha(this.color || 'rgb(0,0,255)', 0.1);
49789
- this.browser.circularView.addChords(chords, {
49790
- track: this.name,
49791
- color: color
49792
- });
49780
+ this.parent.addSplitChordsForViewport(viewport);
49793
49781
  }
49794
49782
  });
49795
49783
  }
@@ -49805,7 +49793,7 @@
49805
49793
  const y = clickState.y;
49806
49794
  const genomicLocation = clickState.genomicLocation;
49807
49795
  const showSoftClips = this.parent.showSoftClips;
49808
- let features = viewport.getCachedFeatures();
49796
+ let features = viewport.cachedFeatures;
49809
49797
  if (!features || features.length === 0) return;
49810
49798
  let packedAlignmentRows = features.packedAlignmentRows;
49811
49799
  let downsampledIntervals = features.downsampledIntervals;
@@ -49891,13 +49879,16 @@
49891
49879
  break;
49892
49880
  }
49893
49881
 
49882
+ case "tlen":
49894
49883
  case "fragmentLength":
49895
- if (alignment.mate && alignment.isMateMapped() && alignment.mate.chr !== alignment.chr) {
49896
- color = getChrColor(alignment.mate.chr);
49897
- } else if (this.parent.minFragmentLength && Math.abs(alignment.fragmentLength) < this.parent.minFragmentLength) {
49898
- color = this.smallFragmentLengthColor;
49899
- } else if (this.parent.maxFragmentLength && Math.abs(alignment.fragmentLength) > this.parent.maxFragmentLength) {
49900
- color = this.largeFragmentLengthColor;
49884
+ if (alignment.mate && alignment.isMateMapped()) {
49885
+ if (alignment.mate.chr !== alignment.chr) {
49886
+ color = getChrColor(alignment.mate.chr);
49887
+ } else if (this.parent.minTemplateLength && Math.abs(alignment.fragmentLength) < this.parent.minTemplateLength) {
49888
+ color = this.smallTLENColor;
49889
+ } else if (this.parent.maxTemplateLength && Math.abs(alignment.fragmentLength) > this.parent.maxTemplateLength) {
49890
+ color = this.largeTLENColor;
49891
+ }
49901
49892
  }
49902
49893
 
49903
49894
  break;
@@ -49939,10 +49930,7 @@
49939
49930
  alignmentContainer.packedAlignmentRows.sort(function (rowA, rowB) {
49940
49931
  const i = rowA.score > rowB.score ? 1 : rowA.score < rowB.score ? -1 : 0;
49941
49932
  return true === direction ? i : -i;
49942
- }); // For debugging
49943
- // for(let r of alignmentContainer.packedAlignmentRows) {
49944
- // console.log(r.score);
49945
- // }
49933
+ });
49946
49934
  }
49947
49935
 
49948
49936
  function shadedBaseColor(qual, baseColor) {
@@ -50097,7 +50085,7 @@
50097
50085
  });
50098
50086
  this.$viewport.append(this.$rulerLabel);
50099
50087
  this.$rulerLabel.click(async () => {
50100
- await this.browser.selectMultiLocusPanel(this.referenceFrame); // const removals = this.browser.referenceFrameList.filter(r => this.referenceFrame !== r)
50088
+ await this.browser.gotoMultilocusPanel(this.referenceFrame); // const removals = this.browser.referenceFrameList.filter(r => this.referenceFrame !== r)
50101
50089
  // for (let referenceFrame of removals) {
50102
50090
  // await this.browser.removeMultiLocusPanel(referenceFrame)
50103
50091
  // }
@@ -50412,7 +50400,7 @@
50412
50400
  referenceFrame.start = ss;
50413
50401
  referenceFrame.end = ee;
50414
50402
  referenceFrame.bpPerPixel = (ee - ss) / width;
50415
- this.browser.updateViews(referenceFrame, this.browser.trackViews, true);
50403
+ this.browser.updateViews(true);
50416
50404
  }
50417
50405
 
50418
50406
  this.boundClickHandler = clickHandler.bind(this);
@@ -51289,7 +51277,9 @@
51289
51277
 
51290
51278
  repaintViews() {
51291
51279
  for (let viewport of this.viewports) {
51292
- viewport.repaint();
51280
+ if (viewport.isVisible()) {
51281
+ viewport.repaint();
51282
+ }
51293
51283
  }
51294
51284
 
51295
51285
  if (typeof this.track.paintAxis === 'function') {
@@ -51313,6 +51303,9 @@
51313
51303
  }
51314
51304
  /**
51315
51305
  * Update viewports to reflect current genomic state, possibly loading additional data.
51306
+ *
51307
+ * @param force - if true, force a repaint even if no new data is loaded
51308
+ * @returns {Promise<void>}
51316
51309
  */
51317
51310
 
51318
51311
 
@@ -51328,7 +51321,7 @@
51328
51321
  } // rpv: viewports whose image (canvas) does not fully cover current genomic range
51329
51322
 
51330
51323
 
51331
- const reloadableViewports = this.viewportsToReload(force); // Trigger viewport to load features needed to cover current genomic range
51324
+ const reloadableViewports = force ? visibleViewports : this.viewportsToReload(); // Trigger viewport to load features needed to cover current genomic range
51332
51325
  // NOTE: these must be loaded synchronously, do not user Promise.all, not all file readers are thread safe
51333
51326
 
51334
51327
  for (let viewport of reloadableViewports) {
@@ -51336,7 +51329,7 @@
51336
51329
  }
51337
51330
 
51338
51331
  if (this.disposed) return; // Track was removed during load
51339
- // Very special case for variant tracks in multilocus view. The # of rows to allocate to the variant (site)
51332
+ // Special case for variant tracks in multilocus view. The # of rows to allocate to the variant (site)
51340
51333
  // section depends on data from all the views. We only need to adjust this however if any data was loaded
51341
51334
  // (i.e. reloadableViewports.length > 0)
51342
51335
 
@@ -51344,8 +51337,8 @@
51344
51337
  let maxRow = 0;
51345
51338
 
51346
51339
  for (let viewport of this.viewports) {
51347
- if (viewport.tile && viewport.tile.features) {
51348
- maxRow = Math.max(maxRow, viewport.tile.features.reduce((a, f) => Math.max(a, f.row || 0), 0));
51340
+ if (viewport.featureCache && viewport.featureCache.features) {
51341
+ maxRow = Math.max(maxRow, viewport.featureCache.features.reduce((a, f) => Math.max(a, f.row || 0), 0));
51349
51342
  }
51350
51343
  }
51351
51344
 
@@ -51368,14 +51361,14 @@
51368
51361
  const start = referenceFrame.start;
51369
51362
  const end = start + referenceFrame.toBP($$1(visibleViewport.contentDiv).width());
51370
51363
 
51371
- if (visibleViewport.tile && visibleViewport.tile.features) {
51372
- if (typeof visibleViewport.tile.features.getMax === 'function') {
51373
- const max = visibleViewport.tile.features.getMax(start, end);
51364
+ if (visibleViewport.featureCache && visibleViewport.featureCache.features) {
51365
+ if (typeof visibleViewport.featureCache.features.getMax === 'function') {
51366
+ const max = visibleViewport.featureCache.features.getMax(start, end);
51374
51367
  allFeatures.push({
51375
51368
  value: max
51376
51369
  });
51377
51370
  } else {
51378
- allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(visibleViewport.tile.features, start, end));
51371
+ allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(visibleViewport.featureCache.features, start, end));
51379
51372
  }
51380
51373
  }
51381
51374
  }
@@ -51385,16 +51378,22 @@
51385
51378
  } else {
51386
51379
  this.track.dataRange = doAutoscale(allFeatures);
51387
51380
  }
51388
- } // Must repaint all viewports if autoscaling
51381
+ } // Must repaint all viewports if autoscaling. Always repaint ruler.
51389
51382
 
51390
51383
 
51391
- if (!isDragging && (this.track.autoscale || this.track.autoscaleGroup)) {
51392
- for (let visibleViewport of visibleViewports) {
51393
- visibleViewport.repaint();
51384
+ if (this.track.autoscale || this.track.autoscaleGroup || this.track.type === 'ruler' || force) {
51385
+ for (let vp of visibleViewports) {
51386
+ vp.repaint();
51394
51387
  }
51395
51388
  } else {
51396
- for (let vp of reloadableViewports) {
51397
- vp.repaint();
51389
+ const reloadedViewports = new Set(reloadableViewports);
51390
+
51391
+ for (let vp of visibleViewports) {
51392
+ const invalid = vp.canvas && vp.canvas._data && vp.canvas._data.invalidate;
51393
+
51394
+ if (invalid || reloadedViewports.has(vp)) {
51395
+ vp.repaint();
51396
+ }
51398
51397
  }
51399
51398
  }
51400
51399
 
@@ -51422,13 +51421,13 @@
51422
51421
  */
51423
51422
 
51424
51423
 
51425
- async getInViewFeatures(force) {
51424
+ async getInViewFeatures() {
51426
51425
  if (!(this.browser && this.browser.referenceFrameList)) {
51427
51426
  return [];
51428
51427
  } // List of viewports that need reloading
51429
51428
 
51430
51429
 
51431
- const rpV = this.viewportsToReload(force);
51430
+ const rpV = this.viewportsToReload();
51432
51431
  const promises = rpV.map(function (vp) {
51433
51432
  return vp.loadFeatures();
51434
51433
  });
@@ -51436,18 +51435,18 @@
51436
51435
  let allFeatures = [];
51437
51436
 
51438
51437
  for (let vp of this.viewports) {
51439
- if (vp.tile && vp.tile.features) {
51438
+ if (vp.featureCache && vp.featureCache.features) {
51440
51439
  const referenceFrame = vp.referenceFrame;
51441
51440
  const start = referenceFrame.start;
51442
51441
  const end = start + referenceFrame.toBP($$1(vp.contentDiv).width());
51443
51442
 
51444
- if (typeof vp.tile.features.getMax === 'function') {
51445
- const max = vp.tile.features.getMax(start, end);
51443
+ if (typeof vp.featureCache.features.getMax === 'function') {
51444
+ const max = vp.featureCache.features.getMax(start, end);
51446
51445
  allFeatures.push({
51447
51446
  value: max
51448
51447
  });
51449
51448
  } else {
51450
- allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(vp.tile.features, start, end));
51449
+ allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(vp.featureCache.features, start, end));
51451
51450
  }
51452
51451
  }
51453
51452
  }
@@ -51504,7 +51503,7 @@
51504
51503
  const start = referenceFrame.start;
51505
51504
  const end = start + referenceFrame.toBP($$1(viewport.contentDiv).width());
51506
51505
  const bpPerPixel = referenceFrame.bpPerPixel;
51507
- return force || !viewport.tile || viewport.tile.invalidate || !viewport.tile.containsRange(chr, start, end, bpPerPixel);
51506
+ return !viewport.featureCache || !viewport.featureCache.containsRange(chr, start, end, bpPerPixel);
51508
51507
  }
51509
51508
  });
51510
51509
  return viewports;
@@ -56314,7 +56313,7 @@
56314
56313
  this.genome = genome;
56315
56314
  this.sourceType = config.sourceType === undefined ? "file" : config.sourceType;
56316
56315
  this.maxWGCount = config.maxWGCount || DEFAULT_MAX_WG_COUNT;
56317
- const queryableFormats = new Set(["bigwig", "bw", "bigbed", "bb", "tdf"]);
56316
+ const queryableFormats = new Set(["bigwig", "bw", "bigbed", "bb", "biginteract", "tdf"]);
56318
56317
 
56319
56318
  if (config.features && Array.isArray(config.features)) {
56320
56319
  // Explicit array of features
@@ -56326,7 +56325,7 @@
56326
56325
  }
56327
56326
 
56328
56327
  this.queryable = false;
56329
- this.featureCache = new FeatureCache(features, genome);
56328
+ this.featureCache = new FeatureCache$1(features, genome);
56330
56329
  } else if (config.reader) {
56331
56330
  // Explicit reader implementation
56332
56331
  this.reader = config.reader;
@@ -56497,13 +56496,13 @@
56497
56496
  } // Note - replacing previous cache with new one. genomicInterval is optional (might be undefined => includes all features)
56498
56497
 
56499
56498
 
56500
- this.featureCache = new FeatureCache(features, this.genome, genomicInterval); // If track is marked "searchable"< cache features by name -- use this with caution, memory intensive
56499
+ this.featureCache = new FeatureCache$1(features, this.genome, genomicInterval); // If track is marked "searchable"< cache features by name -- use this with caution, memory intensive
56501
56500
 
56502
56501
  if (this.config.searchable || this.config.searchableFields) {
56503
56502
  this.addFeaturesToDB(features);
56504
56503
  }
56505
56504
  } else {
56506
- this.featureCache = new FeatureCache([], genomicInterval); // Empty cache
56505
+ this.featureCache = new FeatureCache$1([], genomicInterval); // Empty cache
56507
56506
  }
56508
56507
  }
56509
56508
 
@@ -56727,8 +56726,8 @@
56727
56726
 
56728
56727
  //table chromatinInteract
56729
56728
 
56730
- function getDecoder(definedFieldCount, fieldCount, autoSql) {
56731
- if (autoSql && 'chromatinInteract' === autoSql.table) {
56729
+ function getDecoder(definedFieldCount, fieldCount, autoSql, format) {
56730
+ if (autoSql && 'chromatinInteract' === autoSql.table || "biginteract" === format) {
56732
56731
  return decodeInteract;
56733
56732
  } else {
56734
56733
  const standardFieldCount = definedFieldCount - 3;
@@ -56863,6 +56862,7 @@
56863
56862
  class BWReader {
56864
56863
  constructor(config, genome) {
56865
56864
  this.path = config.url;
56865
+ this.format = config.format || "bigwig";
56866
56866
  this.genome = genome;
56867
56867
  this.rpTreeCache = {};
56868
56868
  this.config = config;
@@ -57441,7 +57441,7 @@
57441
57441
  function getBedDataDecoder() {
57442
57442
  const minSize = 3 * 4 + 1; // Minimum # of bytes required for a bed record
57443
57443
 
57444
- const decoder = getDecoder(this.header.definedFieldCount, this.header.fieldCount, this.autoSql);
57444
+ const decoder = getDecoder(this.header.definedFieldCount, this.header.fieldCount, this.autoSql, this.format);
57445
57445
  return function (data, chrIdx1, bpStart, chrIdx2, bpEnd, featureArray, chrDict) {
57446
57446
  const binaryParser = new BinaryParser(data);
57447
57447
 
@@ -58361,7 +58361,7 @@
58361
58361
  function FeatureSource(config, genome) {
58362
58362
  const format = config.format ? config.format.toLowerCase() : undefined;
58363
58363
 
58364
- if ('bigwig' === format || 'bigbed' === format || 'bb' === format) {
58364
+ if ('bigwig' === format || 'bigbed' === format || 'bb' === format || "biginteract" === format) {
58365
58365
  return new BWSource(config, genome);
58366
58366
  } else if ("tdf" === format) {
58367
58367
  return new TDFSource(config, genome);
@@ -59273,14 +59273,12 @@
59273
59273
  this.featureType = 'numeric';
59274
59274
  this.paintAxis = paintAxis;
59275
59275
  const format = config.format ? config.format.toLowerCase() : config.format;
59276
+ this.flipAxis = config.flipAxis ? config.flipAxis : false;
59277
+ this.logScale = config.logScale ? config.logScale : false;
59276
59278
 
59277
59279
  if ("bigwig" === format) {
59278
- this.flipAxis = config.flipAxis ? config.flipAxis : false;
59279
- this.logScale = config.logScale ? config.logScale : false;
59280
59280
  this.featureSource = new BWSource(config, this.browser.genome);
59281
59281
  } else if ("tdf" === format) {
59282
- this.flipAxis = config.flipAxis ? config.flipAxis : false;
59283
- this.logScale = config.logScale ? config.logScale : false;
59284
59282
  this.featureSource = new TDFSource(config, this.browser.genome);
59285
59283
  } else {
59286
59284
  this.featureSource = FeatureSource(config, this.browser.genome);
@@ -60057,7 +60055,7 @@
60057
60055
 
60058
60056
  const sortHandler = sort => {
60059
60057
  const viewport = clickState.viewport;
60060
- const features = viewport.getCachedFeatures();
60058
+ const features = viewport.cachedFeatures;
60061
60059
  this.sortSamples(sort.chr, sort.start, sort.end, sort.direction, features);
60062
60060
  };
60063
60061
 
@@ -60163,10 +60161,16 @@
60163
60161
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
60164
60162
  * THE SOFTWARE.
60165
60163
  */
60164
+ /**
60165
+ * Represents 2 or more wig tracks overlaid on a common viewport.
60166
+ */
60166
60167
 
60167
60168
  class MergedTrack extends TrackBase {
60168
60169
  constructor(config, browser) {
60169
60170
  super(config, browser);
60171
+ this.type = "merged";
60172
+ this.featureType = 'numeric';
60173
+ this.paintAxis = paintAxis;
60170
60174
  }
60171
60175
 
60172
60176
  init(config) {
@@ -60177,21 +60181,6 @@
60177
60181
  super.init(config);
60178
60182
  }
60179
60183
 
60180
- get height() {
60181
- return this._height;
60182
- }
60183
-
60184
- set height(h) {
60185
- this._height = h;
60186
-
60187
- if (this.tracks) {
60188
- for (let t of this.tracks) {
60189
- t.height = h;
60190
- t.config.height = h;
60191
- }
60192
- }
60193
- }
60194
-
60195
60184
  async postInit() {
60196
60185
  this.tracks = [];
60197
60186
  const p = [];
@@ -60213,45 +60202,83 @@
60213
60202
  }
60214
60203
  }
60215
60204
 
60216
- this.height = this.config.height || 100;
60205
+ this.flipAxis = this.config.flipAxis ? this.config.flipAxis : false;
60206
+ this.logScale = this.config.logScale ? this.config.logScale : false;
60207
+ this.autoscale = this.config.autoscale || this.config.max === undefined;
60208
+
60209
+ if (!this.autoscale) {
60210
+ this.dataRange = {
60211
+ min: this.config.min || 0,
60212
+ max: this.config.max
60213
+ };
60214
+ }
60215
+
60216
+ for (let t of this.tracks) {
60217
+ t.autoscale = false;
60218
+ t.dataRange = this.dataRange;
60219
+ }
60220
+
60221
+ this.height = this.config.height || 50;
60217
60222
  return Promise.all(p);
60218
60223
  }
60219
60224
 
60225
+ get height() {
60226
+ return this._height;
60227
+ }
60228
+
60229
+ set height(h) {
60230
+ this._height = h;
60231
+
60232
+ if (this.tracks) {
60233
+ for (let t of this.tracks) {
60234
+ t.height = h;
60235
+ t.config.height = h;
60236
+ }
60237
+ }
60238
+ }
60239
+
60240
+ menuItemList() {
60241
+ let items = [];
60242
+
60243
+ if (this.flipAxis !== undefined) {
60244
+ items.push({
60245
+ label: "Flip y-axis",
60246
+ click: () => {
60247
+ this.flipAxis = !this.flipAxis;
60248
+ this.trackView.repaintViews();
60249
+ }
60250
+ });
60251
+ }
60252
+
60253
+ items = items.concat(MenuUtils.numericDataMenuItems(this.trackView));
60254
+ return items;
60255
+ }
60256
+
60220
60257
  async getFeatures(chr, bpStart, bpEnd, bpPerPixel) {
60221
60258
  const promises = this.tracks.map(t => t.getFeatures(chr, bpStart, bpEnd, bpPerPixel));
60222
60259
  return Promise.all(promises);
60223
60260
  }
60224
60261
 
60225
60262
  draw(options) {
60226
- var i, len, mergedFeatures, trackOptions, dataRange;
60227
- mergedFeatures = options.features; // Array of feature arrays, 1 for each track
60263
+ const mergedFeatures = options.features; // Array of feature arrays, 1 for each track
60228
60264
 
60229
- dataRange = autoscale(options.referenceFrame.chr, mergedFeatures); //IGVGraphics.fillRect(options.context, 0, options.pixelTop, options.pixelWidth, options.pixelHeight, {'fillStyle': "rgb(255, 255, 255)"});
60265
+ if (this.autoscale) {
60266
+ this.dataRange = autoscale(options.referenceFrame.chr, mergedFeatures);
60267
+ }
60230
60268
 
60231
- for (i = 0, len = this.tracks.length; i < len; i++) {
60232
- trackOptions = Object.assign({}, options);
60269
+ for (let i = 0, len = this.tracks.length; i < len; i++) {
60270
+ const trackOptions = Object.assign({}, options);
60233
60271
  trackOptions.features = mergedFeatures[i];
60234
- this.tracks[i].dataRange = dataRange;
60272
+ this.tracks[i].dataRange = this.dataRange;
60273
+ this.tracks[i].flipAxis = this.flipAxis;
60274
+ this.tracks[i].logScale = this.logScale;
60275
+ this.tracks[i].graphType = this.graphType;
60235
60276
  this.tracks[i].draw(trackOptions);
60236
60277
  }
60237
60278
  }
60238
60279
 
60239
- paintAxis(ctx, pixelWidth, pixelHeight) {
60240
- var i, len, autoscale, track;
60241
- autoscale = true; // Hardcoded for now
60242
-
60243
- for (i = 0, len = this.tracks.length; i < len; i++) {
60244
- track = this.tracks[i];
60245
-
60246
- if (typeof track.paintAxis === 'function') {
60247
- track.paintAxis(ctx, pixelWidth, pixelHeight);
60248
- if (autoscale) break;
60249
- }
60250
- }
60251
- }
60252
-
60253
60280
  popupData(clickState, features) {
60254
- const featuresArray = features || clickState.viewport.getCachedFeatures();
60281
+ const featuresArray = features || clickState.viewport.cachedFeatures;
60255
60282
 
60256
60283
  if (featuresArray && featuresArray.length === this.tracks.length) {
60257
60284
  // Array of feature arrays, 1 for each track
@@ -60269,39 +60296,23 @@
60269
60296
  }
60270
60297
 
60271
60298
  supportsWholeGenome() {
60272
- const b = this.tracks.every(track => track.supportsWholeGenome());
60273
- return b;
60299
+ return this.tracks.every(track => track.supportsWholeGenome());
60274
60300
  }
60275
60301
 
60276
60302
  }
60277
60303
 
60278
60304
  function autoscale(chr, featureArrays) {
60279
- var min = 0,
60280
- max = -Number.MAX_VALUE;
60281
- // if (chr === 'all') {
60282
- // allValues = [];
60283
- // featureArrays.forEach(function (features) {
60284
- // features.forEach(function (f) {
60285
- // if (!Number.isNaN(f.value)) {
60286
- // allValues.push(f.value);
60287
- // }
60288
- // });
60289
- // });
60290
- //
60291
- // min = Math.min(0, IGVMath.percentile(allValues, .1));
60292
- // max = IGVMath.percentile(allValues, 99.9);
60293
- //
60294
- // }
60295
- // else {
60305
+ let min = 0;
60306
+ let max = -Number.MAX_VALUE;
60296
60307
 
60297
- featureArrays.forEach(function (features, i) {
60298
- features.forEach(function (f) {
60308
+ for (let features of featureArrays) {
60309
+ for (let f of features) {
60299
60310
  if (typeof f.value !== 'undefined' && !Number.isNaN(f.value)) {
60300
60311
  min = Math.min(min, f.value);
60301
60312
  max = Math.max(max, f.value);
60302
60313
  }
60303
- });
60304
- }); // }
60314
+ }
60315
+ }
60305
60316
 
60306
60317
  return {
60307
60318
  min: min,
@@ -60885,20 +60896,21 @@
60885
60896
  const cachedFeatures = "all" === refFrame.chr ? this.featureSource.getAllFeatures() : this.featureSource.featureCache.queryFeatures(refFrame.chr, refFrame.start, refFrame.end); // inView features are simply features that have been drawn, i.e. have a drawState
60886
60897
 
60887
60898
  const inView = cachedFeatures.filter(f => f.drawState);
60888
- if (inView.length === 0) erturn;
60899
+ if (inView.length === 0) return;
60889
60900
  this.browser.circularViewVisible = true;
60890
- const chords = makeBedPEChords(inView); // for filtered set, distinguishing the chromosomes is more critical than tracks
60891
-
60892
- const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? this.color : getChrColor(refFrame.chr), 0.5);
60893
- const trackColor = IGVColor.addAlpha(this.color, 0.5); // name the chord set to include filtering information
60894
-
60895
- const encodedName = this.name.replaceAll(' ', '%20');
60896
- const chordSetName = "all" === refFrame.chr ? encodedName : `${encodedName} (${refFrame.chr}:${refFrame.start}-${refFrame.end} ; range:${this.dataRange.min}-${this.dataRange.max})`;
60897
- this.browser.circularView.addChords(chords, {
60898
- track: chordSetName,
60899
- color: chordSetColor,
60900
- trackColor: trackColor
60901
- });
60901
+ const chords = makeBedPEChords(inView);
60902
+ sendChords(chords, this, refFrame, 0.5); //
60903
+ //
60904
+ // // for filtered set, distinguishing the chromosomes is more critical than tracks
60905
+ // const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? this.color : getChrColor(refFrame.chr), 0.5)
60906
+ // const trackColor = IGVColor.addAlpha(this.color, 0.5)
60907
+ //
60908
+ // // name the chord set to include locus and filtering information
60909
+ // const encodedName = this.name.replaceAll(' ', '%20')
60910
+ // const chordSetName = "all" === refFrame.chr ?
60911
+ // encodedName :
60912
+ // `${encodedName} (${refFrame.chr}:${refFrame.start}-${refFrame.end} ; range:${this.dataRange.min}-${this.dataRange.max})`
60913
+ // this.browser.circularView.addChords(chords, {track: chordSetName, color: chordSetColor, trackColor: trackColor})
60902
60914
  }
60903
60915
 
60904
60916
  doAutoscale(features) {
@@ -60985,7 +60997,7 @@
60985
60997
  clickedFeatures(clickState, features) {
60986
60998
  // We use the cached features rather than method to avoid async load. If the
60987
60999
  // feature is not already loaded this won't work, but the user wouldn't be mousing over it either.
60988
- const featureList = features || clickState.viewport.getCachedFeatures();
61000
+ const featureList = features || clickState.viewport.cachedFeatures;
60989
61001
  const candidates = [];
60990
61002
 
60991
61003
  if (featureList) {
@@ -61571,7 +61583,7 @@
61571
61583
  variantColor = "gray";
61572
61584
  }
61573
61585
  } else if (this._color) {
61574
- variantColor = typeof this._color === "function" ? this._color(v) : this._color;
61586
+ variantColor = this.color;
61575
61587
  } else if ("NONVARIANT" === v.type) {
61576
61588
  variantColor = this.nonRefColor;
61577
61589
  } else if ("MIXED" === v.type) {
@@ -61583,6 +61595,10 @@
61583
61595
  return variantColor;
61584
61596
  }
61585
61597
 
61598
+ get color() {
61599
+ return this._color ? typeof this._color === "function" ? this._color(v) : this._color : this.defaultColor;
61600
+ }
61601
+
61586
61602
  clickedFeatures(clickState, features) {
61587
61603
  let featureList = super.clickedFeatures(clickState, features);
61588
61604
  const vGap = this.displayMode === 'EXPANDED' ? this.expandedVGap : this.squishedVGap;
@@ -61848,24 +61864,10 @@
61848
61864
  menuItems.push({
61849
61865
  label: 'Add SVs to circular view',
61850
61866
  click: () => {
61851
- const inView = [];
61852
61867
 
61853
61868
  for (let viewport of this.trackView.viewports) {
61854
- const refFrame = viewport.referenceFrame;
61855
-
61856
- for (let f of viewport.getCachedFeatures()) {
61857
- if (f.end >= refFrame.start && f.start <= refFrame.end) {
61858
- inView.push(f);
61859
- }
61860
- }
61869
+ this.sendChordsForViewport(viewport);
61861
61870
  }
61862
-
61863
- const chords = makeVCFChords(inView);
61864
- const color = IGVColor.addAlpha(this._color || this.defaultColor, 0.5);
61865
- this.browser.circularView.addChords(chords, {
61866
- track: this.name,
61867
- color: color
61868
- });
61869
61871
  }
61870
61872
  });
61871
61873
  }
@@ -61881,20 +61883,20 @@
61881
61883
  list.push({
61882
61884
  label: 'Add SVs to Circular View',
61883
61885
  click: () => {
61884
- const refFrame = viewport.referenceFrame;
61885
- const inView = "all" === refFrame.chr ? this.featureSource.getAllFeatures() : this.featureSource.featureCache.queryFeatures(refFrame.chr, refFrame.start, refFrame.end);
61886
- const chords = makeVCFChords(inView);
61887
- const color = IGVColor.addAlpha(this._color || this.defaultColor, 0.5);
61888
- this.browser.circularView.addChords(chords, {
61889
- track: this.name,
61890
- color: color
61891
- });
61886
+ this.sendChordsForViewport(viewport);
61892
61887
  }
61893
61888
  });
61894
61889
  list.push('<hr/>');
61895
61890
  return list;
61896
61891
  }
61897
61892
  }
61893
+
61894
+ sendChordsForViewport(viewport) {
61895
+ const refFrame = viewport.referenceFrame;
61896
+ const inView = "all" === refFrame.chr ? this.featureSource.getAllFeatures() : this.featureSource.featureCache.queryFeatures(refFrame.chr, refFrame.start, refFrame.end);
61897
+ const chords = makeVCFChords(inView);
61898
+ sendChords(chords, this, refFrame, 0.5);
61899
+ }
61898
61900
  /**
61899
61901
  * Create a "color by" checkbox menu item, optionally initially checked
61900
61902
  * @param menuItem
@@ -62197,7 +62199,7 @@
62197
62199
 
62198
62200
 
62199
62201
  popupData(clickState) {
62200
- let features = clickState.viewport.getCachedFeatures();
62202
+ let features = clickState.viewport.cachedFeatures;
62201
62203
  if (!features || features.length === 0) return [];
62202
62204
  const tolerance = 3;
62203
62205
  const tissue = this.name;
@@ -62543,7 +62545,7 @@
62543
62545
  popupData(clickState) {
62544
62546
  let data = [];
62545
62547
  const track = clickState.viewport.trackView.track;
62546
- const features = clickState.viewport.getCachedFeatures();
62548
+ const features = clickState.viewport.cachedFeatures;
62547
62549
 
62548
62550
  if (features) {
62549
62551
  let count = 0;
@@ -63232,7 +63234,7 @@
63232
63234
  if (!this.featureCache) {
63233
63235
  const options = buildOptions(this.config);
63234
63236
  const data = await igvxhr.loadString(this.config.url, options);
63235
- this.featureCache = new FeatureCache(parseBP(data), genome);
63237
+ this.featureCache = new FeatureCache$1(parseBP(data), genome);
63236
63238
  return this.featureCache.queryFeatures(chr, start, end);
63237
63239
  } else {
63238
63240
  return this.featureCache.queryFeatures(chr, start, end);
@@ -64475,6 +64477,15 @@
64475
64477
  this.id = guid$2();
64476
64478
  }
64477
64479
 
64480
+ extend(locus) {
64481
+ const newStart = Math.min(locus.start, this.start);
64482
+ const newEnd = Math.max(locus.end, this.end);
64483
+ const ratio = (newEnd - newStart) / (this.end - this.start);
64484
+ this.start = newStart;
64485
+ this.end = newEnd;
64486
+ this.bpPerPixel *= ratio;
64487
+ }
64488
+
64478
64489
  calculateEnd(pixels) {
64479
64490
  return this.start + this.bpPerPixel * pixels;
64480
64491
  }
@@ -64559,7 +64570,7 @@
64559
64570
  const viewChanged = start !== this.start || bpPerPixel !== this.bpPerPixel;
64560
64571
 
64561
64572
  if (viewChanged) {
64562
- await browser.updateViews(this);
64573
+ await browser.updateViews(true);
64563
64574
  }
64564
64575
  }
64565
64576
 
@@ -66524,7 +66535,12 @@
66524
66535
  }
66525
66536
  }
66526
66537
 
66527
- await this.loadTrackList(trackConfigurations);
66538
+ await this.loadTrackList(trackConfigurations); // The ruler track is not explicitly loaded, but needs updated nonetheless.
66539
+
66540
+ for (let rtv of this.trackViews.filter(tv => tv.track.type === 'ruler')) {
66541
+ rtv.updateViews();
66542
+ }
66543
+
66528
66544
  this.updateUIWithReferenceFrameList();
66529
66545
  }
66530
66546
 
@@ -66689,26 +66705,22 @@
66689
66705
  }
66690
66706
 
66691
66707
  async loadTrackList(configList) {
66692
- try {
66693
- const promises = [];
66694
-
66695
- for (let config of configList) {
66696
- promises.push(this.loadTrack(config, false));
66697
- }
66708
+ const promises = [];
66698
66709
 
66699
- const loadedTracks = await Promise.all(promises);
66700
- const groupAutoscaleViews = this.trackViews.filter(function (trackView) {
66701
- return trackView.track.autoscaleGroup;
66702
- });
66710
+ for (let config of configList) {
66711
+ promises.push(this.loadTrack(config));
66712
+ }
66703
66713
 
66704
- if (groupAutoscaleViews.length > 0) {
66705
- this.updateViews(groupAutoscaleViews);
66706
- }
66714
+ const loadedTracks = await Promise.all(promises);
66715
+ const groupAutoscaleViews = this.trackViews.filter(function (trackView) {
66716
+ return trackView.track.autoscaleGroup;
66717
+ });
66707
66718
 
66708
- return loadedTracks;
66709
- } finally {
66710
- await this.resize();
66719
+ if (groupAutoscaleViews.length > 0) {
66720
+ this.updateViews();
66711
66721
  }
66722
+
66723
+ return loadedTracks;
66712
66724
  }
66713
66725
 
66714
66726
  async loadROI(config) {
@@ -66722,7 +66734,9 @@
66722
66734
  }
66723
66735
  } else {
66724
66736
  this.roi.push(new ROI(config, this.genome));
66725
- }
66737
+ } // Force reload all views (force = true) to insure ROI features are loaded. Wasteful but this function is
66738
+ // rarely called.
66739
+
66726
66740
 
66727
66741
  await this.updateViews(true);
66728
66742
  }
@@ -66736,7 +66750,7 @@
66736
66750
  }
66737
66751
 
66738
66752
  for (let tv of this.trackViews) {
66739
- tv.updateViews(true);
66753
+ tv.repaintViews();
66740
66754
  }
66741
66755
  }
66742
66756
 
@@ -66744,7 +66758,7 @@
66744
66758
  this.roi = [];
66745
66759
 
66746
66760
  for (let tv of this.trackViews) {
66747
- tv.updateViews(true);
66761
+ tv.repaintViews();
66748
66762
  }
66749
66763
  }
66750
66764
 
@@ -66760,25 +66774,13 @@
66760
66774
  /**
66761
66775
  * Return a promise to load a track.
66762
66776
  *
66763
- * Each track is associated with the following DOM elements
66764
- *
66765
- * leftHandGutter - div on the left for track controls and legend
66766
- * contentDiv - a div element wrapping all the track content. Height can be > viewportDiv height
66767
- * viewportDiv - a div element through which the track is viewed. This might have a vertical scrollbar
66768
- * canvas - canvas element upon which the track is drawn. Child of contentDiv
66769
- *
66770
- * The width of all elements should be equal. Height of the viewportDiv is controlled by the user, but never
66771
- * greater than the contentDiv height. Height of contentDiv and canvas are equal, and governed by the data
66772
- * loaded.
66773
- *
66774
- *
66775
66777
  * @param config
66776
66778
  * @param doResize - undefined by default
66777
66779
  * @returns {*}
66778
66780
  */
66779
66781
 
66780
66782
 
66781
- async loadTrack(config, doResize) {
66783
+ async loadTrack(config) {
66782
66784
  // config might be json
66783
66785
  if (isString$3(config)) {
66784
66786
  config = JSON.parse(config);
@@ -66843,11 +66845,6 @@
66843
66845
 
66844
66846
  msg += ": " + config.url;
66845
66847
  Alert.presentAlert(new Error(msg), undefined);
66846
- } finally {
66847
- // TODO: If loadTrack() is called individually - not via loadTrackList() - call this.resize()
66848
- if (false === doResize) ; else {
66849
- await this.resize();
66850
- }
66851
66848
  }
66852
66849
  }
66853
66850
  /**
@@ -67080,40 +67077,7 @@
67080
67077
  this.navbarManager.navbarDidResize(this.$navigation.width(), isWGV);
67081
67078
  }
67082
67079
 
67083
- await this.resize();
67084
- }
67085
-
67086
- async resize() {
67087
- const viewportWidth = this.calculateViewportWidth(this.referenceFrameList.length);
67088
-
67089
- for (let referenceFrame of this.referenceFrameList) {
67090
- const index = this.referenceFrameList.indexOf(referenceFrame);
67091
- const {
67092
- chr,
67093
- genome
67094
- } = referenceFrame;
67095
- const {
67096
- bpLength
67097
- } = genome.getChromosome(referenceFrame.chr);
67098
- const viewportWidthBP = referenceFrame.toBP(viewportWidth); // viewportWidthBP > bpLength occurs when locus is full chromosome and user widens browser
67099
-
67100
- if (GenomeUtils.isWholeGenomeView(chr) || viewportWidthBP > bpLength) {
67101
- // console.log(`${ Date.now() } Recalc referenceFrame(${ index }) bpp. viewport ${ StringUtils.numberFormatter(viewportWidthBP) } > ${ StringUtils.numberFormatter(bpLength) }.`)
67102
- referenceFrame.bpPerPixel = bpLength / viewportWidth;
67103
- } else {
67104
- // console.log(`${ Date.now() } Recalc referenceFrame(${ index }) end.`)
67105
- referenceFrame.end = referenceFrame.start + referenceFrame.toBP(viewportWidth);
67106
- }
67107
-
67108
- for (let {
67109
- viewports
67110
- } of this.trackViews) {
67111
- viewports[index].setWidth(viewportWidth);
67112
- }
67113
- }
67114
-
67115
- await this.updateViews(true);
67116
- this.updateUIWithReferenceFrameList();
67080
+ await resize.call(this);
67117
67081
  }
67118
67082
 
67119
67083
  async updateViews(force) {
@@ -67189,6 +67153,12 @@
67189
67153
  }
67190
67154
  }
67191
67155
 
67156
+ repaintViews() {
67157
+ for (let trackView of this.trackViews) {
67158
+ trackView.repaintViews();
67159
+ }
67160
+ }
67161
+
67192
67162
  updateLocusSearchWidget() {
67193
67163
  const referenceFrameList = this.referenceFrameList; // Update end position of reference frames based on pixel widths. This is hacky, but its been done here
67194
67164
  // for a long time, although indirectly.
@@ -67280,7 +67250,54 @@
67280
67250
  }
67281
67251
 
67282
67252
  this.centerLineList = this.createCenterLineList(this.columnContainer);
67283
- await this.resize();
67253
+ await resize.call(this);
67254
+ }
67255
+ /**
67256
+ * Add a new multi-locus panel for the specified region
67257
+ * @param chr
67258
+ * @param start
67259
+ * @param end
67260
+ * @param referenceFrameLeft - optional, if supplied new panel should be placed to the immediate right
67261
+ */
67262
+
67263
+
67264
+ async addMultiLocusPanel(chr, start, end, referenceFrameLeft) {
67265
+ // account for reduced viewport width as a result of adding right mate pair panel
67266
+ const viewportWidth = this.calculateViewportWidth(1 + this.referenceFrameList.length);
67267
+ const scaleFactor = this.calculateViewportWidth(this.referenceFrameList.length) / this.calculateViewportWidth(1 + this.referenceFrameList.length);
67268
+
67269
+ for (let refFrame of this.referenceFrameList) {
67270
+ refFrame.bpPerPixel *= scaleFactor;
67271
+ }
67272
+
67273
+ const bpp = (end - start) / viewportWidth;
67274
+ const newReferenceFrame = new ReferenceFrame(this.genome, chr, start, end, bpp);
67275
+ const indexLeft = referenceFrameLeft ? this.referenceFrameList.indexOf(referenceFrameLeft) : this.referenceFrameList.length - 1;
67276
+ const indexRight = 1 + indexLeft; // TODO -- this is really ugly
67277
+
67278
+ const {
67279
+ $viewport
67280
+ } = this.trackViews[0].viewports[indexLeft];
67281
+ const viewportColumn = viewportColumnManager.insertAfter($viewport.get(0).parentElement);
67282
+
67283
+ if (indexRight === this.referenceFrameList.length) {
67284
+ this.referenceFrameList.push(newReferenceFrame);
67285
+
67286
+ for (let trackView of this.trackViews) {
67287
+ const viewport = createViewport(trackView, viewportColumn, newReferenceFrame);
67288
+ trackView.viewports.push(viewport);
67289
+ }
67290
+ } else {
67291
+ this.referenceFrameList.splice(indexRight, 0, newReferenceFrame);
67292
+
67293
+ for (let trackView of this.trackViews) {
67294
+ const viewport = createViewport(trackView, viewportColumn, newReferenceFrame);
67295
+ trackView.viewports.splice(indexRight, 0, viewport);
67296
+ }
67297
+ }
67298
+
67299
+ this.centerLineList = this.createCenterLineList(this.columnContainer);
67300
+ resize.call(this);
67284
67301
  }
67285
67302
 
67286
67303
  async removeMultiLocusPanel(referenceFrame) {
@@ -67309,8 +67326,15 @@
67309
67326
  const scaleFactor = this.calculateViewportWidth(1 + this.referenceFrameList.length) / this.calculateViewportWidth(this.referenceFrameList.length);
67310
67327
  await this.rescaleForMultiLocus(scaleFactor);
67311
67328
  }
67329
+ /**
67330
+ * Goto the locus represented by the selected referenceFrame, discarding all other panels
67331
+ *
67332
+ * @param referenceFrame
67333
+ * @returns {Promise<void>}
67334
+ */
67312
67335
 
67313
- async selectMultiLocusPanel(referenceFrame) {
67336
+
67337
+ async gotoMultilocusPanel(referenceFrame) {
67314
67338
  const referenceFrameIndex = this.referenceFrameList.indexOf(referenceFrame); // Remove columns for unselected panels
67315
67339
 
67316
67340
  this.columnContainer.querySelectorAll('.igv-column').forEach((column, c) => {
@@ -67358,7 +67382,7 @@
67358
67382
 
67359
67383
  this.centerLineList = this.createCenterLineList(this.columnContainer);
67360
67384
  this.updateUIWithReferenceFrameList();
67361
- await this.updateViews(true);
67385
+ await this.updateViews();
67362
67386
  }
67363
67387
  /**
67364
67388
  * @deprecated This is a deprecated method with no known usages. To be removed in a future release.
@@ -67612,18 +67636,9 @@
67612
67636
  return surl;
67613
67637
  }
67614
67638
 
67615
- currentLoci() {
67616
- const loci = [];
67639
+ currentReferenceFrames() {
67617
67640
  const anyTrackView = this.trackViews[0];
67618
-
67619
- for (let {
67620
- referenceFrame
67621
- } of anyTrackView.viewports) {
67622
- const locusString = referenceFrame.getLocusString();
67623
- loci.push(locusString);
67624
- }
67625
-
67626
- return loci;
67641
+ return anyTrackView.viewports.map(vp => vp.referenceFrame);
67627
67642
  }
67628
67643
  /**
67629
67644
  * Record a mouse click on a specific viewport. This might be the start of a drag operation. Dragging
@@ -67736,12 +67751,9 @@
67736
67751
  }
67737
67752
 
67738
67753
  addWindowResizeHandler() {
67739
- this.boundWindowResizeHandler = windowResizeHandler.bind(this);
67754
+ // Create a copy of the prototype "resize" function bound to this instance. Neccessary to support removing.
67755
+ this.boundWindowResizeHandler = resize.bind(this);
67740
67756
  window.addEventListener('resize', this.boundWindowResizeHandler);
67741
-
67742
- function windowResizeHandler() {
67743
- this.resize();
67744
- }
67745
67757
  }
67746
67758
 
67747
67759
  removeWindowResizeHandler() {
@@ -67835,6 +67847,47 @@
67835
67847
  }
67836
67848
 
67837
67849
  }
67850
+ /**
67851
+ * Function called win window is resized, or visibility changed (e.g. "show" from a tab). This is a function rather
67852
+ * than class method because it needs to be copied and bound to specific instances of browser to support listener
67853
+ * removal
67854
+ *
67855
+ * @returns {Promise<void>}
67856
+ */
67857
+
67858
+
67859
+ async function resize() {
67860
+ const viewportWidth = this.calculateViewportWidth(this.referenceFrameList.length);
67861
+
67862
+ for (let referenceFrame of this.referenceFrameList) {
67863
+ const index = this.referenceFrameList.indexOf(referenceFrame);
67864
+ const {
67865
+ chr,
67866
+ genome
67867
+ } = referenceFrame;
67868
+ const {
67869
+ bpLength
67870
+ } = genome.getChromosome(referenceFrame.chr);
67871
+ const viewportWidthBP = referenceFrame.toBP(viewportWidth); // viewportWidthBP > bpLength occurs when locus is full chromosome and user widens browser
67872
+
67873
+ if (GenomeUtils.isWholeGenomeView(chr) || viewportWidthBP > bpLength) {
67874
+ // console.log(`${ Date.now() } Recalc referenceFrame(${ index }) bpp. viewport ${ StringUtils.numberFormatter(viewportWidthBP) } > ${ StringUtils.numberFormatter(bpLength) }.`)
67875
+ referenceFrame.bpPerPixel = bpLength / viewportWidth;
67876
+ } else {
67877
+ // console.log(`${ Date.now() } Recalc referenceFrame(${ index }) end.`)
67878
+ referenceFrame.end = referenceFrame.start + referenceFrame.toBP(viewportWidth);
67879
+ }
67880
+
67881
+ for (let {
67882
+ viewports
67883
+ } of this.trackViews) {
67884
+ viewports[index].setWidth(viewportWidth);
67885
+ }
67886
+ }
67887
+
67888
+ this.updateUIWithReferenceFrameList();
67889
+ await this.updateViews(true);
67890
+ }
67838
67891
 
67839
67892
  function handleMouseMove(e) {
67840
67893
  e.preventDefault();