igv 2.11.0 → 2.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/igv.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
@@ -20032,6 +20032,7 @@
20032
20032
 
20033
20033
  case "bedpe":
20034
20034
  case "bedpe-loop":
20035
+ case "biginteract":
20035
20036
  return "interact";
20036
20037
 
20037
20038
  case "bp":
@@ -20286,6 +20287,30 @@
20286
20287
  return window.location.protocol === "https:" || window.location.hostname === "localhost";
20287
20288
  }
20288
20289
 
20290
+ const pairs = [['A', 'T'], ['G', 'C'], ['Y', 'R'], ['W', 'S'], ['K', 'M'], ['D', 'H'], ['B', 'V']];
20291
+ const complements = new Map();
20292
+
20293
+ for (let p of pairs) {
20294
+ const p1 = p[0];
20295
+ const p2 = p[1];
20296
+ complements.set(p1, p2);
20297
+ complements.set(p2, p1);
20298
+ complements.set(p1.toLowerCase(), p2.toLowerCase());
20299
+ complements.set(p2.toLowerCase(), p1.toLowerCase());
20300
+ }
20301
+
20302
+ function reverseComplementSequence(sequence) {
20303
+ let comp = '';
20304
+ let idx = sequence.length;
20305
+
20306
+ while (idx-- > 0) {
20307
+ const base = sequence[idx];
20308
+ comp += complements.has(base) ? complements.get(base) : base;
20309
+ }
20310
+
20311
+ return comp;
20312
+ }
20313
+
20289
20314
  /*
20290
20315
  * The MIT License (MIT)
20291
20316
  *
@@ -20439,16 +20464,21 @@
20439
20464
  const viewport = clickState.viewport;
20440
20465
 
20441
20466
  if (viewport.referenceFrame.bpPerPixel <= 1) {
20467
+ const pixelWidth = viewport.getWidth();
20468
+ const bpWindow = pixelWidth * viewport.referenceFrame.bpPerPixel;
20469
+ const chr = viewport.referenceFrame.chr;
20470
+ const start = Math.floor(viewport.referenceFrame.start);
20471
+ const end = Math.ceil(start + bpWindow);
20442
20472
  const items = [{
20443
- label: 'View visible sequence...',
20473
+ label: this.reversed ? 'View visible sequence (reversed)...' : 'View visible sequence...',
20444
20474
  click: async () => {
20445
- const pixelWidth = viewport.getWidth();
20446
- const bpWindow = pixelWidth * viewport.referenceFrame.bpPerPixel;
20447
- const chr = viewport.referenceFrame.chr;
20448
- const start = viewport.referenceFrame.start;
20449
- const end = start + bpWindow;
20450
- const sequence = await this.browser.genome.sequence.getSequence(chr, start, end);
20451
- Alert.presentAlert(sequence);
20475
+ let seq = await this.browser.genome.sequence.getSequence(chr, start, end);
20476
+
20477
+ if (this.reversed) {
20478
+ seq = reverseComplementSequence(seq);
20479
+ }
20480
+
20481
+ Alert.presentAlert(seq);
20452
20482
  }
20453
20483
  }];
20454
20484
 
@@ -20456,13 +20486,18 @@
20456
20486
  items.push({
20457
20487
  label: 'Copy visible sequence',
20458
20488
  click: async () => {
20459
- const pixelWidth = viewport.getWidth();
20460
- const bpWindow = pixelWidth * viewport.referenceFrame.bpPerPixel;
20461
- const chr = viewport.referenceFrame.chr;
20462
- const start = viewport.referenceFrame.start;
20463
- const end = start + bpWindow;
20464
- const sequence = await this.browser.genome.sequence.getSequence(chr, start, end);
20465
- navigator.clipboard.writeText(sequence);
20489
+ let seq = await this.browser.genome.sequence.getSequence(chr, start, end);
20490
+
20491
+ if (this.reversed) {
20492
+ seq = reverseComplementSequence(seq);
20493
+ }
20494
+
20495
+ try {
20496
+ await navigator.clipboard.writeText(seq);
20497
+ } catch (e) {
20498
+ console.error(e);
20499
+ Alert.presentAlert(`error copying sequence to clipboard ${e}`);
20500
+ }
20466
20501
  }
20467
20502
  });
20468
20503
  }
@@ -20603,7 +20638,7 @@
20603
20638
  }
20604
20639
  }
20605
20640
 
20606
- supportsWholeGenome() {
20641
+ get supportsWholeGenome() {
20607
20642
  return false;
20608
20643
  }
20609
20644
 
@@ -20671,12 +20706,13 @@
20671
20706
  });
20672
20707
  this.$viewport.append(this.$content);
20673
20708
  this.$content.height(this.$viewport.height());
20674
- this.contentDiv = this.$content.get(0);
20675
- this.$canvas = $$1('<canvas>');
20676
- this.$content.append(this.$canvas);
20677
- this.canvas = this.$canvas.get(0);
20678
- this.ctx = this.canvas.getContext("2d");
20679
- this.setWidth(width);
20709
+ this.contentDiv = this.$content.get(0); // this.$canvas = $('<canvas>')
20710
+ // this.$content.append(this.$canvas)
20711
+ //
20712
+ // this.canvas = this.$canvas.get(0)
20713
+ // this.ctx = this.canvas.getContext("2d")
20714
+
20715
+ this.$viewport.width(width);
20680
20716
  this.initializationHelper();
20681
20717
  }
20682
20718
 
@@ -20732,14 +20768,13 @@
20732
20768
  console.log('Viewport - draw(drawConfiguration, features, roiFeatures)');
20733
20769
  }
20734
20770
 
20735
- checkContentHeight() {
20771
+ checkContentHeight(features) {
20736
20772
  let track = this.trackView.track;
20773
+ features = features || this.cachedFeatures;
20737
20774
 
20738
20775
  if ("FILL" === track.displayMode) {
20739
20776
  this.setContentHeight(this.$viewport.height());
20740
20777
  } else if (typeof track.computePixelHeight === 'function') {
20741
- let features = this.cachedFeatures;
20742
-
20743
20778
  if (features && features.length > 0) {
20744
20779
  let requiredContentHeight = track.computePixelHeight(features);
20745
20780
  let currentContentHeight = this.$content.height();
@@ -20759,7 +20794,6 @@
20759
20794
  // Maximum height of a canvas is ~32,000 pixels on Chrome, possibly smaller on other platforms
20760
20795
  contentHeight = Math.min(contentHeight, 32000);
20761
20796
  this.$content.height(contentHeight);
20762
- if (this.tile) this.tile.invalidate = true;
20763
20797
  }
20764
20798
 
20765
20799
  isLoading() {
@@ -20774,8 +20808,6 @@
20774
20808
 
20775
20809
  setWidth(width) {
20776
20810
  this.$viewport.width(width);
20777
- this.canvas.style.width = `${width}px`;
20778
- this.canvas.setAttribute('width', width);
20779
20811
  }
20780
20812
 
20781
20813
  getWidth() {
@@ -20803,7 +20835,6 @@
20803
20835
  this.popover.dispose();
20804
20836
  }
20805
20837
 
20806
- this.removeMouseHandlers();
20807
20838
  this.$viewport.get(0).remove(); // Null out all properties -- this should not be neccessary, but just in case there is a
20808
20839
  // reference to self somewhere we want to free memory.
20809
20840
 
@@ -23060,7 +23091,7 @@
23060
23091
  }
23061
23092
  };
23062
23093
 
23063
- const _version = "2.11.0";
23094
+ const _version = "2.12.0";
23064
23095
 
23065
23096
  function version$1() {
23066
23097
  return _version;
@@ -23410,6 +23441,7 @@
23410
23441
  }
23411
23442
 
23412
23443
  async getSequence(chr, start, end) {
23444
+ chr = this.getChromosomeName(chr);
23413
23445
  return this.sequence.getSequence(chr, start, end);
23414
23446
  }
23415
23447
 
@@ -23536,15 +23568,13 @@
23536
23568
  });
23537
23569
  this.$viewport.append(this.$spinner);
23538
23570
  this.$spinner.append($$1('<div>'));
23539
- const {
23540
- track
23541
- } = this.trackView;
23571
+ const track = this.trackView.track;
23542
23572
 
23543
23573
  if ('sequence' !== track.type) {
23544
23574
  this.$zoomInNotice = this.createZoomInNotice(this.$content);
23545
23575
  }
23546
23576
 
23547
- if (track.name && "sequence" !== track.config.type) {
23577
+ if (track.name && "sequence" !== track.id) {
23548
23578
  this.$trackLabel = $$1('<div class="igv-track-label">');
23549
23579
  this.$viewport.append(this.$trackLabel);
23550
23580
  this.setTrackLabel(track.name);
@@ -23558,6 +23588,11 @@
23558
23588
  this.addMouseHandlers();
23559
23589
  }
23560
23590
 
23591
+ setContentHeight(contentHeight) {
23592
+ super.setContentHeight(contentHeight);
23593
+ if (this.featureCache) this.featureCache.redraw = true;
23594
+ }
23595
+
23561
23596
  setTrackLabel(label) {
23562
23597
  this.$trackLabel.empty();
23563
23598
  this.$trackLabel.html(label);
@@ -23574,55 +23609,72 @@
23574
23609
  this.$spinner.hide();
23575
23610
  }
23576
23611
  }
23612
+ /**
23613
+ * Test to determine if we are zoomed in far enough to see features. Applicable to tracks with visibility windows.
23614
+ *
23615
+ * As a side effect the viewports canvas is removed if zoomed out.
23616
+ *
23617
+ * @returns {boolean} true if we are zoomed in past visibility window, false otherwise
23618
+ */
23577
23619
 
23578
- checkZoomIn() {
23579
- const showZoomInNotice = () => {
23580
- const referenceFrame = this.referenceFrame;
23581
23620
 
23582
- if (this.referenceFrame.chr.toLowerCase() === "all" && !this.trackView.track.supportsWholeGenome()) {
23621
+ checkZoomIn() {
23622
+ const zoomedOutOfWindow = () => {
23623
+ if (this.referenceFrame.chr.toLowerCase() === "all" && !this.trackView.track.supportsWholeGenome) {
23583
23624
  return true;
23584
23625
  } else {
23585
23626
  const visibilityWindow = this.trackView.track.visibilityWindow;
23586
- return visibilityWindow !== undefined && visibilityWindow > 0 && referenceFrame.bpPerPixel * this.$viewport.width() > visibilityWindow;
23627
+ return visibilityWindow !== undefined && visibilityWindow > 0 && this.referenceFrame.bpPerPixel * this.$viewport.width() > visibilityWindow;
23587
23628
  }
23588
23629
  };
23589
23630
 
23631
+ if (this.trackView.track && "sequence" === this.trackView.track.type && this.referenceFrame.bpPerPixel > 1) {
23632
+ $$1(this.canvas).remove();
23633
+ this.canvas = undefined; //this.featureCache = undefined
23634
+
23635
+ return false;
23636
+ }
23637
+
23590
23638
  if (!this.viewIsReady()) {
23591
23639
  return false;
23592
23640
  }
23593
23641
 
23594
- if (this.$zoomInNotice) {
23595
- if (showZoomInNotice()) {
23596
- // Out of visibility window
23597
- if (this.canvas) {
23598
- this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
23599
- this.tile = undefined;
23600
- }
23642
+ if (zoomedOutOfWindow()) {
23643
+ // Out of visibility window
23644
+ if (this.canvas) {
23645
+ $$1(this.canvas).remove();
23646
+ this.canvas = undefined; //this.featureCache = undefined
23647
+ }
23601
23648
 
23602
- this.$zoomInNotice.show();
23649
+ if (this.trackView.track.autoHeight) {
23650
+ const minHeight = this.trackView.minHeight || 0;
23651
+ this.setContentHeight(minHeight);
23652
+ }
23603
23653
 
23604
- if (this.trackView.track.autoHeight) {
23605
- const minHeight = this.trackView.minHeight || 0;
23606
- this.setContentHeight(minHeight);
23607
- }
23654
+ if (this.$zoomInNotice) {
23655
+ this.$zoomInNotice.show();
23656
+ }
23608
23657
 
23609
- return false;
23610
- } else {
23658
+ return false;
23659
+ } else {
23660
+ if (this.$zoomInNotice) {
23611
23661
  this.$zoomInNotice.hide();
23612
- return true;
23613
23662
  }
23614
- }
23615
23663
 
23616
- return true;
23664
+ return true;
23665
+ }
23617
23666
  }
23667
+ /**
23668
+ * Adjust the canvas to the current genomic state.
23669
+ */
23670
+
23618
23671
 
23619
23672
  shift() {
23620
- const self = this;
23621
- const referenceFrame = self.referenceFrame;
23673
+ const referenceFrame = this.referenceFrame;
23622
23674
 
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";
23675
+ if (this.canvas && this.canvas._data && this.canvas._data.chr === this.referenceFrame.chr && this.canvas._data.bpPerPixel === referenceFrame.bpPerPixel) {
23676
+ const pixelOffset = Math.round((this.canvas._data.startBP - referenceFrame.start) / referenceFrame.bpPerPixel);
23677
+ this.canvas.style.left = pixelOffset + "px";
23626
23678
  }
23627
23679
  }
23628
23680
 
@@ -23648,9 +23700,10 @@
23648
23700
  this.startSpinner();
23649
23701
 
23650
23702
  try {
23651
- const features = await this.getFeatures(this.trackView.track, chr, bpStart, bpEnd, referenceFrame.bpPerPixel);
23703
+ const track = this.trackView.track;
23704
+ const features = await this.getFeatures(track, chr, bpStart, bpEnd, referenceFrame.bpPerPixel);
23652
23705
  let roiFeatures = [];
23653
- const roi = mergeArrays(this.browser.roi, this.trackView.track.roi);
23706
+ const roi = mergeArrays(this.browser.roi, track.roi);
23654
23707
 
23655
23708
  if (roi) {
23656
23709
  for (let r of roi) {
@@ -23662,11 +23715,13 @@
23662
23715
  }
23663
23716
  }
23664
23717
 
23665
- this.tile = new Tile(chr, bpStart, bpEnd, referenceFrame.bpPerPixel, features, roiFeatures);
23718
+ const mr = track && ("wig" === track.type || "merged" === track.type); // wig tracks are potentially multiresolution (e.g. bigwig)
23719
+
23720
+ this.featureCache = new FeatureCache(chr, bpStart, bpEnd, referenceFrame.bpPerPixel, features, roiFeatures, mr);
23666
23721
  this.loading = false;
23667
23722
  this.hideMessage();
23668
23723
  this.stopSpinner();
23669
- return this.tile;
23724
+ return this.featureCache;
23670
23725
  } catch (error) {
23671
23726
  // Track might have been removed during load
23672
23727
  if (this.trackView && this.trackView.disposed !== true) {
@@ -23679,38 +23734,32 @@
23679
23734
  this.stopSpinner();
23680
23735
  }
23681
23736
  }
23737
+ /**
23738
+ * Repaint the canvas using the cached features
23739
+ *
23740
+ */
23682
23741
 
23683
- async repaint() {
23684
- if (undefined === this.tile) {
23742
+
23743
+ repaint() {
23744
+ if (undefined === this.featureCache) {
23685
23745
  return;
23686
23746
  }
23687
23747
 
23688
23748
  let {
23689
23749
  features,
23690
- roiFeatures,
23691
- bpPerPixel,
23692
- startBP,
23693
- endBP
23694
- } = this.tile; // const isWGV = GenomeUtils.isWholeGenomeView(this.browser.referenceFrameList[0].chr)
23695
-
23696
- const isWGV = GenomeUtils.isWholeGenomeView(this.referenceFrame.chr);
23697
- let pixelWidth;
23698
-
23699
- if (isWGV) {
23700
- bpPerPixel = this.referenceFrame.end / this.$viewport.width();
23701
- startBP = 0;
23702
- endBP = this.referenceFrame.end;
23703
- pixelWidth = this.$viewport.width();
23704
- } else {
23705
- pixelWidth = Math.ceil((endBP - startBP) / bpPerPixel);
23706
- } // For deep tracks we paint a canvas == 3*viewportHeight centered on the current vertical scroll position
23750
+ roiFeatures
23751
+ } = this.featureCache; //this.tile.bpPerPixel = this.referenceFrame.bpPerPixel
23752
+ // const isWGV = GenomeUtils.isWholeGenomeView(this.browser.referenceFrameList[0].chr)
23707
23753
 
23754
+ const isWGV = GenomeUtils.isWholeGenomeView(this.referenceFrame.chr); // Canvas dimensions. There is no left-right panning for WGV so canvas width is viewport width.
23755
+ // For deep tracks we paint a canvas == 3*viewportHeight centered on the current vertical scroll position
23708
23756
 
23757
+ const pixelWidth = isWGV ? this.$viewport.width() : 3 * this.$viewport.width();
23709
23758
  const viewportHeight = this.$viewport.height();
23710
23759
  const contentHeight = this.getContentHeight();
23711
23760
  const minHeight = roiFeatures ? Math.max(contentHeight, viewportHeight) : contentHeight; // Need to fill viewport for ROIs.
23712
23761
 
23713
- let pixelHeight = Math.min(minHeight, 3 * viewportHeight);
23762
+ const pixelHeight = Math.min(minHeight, 3 * viewportHeight);
23714
23763
 
23715
23764
  if (0 === pixelWidth || 0 === pixelHeight) {
23716
23765
  if (this.canvas) {
@@ -23720,26 +23769,22 @@
23720
23769
  return;
23721
23770
  }
23722
23771
 
23723
- const canvasTop = Math.max(0, -this.$content.position().top - viewportHeight); // Always use high DPI if in compressed display mode, otherwise use preference setting;
23724
-
23725
- let devicePixelRatio;
23726
-
23727
- if ("FILL" === this.trackView.track.displayMode) {
23728
- devicePixelRatio = window.devicePixelRatio;
23729
- } else {
23730
- devicePixelRatio = this.trackView.track.supportHiDPI === false ? 1 : window.devicePixelRatio;
23731
- }
23732
-
23733
- const pixelXOffset = Math.round((startBP - this.referenceFrame.start) / this.referenceFrame.bpPerPixel);
23772
+ const canvasTop = Math.max(0, -this.$content.position().top - viewportHeight);
23773
+ const bpPerPixel = this.referenceFrame.bpPerPixel;
23774
+ const startBP = this.referenceFrame.start - (isWGV ? 0 : pixelWidth / 3 * bpPerPixel);
23775
+ const endBP = this.referenceFrame.end + (isWGV ? 0 : pixelWidth / 3 * bpPerPixel);
23776
+ const pixelXOffset = Math.round((startBP - this.referenceFrame.start) / bpPerPixel);
23734
23777
  const newCanvas = $$1('<canvas class="igv-canvas">').get(0);
23735
- const ctx = newCanvas.getContext("2d");
23736
23778
  newCanvas.style.width = pixelWidth + "px";
23737
23779
  newCanvas.style.height = pixelHeight + "px";
23780
+ newCanvas.style.left = pixelXOffset + "px";
23781
+ newCanvas.style.top = canvasTop + "px"; // Always use high DPI if in "FILL" display mode, otherwise use track setting;
23782
+
23783
+ const devicePixelRatio = "FILL" === this.trackView.track.displayMode || this.trackView.track.supportHiDPI !== false ? window.devicePixelRatio : 1;
23738
23784
  newCanvas.width = devicePixelRatio * pixelWidth;
23739
23785
  newCanvas.height = devicePixelRatio * pixelHeight;
23786
+ const ctx = newCanvas.getContext("2d");
23740
23787
  ctx.scale(devicePixelRatio, devicePixelRatio);
23741
- newCanvas.style.left = pixelXOffset + "px";
23742
- newCanvas.style.top = canvasTop + "px";
23743
23788
  ctx.translate(0, -canvasTop);
23744
23789
  const drawConfiguration = {
23745
23790
  context: ctx,
@@ -23756,20 +23801,32 @@
23756
23801
  viewportWidth: this.$viewport.width()
23757
23802
  };
23758
23803
  this.draw(drawConfiguration, features, roiFeatures);
23759
- this.canvasVerticalRange = {
23760
- top: canvasTop,
23761
- bottom: canvasTop + pixelHeight
23762
- };
23804
+ this.featureCache.canvasTop = canvasTop;
23805
+ this.featureCache.height = pixelHeight;
23763
23806
 
23764
- if (this.$canvas) {
23765
- this.$canvas.remove();
23807
+ if (this.canvas) {
23808
+ $$1(this.canvas).remove();
23766
23809
  }
23767
23810
 
23768
- this.$canvas = $$1(newCanvas);
23769
- this.$content.append(this.$canvas);
23811
+ newCanvas._data = {
23812
+ chr: this.featureCache.chr,
23813
+ bpPerPixel,
23814
+ startBP,
23815
+ endBP,
23816
+ pixelHeight,
23817
+ pixelTop: canvasTop
23818
+ };
23770
23819
  this.canvas = newCanvas;
23771
- this.ctx = ctx;
23820
+ this.$content.append($$1(newCanvas));
23772
23821
  }
23822
+ /**
23823
+ * Draw the associated track.
23824
+ *
23825
+ * @param drawConfiguration
23826
+ * @param features
23827
+ * @param roiFeatures
23828
+ */
23829
+
23773
23830
 
23774
23831
  draw(drawConfiguration, features, roiFeatures) {
23775
23832
  // console.log(`${ Date.now() } viewport draw(). track ${ this.trackView.track.type }. content-css-top ${ this.$content.css('top') }. canvas-top ${ drawConfiguration.pixelTop }.`)
@@ -23784,51 +23841,6 @@
23784
23841
  r.track.draw(drawConfiguration);
23785
23842
  }
23786
23843
  }
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
23844
  }
23833
23845
 
23834
23846
  containsPosition(chr, position) {
@@ -23843,15 +23855,17 @@
23843
23855
  return this.loading;
23844
23856
  }
23845
23857
 
23846
- saveImage() {
23847
- if (!this.ctx) return;
23848
- const canvasTop = this.canvasVerticalRange ? this.canvasVerticalRange.top : 0;
23858
+ savePNG() {
23859
+ if (!this.canvas) return;
23860
+ const canvasMetadata = this.featureCache;
23861
+ const canvasTop = canvasMetadata ? canvasMetadata.canvasTop : 0;
23849
23862
  const devicePixelRatio = window.devicePixelRatio;
23850
23863
  const w = this.$viewport.width() * devicePixelRatio;
23851
23864
  const h = this.$viewport.height() * devicePixelRatio;
23852
23865
  const x = -$$1(this.canvas).position().left * devicePixelRatio;
23853
23866
  const y = (-this.$content.position().top - canvasTop) * devicePixelRatio;
23854
- const imageData = this.ctx.getImageData(x, y, w, h);
23867
+ const ctx = this.canvas.getContext("2d");
23868
+ const imageData = ctx.getImageData(x, y, w, h);
23855
23869
  const exportCanvas = document.createElement('canvas');
23856
23870
  const exportCtx = exportCanvas.getContext('2d');
23857
23871
  exportCanvas.width = imageData.width;
@@ -23967,29 +23981,44 @@
23967
23981
  viewportWidth: width,
23968
23982
  selection: this.selection
23969
23983
  };
23970
- const features = this.tile ? this.tile.features : [];
23971
- const roiFeatures = this.tile ? this.tile.roiFeatures : undefined;
23984
+ const features = this.featureCache ? this.featureCache.features : [];
23985
+ const roiFeatures = this.featureCache ? this.featureCache.roiFeatures : undefined;
23972
23986
  this.draw(config, features, roiFeatures);
23973
23987
  context.restore();
23974
23988
  }
23975
23989
 
23976
- getCachedFeatures() {
23977
- return this.tile ? this.tile.features : [];
23990
+ get cachedFeatures() {
23991
+ return this.featureCache ? this.featureCache.features : [];
23978
23992
  }
23979
23993
 
23980
23994
  async getFeatures(track, chr, start, end, bpPerPixel) {
23981
- if (this.tile && this.tile.containsRange(chr, start, end, bpPerPixel)) {
23982
- return this.tile.features;
23995
+ if (this.featureCache && this.featureCache.containsRange(chr, start, end, bpPerPixel)) {
23996
+ return this.featureCache.features;
23983
23997
  } else if (typeof track.getFeatures === "function") {
23984
23998
  const features = await track.getFeatures(chr, start, end, bpPerPixel, this);
23985
- this.cachedFeatures = features;
23986
- this.checkContentHeight();
23999
+ this.checkContentHeight(features);
23987
24000
  return features;
23988
24001
  } else {
23989
24002
  return undefined;
23990
24003
  }
23991
24004
  }
23992
24005
 
24006
+ needsRepaint() {
24007
+ if (!this.canvas) return true;
24008
+ const data = this.canvas._data;
24009
+ return !data || this.referenceFrame.start < data.startBP || this.referenceFrame.end > data.endBP || this.referenceFrame.chr !== data.chr || this.referenceFrame.bpPerPixel != data.bpPerPixel;
24010
+ }
24011
+
24012
+ needsReload() {
24013
+ if (!this.featureCache) return true;
24014
+ const referenceFrame = this.referenceFrame;
24015
+ const chr = this.referenceFrame.chr;
24016
+ const start = referenceFrame.start;
24017
+ const end = start + referenceFrame.toBP($$1(this.contentDiv).width());
24018
+ const bpPerPixel = referenceFrame.bpPerPixel;
24019
+ return !this.featureCache.containsRange(chr, start, end, bpPerPixel);
24020
+ }
24021
+
23993
24022
  createZoomInNotice($parent) {
23994
24023
  const $container = $$1('<div>', {
23995
24024
  class: 'igv-zoom-in-notice-container'
@@ -24007,36 +24036,42 @@
24007
24036
  }
24008
24037
 
24009
24038
  addMouseHandlers() {
24010
- this.addViewportContextMenuHandler(this.$viewport.get(0));
24011
- this.addViewportMouseDownHandler(this.$viewport.get(0));
24012
- this.addViewportTouchStartHandler(this.$viewport.get(0));
24013
- this.addViewportMouseUpHandler(this.$viewport.get(0));
24014
- this.addViewportTouchEndHandler(this.$viewport.get(0));
24015
- this.addViewportClickHandler(this.$viewport.get(0));
24039
+ const viewport = this.$viewport.get(0);
24040
+ this.addViewportContextMenuHandler(viewport);
24016
24041
 
24017
- if (this.trackView.track.name && "sequence" !== this.trackView.track.config.type) {
24018
- this.addTrackLabelClickHandler(this.$trackLabel.get(0));
24019
- }
24020
- }
24042
+ const md = event => {
24043
+ this.enableClick = true;
24044
+ this.browser.mouseDownOnViewport(event, this);
24045
+ pageCoordinates$1(event);
24046
+ };
24021
24047
 
24022
- removeMouseHandlers() {
24023
- this.removeViewportContextMenuHandler(this.$viewport.get(0));
24024
- this.removeViewportMouseDownHandler(this.$viewport.get(0));
24025
- this.removeViewportTouchStartHandler(this.$viewport.get(0));
24026
- this.removeViewportMouseUpHandler(this.$viewport.get(0));
24027
- this.removeViewportTouchEndHandler(this.$viewport.get(0));
24028
- this.removeViewportClickHandler(this.$viewport.get(0));
24048
+ viewport.addEventListener('mousedown', md);
24049
+ viewport.addEventListener('touchstart', md);
24050
+
24051
+ const mu = event => {
24052
+ // Any mouse up cancels drag and scrolling
24053
+ if (this.browser.dragObject || this.browser.isScrolling) {
24054
+ this.browser.cancelTrackPan(); // event.preventDefault();
24055
+ // event.stopPropagation();
24056
+
24057
+ this.enableClick = false; // Until next mouse down
24058
+ } else {
24059
+ this.browser.cancelTrackPan();
24060
+ this.browser.endTrackDrag();
24061
+ }
24062
+ };
24063
+
24064
+ viewport.addEventListener('mouseup', mu);
24065
+ viewport.addEventListener('touchend', mu);
24066
+ this.addViewportClickHandler(this.$viewport.get(0));
24029
24067
 
24030
24068
  if (this.trackView.track.name && "sequence" !== this.trackView.track.config.type) {
24031
- this.removeTrackLabelClickHandler(this.$trackLabel.get(0));
24069
+ this.addTrackLabelClickHandler(this.$trackLabel.get(0));
24032
24070
  }
24033
24071
  }
24034
24072
 
24035
24073
  addViewportContextMenuHandler(viewport) {
24036
- this.boundContextMenuHandler = contextMenuHandler.bind(this);
24037
- viewport.addEventListener('contextmenu', this.boundContextMenuHandler);
24038
-
24039
- function contextMenuHandler(event) {
24074
+ viewport.addEventListener('contextmenu', event => {
24040
24075
  // Ignore if we are doing a drag. This can happen with touch events.
24041
24076
  if (this.browser.dragObject) {
24042
24077
  return false;
@@ -24076,54 +24111,11 @@
24076
24111
  click: () => this.saveSVG()
24077
24112
  });
24078
24113
  this.browser.menuPopup.presentTrackContextMenu(event, menuItems);
24079
- }
24080
- }
24081
-
24082
- removeViewportContextMenuHandler(viewport) {
24083
- viewport.removeEventListener('contextmenu', this.boundContextMenuHandler);
24084
- }
24085
-
24086
- addViewportMouseDownHandler(viewport) {
24087
- this.boundMouseDownHandler = mouseDownHandler.bind(this);
24088
- viewport.addEventListener('mousedown', this.boundMouseDownHandler);
24089
- }
24090
-
24091
- removeViewportMouseDownHandler(viewport) {
24092
- viewport.removeEventListener('mousedown', this.boundMouseDownHandler);
24093
- }
24094
-
24095
- addViewportTouchStartHandler(viewport) {
24096
- this.boundTouchStartHandler = mouseDownHandler.bind(this);
24097
- viewport.addEventListener('touchstart', this.boundTouchStartHandler);
24098
- }
24099
-
24100
- removeViewportTouchStartHandler(viewport) {
24101
- viewport.removeEventListener('touchstart', this.boundTouchStartHandler);
24102
- }
24103
-
24104
- addViewportMouseUpHandler(viewport) {
24105
- this.boundMouseUpHandler = mouseUpHandler.bind(this);
24106
- viewport.addEventListener('mouseup', this.boundMouseUpHandler);
24107
- }
24108
-
24109
- removeViewportMouseUpHandler(viewport) {
24110
- viewport.removeEventListener('mouseup', this.boundMouseUpHandler);
24111
- }
24112
-
24113
- addViewportTouchEndHandler(viewport) {
24114
- this.boundTouchEndHandler = mouseUpHandler.bind(this);
24115
- viewport.addEventListener('touchend', this.boundTouchEndHandler);
24116
- }
24117
-
24118
- removeViewportTouchEndHandler(viewport) {
24119
- viewport.removeEventListener('touchend', this.boundTouchEndHandler);
24114
+ });
24120
24115
  }
24121
24116
 
24122
24117
  addViewportClickHandler(viewport) {
24123
- this.boundClickHandler = clickHandler.bind(this);
24124
- viewport.addEventListener('click', this.boundClickHandler);
24125
-
24126
- function clickHandler(event) {
24118
+ viewport.addEventListener('click', event => {
24127
24119
  if (this.enableClick) {
24128
24120
  if (3 === event.which || event.ctrlKey) {
24129
24121
  return;
@@ -24198,18 +24190,11 @@
24198
24190
 
24199
24191
  lastClickTime = time;
24200
24192
  }
24201
- }
24202
- }
24203
-
24204
- removeViewportClickHandler(viewport) {
24205
- viewport.removeEventListener('click', this.boundClickHandler);
24193
+ });
24206
24194
  }
24207
24195
 
24208
24196
  addTrackLabelClickHandler(trackLabel) {
24209
- this.boundTrackLabelClickHandler = clickHandler.bind(this);
24210
- trackLabel.addEventListener('click', this.boundTrackLabelClickHandler);
24211
-
24212
- function clickHandler(event) {
24197
+ trackLabel.addEventListener('click', event => {
24213
24198
  event.stopPropagation();
24214
24199
  const {
24215
24200
  track
@@ -24230,44 +24215,16 @@
24230
24215
  this.popover = new Popover(this.browser.columnContainer, track.name || '');
24231
24216
  this.popover.presentContentWithEvent(event, str);
24232
24217
  }
24233
- }
24234
- }
24235
-
24236
- removeTrackLabelClickHandler(trackLabel) {
24237
- trackLabel.removeEventListener('click', this.boundTrackLabelClickHandler);
24218
+ });
24238
24219
  }
24239
24220
 
24240
24221
  }
24241
24222
 
24242
- function mouseDownHandler(event) {
24243
- this.enableClick = true;
24244
- this.browser.mouseDownOnViewport(event, this);
24245
- pageCoordinates$1(event);
24246
- }
24247
-
24248
- function mouseUpHandler(event) {
24249
- // Any mouse up cancels drag and scrolling
24250
- if (this.browser.dragObject || this.browser.isScrolling) {
24251
- this.browser.cancelTrackPan(); // event.preventDefault();
24252
- // event.stopPropagation();
24253
-
24254
- this.enableClick = false; // Until next mouse down
24255
- } else {
24256
- this.browser.cancelTrackPan();
24257
- this.browser.endTrackDrag();
24258
- }
24259
- }
24260
-
24261
24223
  function createClickState(event, viewport) {
24262
24224
  const referenceFrame = viewport.referenceFrame;
24263
24225
  const viewportCoords = translateMouseCoordinates$1(event, viewport.contentDiv);
24264
24226
  const canvasCoords = translateMouseCoordinates$1(event, viewport.canvas);
24265
24227
  const genomicLocation = referenceFrame.start + referenceFrame.toBP(viewportCoords.x);
24266
-
24267
- if (undefined === genomicLocation || null === viewport.tile) {
24268
- return undefined;
24269
- }
24270
-
24271
24228
  return {
24272
24229
  event,
24273
24230
  viewport,
@@ -24321,22 +24278,28 @@
24321
24278
  return rows.join('');
24322
24279
  }
24323
24280
 
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
- };
24281
+ class FeatureCache {
24282
+ constructor(chr, tileStart, tileEnd, bpPerPixel, features, roiFeatures, multiresolution) {
24283
+ this.chr = chr;
24284
+ this.startBP = tileStart;
24285
+ this.endBP = tileEnd;
24286
+ this.bpPerPixel = bpPerPixel;
24287
+ this.features = features;
24288
+ this.roiFeatures = roiFeatures;
24289
+ this.multiresolution = multiresolution;
24290
+ }
24332
24291
 
24333
- Tile.prototype.containsRange = function (chr, start, end, bpPerPixel) {
24334
- return this.bpPerPixel === bpPerPixel && start >= this.startBP && end <= this.endBP && chr === this.chr;
24335
- };
24292
+ containsRange(chr, start, end, bpPerPixel) {
24293
+ // For multi-resolution tracks allow for a 2X change in bpPerPixel
24294
+ const r = this.multiresolution ? this.bpPerPixel / bpPerPixel : 1;
24295
+ return start >= this.startBP && end <= this.endBP && chr === this.chr && r > 0.5 && r < 2;
24296
+ }
24336
24297
 
24337
- Tile.prototype.overlapsRange = function (chr, start, end) {
24338
- return this.chr === chr && end >= this.startBP && start <= this.endBP;
24339
- };
24298
+ overlapsRange(chr, start, end) {
24299
+ return this.chr === chr && end >= this.startBP && start <= this.endBP;
24300
+ }
24301
+
24302
+ }
24340
24303
  /**
24341
24304
  * Merge 2 arrays. a and/or b can be undefined. If both are undefined, return undefined
24342
24305
  * @param a An array or undefined
@@ -24475,10 +24438,12 @@
24475
24438
  end: bp(this.rulerViewport.referenceFrame, left + width)
24476
24439
  };
24477
24440
  validateLocusExtent(this.rulerViewport.browser.genome.getChromosome(this.rulerViewport.referenceFrame.chr).bpLength, extent, this.rulerViewport.browser.minimumBases());
24478
- this.rulerViewport.referenceFrame.bpPerPixel = (Math.round(extent.end) - Math.round(extent.start)) / this.rulerViewport.contentDiv.clientWidth;
24479
- this.rulerViewport.referenceFrame.start = Math.round(extent.start);
24480
- this.rulerViewport.referenceFrame.end = Math.round(extent.end);
24481
- this.rulerViewport.browser.updateViews(this.rulerViewport.referenceFrame);
24441
+ const newStart = Math.round(extent.start);
24442
+ const newEnd = Math.round(extent.end);
24443
+ this.rulerViewport.referenceFrame.bpPerPixel = (newEnd - newStart) / this.rulerViewport.contentDiv.clientWidth;
24444
+ this.rulerViewport.referenceFrame.start = newStart;
24445
+ this.rulerViewport.referenceFrame.end = newEnd;
24446
+ this.rulerViewport.browser.updateViews();
24482
24447
  }
24483
24448
  }
24484
24449
  }
@@ -25480,6 +25445,18 @@
25480
25445
  return true; // By definition
25481
25446
  }
25482
25447
 
25448
+ isMateMapped() {
25449
+ return true; // By definition
25450
+ }
25451
+
25452
+ isProperPair() {
25453
+ return this.firstAlignment.isProperPair();
25454
+ }
25455
+
25456
+ get fragmentLength() {
25457
+ return Math.abs(this.firstAlignment.fragmentLength);
25458
+ }
25459
+
25483
25460
  firstOfPairStrand() {
25484
25461
  if (this.firstAlignment.isFirstOfPair()) {
25485
25462
  return this.firstAlignment.strand;
@@ -25846,7 +25823,15 @@
25846
25823
  */
25847
25824
 
25848
25825
  class AlignmentContainer {
25849
- constructor(chr, start, end, samplingWindowSize, samplingDepth, pairsSupported, alleleFreqThreshold) {
25826
+ // this.config.samplingWindowSize, this.config.samplingDepth,
25827
+ // this.config.pairsSupported, this.config.alleleFreqThreshold)
25828
+ constructor(chr, start, end, _ref) {
25829
+ let {
25830
+ samplingWindowSize,
25831
+ samplingDepth,
25832
+ pairsSupported,
25833
+ alleleFreqThreshold
25834
+ } = _ref;
25850
25835
  this.chr = chr;
25851
25836
  this.start = Math.floor(start);
25852
25837
  this.end = Math.ceil(end);
@@ -25869,17 +25854,10 @@
25869
25854
  // TODO -- pass this in
25870
25855
  return alignment.isMapped() && !alignment.isFailsVendorQualityCheck();
25871
25856
  };
25872
-
25873
- this.pairedEndStats = new PairedEndStats();
25874
25857
  }
25875
25858
 
25876
25859
  push(alignment) {
25877
25860
  if (this.filter(alignment) === false) return;
25878
-
25879
- if (alignment.isPaired()) {
25880
- this.pairedEndStats.push(alignment);
25881
- }
25882
-
25883
25861
  this.coverageMap.incCounts(alignment); // Count coverage before any downsampling
25884
25862
 
25885
25863
  if (this.pairsSupported && this.downsampledReads.has(alignment.readName)) {
@@ -25908,7 +25886,6 @@
25908
25886
  });
25909
25887
  this.pairsCache = undefined;
25910
25888
  this.downsampledReads = undefined;
25911
- this.pairedEndStats.compute();
25912
25889
  }
25913
25890
 
25914
25891
  contains(chr, start, end) {
@@ -26203,62 +26180,6 @@
26203
26180
 
26204
26181
  }
26205
26182
 
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
26183
  class SupplementaryAlignment {
26263
26184
  constructor(rec) {
26264
26185
  const tokens = rec.split(',');
@@ -27021,7 +26942,7 @@
27021
26942
  const lseq = readInt(ba, offset + 20);
27022
26943
  const mateChrIdx = readInt(ba, offset + 24);
27023
26944
  const matePos = readInt(ba, offset + 28);
27024
- const tlen = readInt(ba, offset + 32);
26945
+ const fragmentLength = readInt(ba, offset + 32);
27025
26946
  let readName = [];
27026
26947
 
27027
26948
  for (let j = 0; j < nl - 1; ++j) {
@@ -27062,7 +26983,7 @@
27062
26983
  alignment.readName = readName;
27063
26984
  alignment.cigar = cigar;
27064
26985
  alignment.lengthOnRef = lengthOnRef;
27065
- alignment.fragmentLength = tlen;
26986
+ alignment.fragmentLength = fragmentLength;
27066
26987
  alignment.mq = mq;
27067
26988
  BamUtils.bam_tag2cigar(ba, blockEnd, p, lseq, alignment, cigarArray);
27068
26989
  alignment.end = alignment.start + alignment.lengthOnRef;
@@ -27462,7 +27383,7 @@
27462
27383
  const header = this.header;
27463
27384
  const queryChr = header.chrAliasTable.hasOwnProperty(chr) ? header.chrAliasTable[chr] : chr;
27464
27385
  const qAlignments = this.alignmentCache.queryFeatures(queryChr, bpStart, bpEnd);
27465
- const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.samplingWindowSize, this.samplingDepth, this.pairsSupported, this.alleleFreqThreshold);
27386
+ const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config);
27466
27387
 
27467
27388
  for (let a of qAlignments) {
27468
27389
  alignmentContainer.push(a);
@@ -27489,13 +27410,13 @@
27489
27410
  const alignments = [];
27490
27411
  this.header = BamUtils.decodeBamHeader(data);
27491
27412
  BamUtils.decodeBamRecords(data, this.header.size, alignments, this.header.chrNames);
27492
- this.alignmentCache = new FeatureCache(alignments, this.genome);
27413
+ this.alignmentCache = new FeatureCache$1(alignments, this.genome);
27493
27414
  }
27494
27415
 
27495
27416
  fetchAlignments(chr, bpStart, bpEnd) {
27496
27417
  const queryChr = this.header.chrAliasTable.hasOwnProperty(chr) ? this.header.chrAliasTable[chr] : chr;
27497
27418
  const features = this.alignmentCache.queryFeatures(queryChr, bpStart, bpEnd);
27498
- const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.samplingWindowSize, this.samplingDepth, this.pairsSupported);
27419
+ const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config);
27499
27420
 
27500
27421
  for (let feature of features) {
27501
27422
  alignmentContainer.push(feature);
@@ -28495,7 +28416,7 @@
28495
28416
  const chrToIndex = await this.getChrIndex();
28496
28417
  const queryChr = this.chrAliasTable.hasOwnProperty(chr) ? this.chrAliasTable[chr] : chr;
28497
28418
  const chrId = chrToIndex[queryChr];
28498
- const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config.samplingWindowSize, this.config.samplingDepth, this.config.pairsSupported, this.config.alleleFreqThreshold);
28419
+ const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config);
28499
28420
 
28500
28421
  if (chrId === undefined) {
28501
28422
  return alignmentContainer;
@@ -28644,7 +28565,7 @@
28644
28565
 
28645
28566
  async readAlignments(chr, start, end) {
28646
28567
  if (!this.bamReaders.hasOwnProperty(chr)) {
28647
- return new AlignmentContainer(chr, start, end);
28568
+ return new AlignmentContainer(chr, start, end, this.config);
28648
28569
  } else {
28649
28570
  let reader = this.bamReaders[chr];
28650
28571
  const a = await reader.readAlignments(chr, start, end);
@@ -28716,7 +28637,7 @@
28716
28637
  return igvxhr.loadString(url, buildOptions(self.config)).then(function (sam) {
28717
28638
  var alignmentContainer;
28718
28639
  header.chrToIndex[queryChr];
28719
- alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, self.samplingWindowSize, self.samplingDepth, self.pairsSupported, self.alleleFreqThreshold);
28640
+ alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, self.config);
28720
28641
  BamUtils.decodeSamRecords(sam, alignmentContainer, queryChr, bpStart, bpEnd, self.filter);
28721
28642
  return alignmentContainer;
28722
28643
  });
@@ -28926,7 +28847,7 @@
28926
28847
 
28927
28848
  const ba = unbgzf(compressedData.buffer);
28928
28849
  const chrIdx = this.header.chrToIndex[chr];
28929
- const alignmentContainer = new AlignmentContainer(chr, start, end, this.samplingWindowSize, this.samplingDepth, this.pairsSupported, this.alleleFreqThreshold);
28850
+ const alignmentContainer = new AlignmentContainer(chr, start, end, this.config);
28930
28851
  BamUtils.decodeBamRecords(ba, this.header.size, alignmentContainer, this.header.chrNames, chrIdx, start, end);
28931
28852
  alignmentContainer.finish();
28932
28853
  return alignmentContainer;
@@ -44464,7 +44385,7 @@
44464
44385
  const header = await this.getHeader();
44465
44386
  const queryChr = header.chrAliasTable.hasOwnProperty(chr) ? header.chrAliasTable[chr] : chr;
44466
44387
  const chrIdx = header.chrToIndex[queryChr];
44467
- const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.samplingWindowSize, this.samplingDepth, this.pairsSupported, this.alleleFreqThreshold);
44388
+ const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config);
44468
44389
 
44469
44390
  if (chrIdx === undefined) {
44470
44391
  return alignmentContainer;
@@ -45073,7 +44994,7 @@
45073
44994
  "pageSize": "10000"
45074
44995
  },
45075
44996
  decode: decodeGa4ghReads,
45076
- results: new AlignmentContainer(chr, bpStart, bpEnd, self.samplingWindowSize, self.samplingDepth, self.pairsSupported, self.alleleFreqThreshold)
44997
+ results: new AlignmentContainer(chr, bpStart, bpEnd, self.config)
45077
44998
  });
45078
44999
  });
45079
45000
 
@@ -45380,7 +45301,6 @@
45380
45301
  const genome = browser.genome;
45381
45302
  this.config = config;
45382
45303
  this.genome = genome;
45383
- this.alignmentContainer = undefined;
45384
45304
 
45385
45305
  if (isDataURL(config.url)) {
45386
45306
  if ("cram" === config.format) {
@@ -45431,58 +45351,44 @@
45431
45351
  }
45432
45352
 
45433
45353
  setViewAsPairs(bool) {
45434
-
45435
- if (this.viewAsPairs !== bool) {
45436
- this.viewAsPairs = bool; // if (this.alignmentContainer) {
45437
- // this.alignmentContainer.setViewAsPairs(bool);
45438
- // }
45439
- }
45354
+ this.viewAsPairs = bool;
45440
45355
  }
45441
45356
 
45442
45357
  setShowSoftClips(bool) {
45443
- if (this.showSoftClips !== bool) {
45444
- this.showSoftClips = bool;
45445
- }
45358
+ this.showSoftClips = bool;
45446
45359
  }
45447
45360
 
45448
45361
  async getAlignments(chr, bpStart, bpEnd) {
45449
45362
  const genome = this.genome;
45450
45363
  const showSoftClips = this.showSoftClips;
45364
+ const alignmentContainer = await this.bamReader.readAlignments(chr, bpStart, bpEnd);
45365
+ let alignments = alignmentContainer.alignments;
45451
45366
 
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
45367
+ if (!this.viewAsPairs) {
45368
+ alignments = unpairAlignments([{
45369
+ alignments: alignments
45370
+ }]);
45371
+ }
45467
45372
 
45468
- this.alignmentContainer = alignmentContainer;
45373
+ const hasAlignments = alignments.length > 0;
45374
+ alignmentContainer.packedAlignmentRows = packAlignmentRows(alignments, alignmentContainer.start, alignmentContainer.end, showSoftClips);
45375
+ this.alignmentContainer = alignmentContainer;
45469
45376
 
45470
- if (hasAlignments) {
45471
- const sequence = await genome.sequence.getSequence(chr, alignmentContainer.start, alignmentContainer.end);
45377
+ if (hasAlignments) {
45378
+ const sequence = await genome.sequence.getSequence(chr, alignmentContainer.start, alignmentContainer.end);
45472
45379
 
45473
- if (sequence) {
45474
- alignmentContainer.coverageMap.refSeq = sequence; // TODO -- fix this
45380
+ if (sequence) {
45381
+ alignmentContainer.coverageMap.refSeq = sequence; // TODO -- fix this
45475
45382
 
45476
- alignmentContainer.sequence = sequence; // TODO -- fix this
45383
+ alignmentContainer.sequence = sequence; // TODO -- fix this
45477
45384
 
45478
- return alignmentContainer;
45479
- } else {
45480
- console.error("No sequence for: " + chr + ":" + alignmentContainer.start + "-" + alignmentContainer.end);
45481
- }
45385
+ return alignmentContainer;
45386
+ } else {
45387
+ console.error("No sequence for: " + chr + ":" + alignmentContainer.start + "-" + alignmentContainer.end);
45482
45388
  }
45483
-
45484
- return alignmentContainer;
45485
45389
  }
45390
+
45391
+ return alignmentContainer;
45486
45392
  }
45487
45393
 
45488
45394
  }
@@ -45666,7 +45572,7 @@
45666
45572
  return state;
45667
45573
  }
45668
45574
 
45669
- supportsWholeGenome() {
45575
+ get supportsWholeGenome() {
45670
45576
  return false;
45671
45577
  }
45672
45578
  /**
@@ -45827,7 +45733,7 @@
45827
45733
  clickedFeatures(clickState, features) {
45828
45734
  // We use the cached features rather than method to avoid async load. If the
45829
45735
  // 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();
45736
+ if (!features) features = clickState.viewport.cachedFeatures;
45831
45737
 
45832
45738
  if (!features || features.length === 0) {
45833
45739
  return [];
@@ -48466,52 +48372,125 @@
48466
48372
  return regions;
48467
48373
  }
48468
48374
 
48375
+ function sendChords(chords, track, refFrame, alpha) {
48376
+ const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? track.color : getChrColor(refFrame.chr), alpha);
48377
+ const trackColor = IGVColor.addAlpha(track.color || 'rgb(0,0,255)', alpha); // name the chord set to include locus and filtering information
48378
+
48379
+ const encodedName = track.name.replaceAll(' ', '%20');
48380
+ const chordSetName = "all" === refFrame.chr ? encodedName : `${encodedName} ${refFrame.chr}:${refFrame.start}-${refFrame.end}`;
48381
+ track.browser.circularView.addChords(chords, {
48382
+ track: chordSetName,
48383
+ color: chordSetColor,
48384
+ trackColor: trackColor
48385
+ }); // show circular view if hidden
48386
+
48387
+ if (!track.browser.circularViewVisible) track.browser.circularViewVisible = true;
48388
+ }
48389
+
48469
48390
  function createCircularView(el, browser) {
48470
48391
  const circularView = new CircularView(el, {
48471
48392
  onChordClick: (feature, chordTrack, pluginManager) => {
48472
48393
  const f1 = feature.data;
48473
48394
  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);
48395
+ addFrameForFeature(f1);
48396
+ addFrameForFeature(f2);
48397
+
48398
+ function addFrameForFeature(feature) {
48399
+ feature.chr = browser.genome.getChromosomeName(feature.refName);
48400
+ let frameFound = false;
48401
+
48402
+ for (let referenceFrame of browser.referenceFrameList) {
48403
+ const l = Locus.fromLocusString(referenceFrame.getLocusString());
48404
+
48405
+ if (l.contains(feature)) {
48406
+ frameFound = true;
48407
+ break;
48408
+ } else if (l.overlaps(feature)) {
48409
+ referenceFrame.extend(feature);
48410
+ frameFound = true;
48411
+ break;
48498
48412
  }
48499
48413
  }
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
- }
48507
48414
 
48508
- const searchString = loci.map(l => l.getLocusString()).join(" ");
48509
- browser.search(searchString);
48415
+ if (!frameFound) {
48416
+ const flanking = 2000;
48417
+ const center = (feature.start + feature.end) / 2;
48418
+ browser.addMultiLocusPanel(feature.chr, center - flanking, center + flanking);
48419
+ }
48420
+ }
48510
48421
  }
48511
48422
  });
48512
48423
  return circularView;
48513
48424
  }
48514
48425
 
48426
+ class PairedEndStats {
48427
+ constructor(alignments, _ref) {
48428
+ let {
48429
+ minTLENPercentile,
48430
+ maxTLENPercentile
48431
+ } = _ref;
48432
+ this.totalCount = 0;
48433
+ this.frCount = 0;
48434
+ this.rfCount = 0;
48435
+ this.ffCount = 0;
48436
+ this.sumF = 0;
48437
+ this.sumF2 = 0;
48438
+ this.lp = minTLENPercentile === undefined ? 0.1 : minTLENPercentile;
48439
+ this.up = maxTLENPercentile === undefined ? 99.5 : maxTLENPercentile;
48440
+ this.isizes = [];
48441
+ this.compute(alignments);
48442
+ }
48443
+
48444
+ compute(alignments) {
48445
+ for (let alignment of alignments) {
48446
+ if (alignment.isProperPair()) {
48447
+ var tlen = Math.abs(alignment.fragmentLength);
48448
+ this.sumF += tlen;
48449
+ this.sumF2 += tlen * tlen;
48450
+ this.isizes.push(tlen);
48451
+ var po = alignment.pairOrientation;
48452
+
48453
+ if (typeof po === "string" && po.length === 4) {
48454
+ var tmp = '' + po.charAt(0) + po.charAt(2);
48455
+
48456
+ switch (tmp) {
48457
+ case 'FF':
48458
+ case 'RR':
48459
+ this.ffCount++;
48460
+ break;
48461
+
48462
+ case "FR":
48463
+ this.frCount++;
48464
+ break;
48465
+
48466
+ case "RF":
48467
+ this.rfCount++;
48468
+ }
48469
+ }
48470
+
48471
+ this.totalCount++;
48472
+ }
48473
+ }
48474
+
48475
+ 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";
48476
+ this.minTLEN = this.lp === 0 ? 0 : percentile(this.isizes, this.lp);
48477
+ this.maxTLEN = percentile(this.isizes, this.up); // var fMean = this.sumF / this.totalCount
48478
+ // var stdDev = Math.sqrt((this.totalCount * this.sumF2 - this.sumF * this.sumF) / (this.totalCount * this.totalCount))
48479
+ // this.minTLEN = fMean - 3 * stdDev
48480
+ // this.maxTLEN = fMean + 3 * stdDev
48481
+ }
48482
+
48483
+ }
48484
+
48485
+ function percentile(array, p) {
48486
+ if (array.length === 0) return undefined;
48487
+ var k = Math.floor(array.length * (p / 100));
48488
+ array.sort(function (a, b) {
48489
+ return a - b;
48490
+ });
48491
+ return array[k];
48492
+ }
48493
+
48515
48494
  /*
48516
48495
  * The MIT License (MIT)
48517
48496
  *
@@ -48571,10 +48550,7 @@
48571
48550
  this.showInsertions = false !== config.showInsertions;
48572
48551
  this.showMismatches = false !== config.showMismatches;
48573
48552
  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
48553
+ this.coverageColor = config.coverageColor; // The sort object can be an array in the case of multi-locus view, however if multiple sort positions
48578
48554
  // are present for a given reference frame the last one will take precedence
48579
48555
 
48580
48556
  if (config.sort) {
@@ -48602,12 +48578,22 @@
48602
48578
  return this._height;
48603
48579
  }
48604
48580
 
48581
+ get minTemplateLength() {
48582
+ const configMinTLEN = this.config.minTLEN !== undefined ? this.config.minTLEN : this.config.minFragmentLength;
48583
+ return configMinTLEN !== undefined ? configMinTLEN : this._pairedEndStats ? this._pairedEndStats.minTLEN : 0;
48584
+ }
48585
+
48586
+ get maxTemplateLength() {
48587
+ const configMaxTLEN = this.config.maxTLEN !== undefined ? this.config.maxTLEN : this.config.maxFragmentLength;
48588
+ return configMaxTLEN !== undefined ? configMaxTLEN : this._pairedEndStats ? this._pairedEndStats.maxTLEN : 1000;
48589
+ }
48590
+
48605
48591
  sort(options) {
48606
48592
  options = this.assignSort(options);
48607
48593
 
48608
48594
  for (let vp of this.trackView.viewports) {
48609
48595
  if (vp.containsPosition(options.chr, options.position)) {
48610
- const alignmentContainer = vp.getCachedFeatures();
48596
+ const alignmentContainer = vp.cachedFeatures;
48611
48597
 
48612
48598
  if (alignmentContainer) {
48613
48599
  sortAlignmentRows(options, alignmentContainer);
@@ -48642,16 +48628,16 @@
48642
48628
  async getFeatures(chr, bpStart, bpEnd, bpPerPixel, viewport) {
48643
48629
  const alignmentContainer = await this.featureSource.getAlignments(chr, bpStart, bpEnd);
48644
48630
 
48645
- if (alignmentContainer.alignments && alignmentContainer.alignments.length > 99) {
48646
- if (undefined === this.minFragmentLength) {
48647
- this.minFragmentLength = alignmentContainer.pairedEndStats.lowerFragmentLength;
48648
- }
48631
+ if (alignmentContainer.paired && !this._pairedEndStats && !this.config.maxFragmentLength) {
48632
+ const pairedEndStats = new PairedEndStats(alignmentContainer.alignments, this.config);
48649
48633
 
48650
- if (undefined === this.maxFragmentLength) {
48651
- this.maxFragmentLength = alignmentContainer.pairedEndStats.upperFragmentLength;
48634
+ if (pairedEndStats.totalCount > 99) {
48635
+ this._pairedEndStats = pairedEndStats;
48652
48636
  }
48653
48637
  }
48654
48638
 
48639
+ alignmentContainer.alignments = undefined; // Don't need to hold onto these anymore
48640
+
48655
48641
  const sort = this.sortObject;
48656
48642
 
48657
48643
  if (sort) {
@@ -48756,7 +48742,7 @@
48756
48742
  label: 'pair orientation'
48757
48743
  });
48758
48744
  colorByMenuItems.push({
48759
- key: 'fragmentLength',
48745
+ key: 'tlen',
48760
48746
  label: 'insert size (TLEN)'
48761
48747
  });
48762
48748
  colorByMenuItems.push({
@@ -48867,36 +48853,19 @@
48867
48853
  this.trackView.repaintViews();
48868
48854
  }
48869
48855
  });
48870
- } // Experimental JBrowse feature
48856
+ } // Add chords to JBrowse circular view, if present
48871
48857
 
48872
48858
 
48873
- if (this.browser.circularView && true === this.browser.circularViewVisible && (this.alignmentTrack.hasPairs || this.alignmentTrack.hasSupplemental)) {
48859
+ if (this.browser.circularView && (this.alignmentTrack.hasPairs || this.alignmentTrack.hasSupplemental)) {
48874
48860
  menuItems.push('<hr/>');
48875
48861
 
48876
48862
  if (this.alignmentTrack.hasPairs) {
48877
48863
  menuItems.push({
48878
48864
  label: 'Add discordant pairs to circular view',
48879
48865
  click: () => {
48880
- const maxFragmentLength = this.maxFragmentLength;
48881
- const inView = [];
48882
-
48883
48866
  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
- }
48867
+ this.addPairedChordsForViewport(viewport);
48891
48868
  }
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
48869
  }
48901
48870
  });
48902
48871
  }
@@ -48905,25 +48874,9 @@
48905
48874
  menuItems.push({
48906
48875
  label: 'Add split reads to circular view',
48907
48876
  click: () => {
48908
- const inView = [];
48909
-
48910
48877
  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
- }
48878
+ this.addSplitChordsForViewport(viewport);
48919
48879
  }
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
48880
  }
48928
48881
  });
48929
48882
  }
@@ -49042,7 +48995,7 @@
49042
48995
  }
49043
48996
 
49044
48997
  getCachedAlignmentContainers() {
49045
- return this.trackView.viewports.map(vp => vp.getCachedFeatures());
48998
+ return this.trackView.viewports.map(vp => vp.cachedFeatures);
49046
48999
  }
49047
49000
 
49048
49001
  get dataRange() {
@@ -49068,6 +49021,56 @@
49068
49021
  set autoscale(autoscale) {
49069
49022
  this.coverageTrack.autoscale = autoscale;
49070
49023
  }
49024
+ /**
49025
+ * Add chords to the circular view for the given viewport, represented by its reference frame
49026
+ * @param refFrame
49027
+ */
49028
+
49029
+
49030
+ addPairedChordsForViewport(viewport) {
49031
+ const maxTemplateLength = this.maxTemplateLength;
49032
+ const inView = [];
49033
+ const refFrame = viewport.referenceFrame;
49034
+
49035
+ for (let a of viewport.cachedFeatures.allAlignments()) {
49036
+ if (a.end >= refFrame.start && a.start <= refFrame.end && a.mate && a.mate.chr && (a.mate.chr !== a.chr || Math.max(a.fragmentLength) > maxTemplateLength)) {
49037
+ inView.push(a);
49038
+ }
49039
+ }
49040
+
49041
+ const chords = makePairedAlignmentChords(inView);
49042
+ sendChords(chords, this, refFrame, 0.02); // const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? this.color : getChrColor(refFrame.chr), 0.02)
49043
+ // const trackColor = IGVColor.addAlpha(this.color || 'rgb(0,0,255)', 0.02)
49044
+ //
49045
+ // // name the chord set to include track name and locus
49046
+ // const encodedName = this.name.replaceAll(' ', '%20')
49047
+ // const chordSetName = "all" === refFrame.chr ? encodedName :
49048
+ // `${encodedName} (${refFrame.chr}:${refFrame.start}-${refFrame.end}`
49049
+ // this.browser.circularView.addChords(chords, {name: chordSetName, color: chordSetColor, trackColor: trackColor})
49050
+ }
49051
+
49052
+ addSplitChordsForViewport(viewport) {
49053
+ const inView = [];
49054
+ const refFrame = viewport.referenceFrame;
49055
+
49056
+ for (let a of viewport.cachedFeatures.allAlignments()) {
49057
+ const sa = a.hasTag('SA');
49058
+
49059
+ if (a.end >= refFrame.start && a.start <= refFrame.end && sa) {
49060
+ inView.push(a);
49061
+ }
49062
+ }
49063
+
49064
+ const chords = makeSupplementalAlignmentChords(inView);
49065
+ sendChords(chords, this, refFrame, 0.02); // const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? this.color : getChrColor(refFrame.chr), 0.02)
49066
+ // const trackColor = IGVColor.addAlpha(this.color || 'rgb(0,0,255)', 0.02)
49067
+ //
49068
+ // // name the chord set to include track name and locus
49069
+ // const encodedName = this.name.replaceAll(' ', '%20')
49070
+ // const chordSetName = "all" === refFrame.chr ? encodedName :
49071
+ // `${encodedName} (${refFrame.chr}:${refFrame.start}-${refFrame.end}`
49072
+ // this.browser.circularView.addChords(chords, {name: chordSetName, color: chordSetColor, trackColor: trackColor})
49073
+ }
49071
49074
 
49072
49075
  }
49073
49076
 
@@ -49178,7 +49181,7 @@
49178
49181
  }
49179
49182
 
49180
49183
  getClickedObject(clickState) {
49181
- let features = clickState.viewport.getCachedFeatures();
49184
+ let features = clickState.viewport.cachedFeatures;
49182
49185
  if (!features || features.length === 0) return;
49183
49186
  const genomicLocation = Math.floor(clickState.genomicLocation);
49184
49187
  const coverageMap = features.coverageMap;
@@ -49264,14 +49267,14 @@
49264
49267
  this.deletionColor = config.deletionColor || "black";
49265
49268
  this.skippedColor = config.skippedColor || "rgb(150, 170, 170)";
49266
49269
  this.pairConnectorColor = config.pairConnectorColor;
49267
- this.smallFragmentLengthColor = config.smallFragmentLengthColor || "rgb(0, 0, 150)";
49268
- this.largeFragmentLengthColor = config.largeFragmentLengthColor || "rgb(200, 0, 0)";
49270
+ this.smallTLENColor = config.smallTLENColor || config.smallFragmentLengthColor || "rgb(0, 0, 150)";
49271
+ this.largeTLENColor = config.largeTLENColor || config.largeFragmentLengthColor || "rgb(200, 0, 0)";
49269
49272
  this.pairOrientation = config.pairOrienation || 'fr';
49270
49273
  this.pairColors = {};
49271
49274
  this.pairColors["RL"] = config.rlColor || "rgb(0, 150, 0)";
49272
49275
  this.pairColors["RR"] = config.rrColor || "rgb(20, 50, 200)";
49273
49276
  this.pairColors["LL"] = config.llColor || "rgb(0, 150, 150)";
49274
- this.colorBy = config.colorBy || "pairOrientation";
49277
+ this.colorBy = config.colorBy || "unexpectedPair";
49275
49278
  this.colorByTag = config.colorByTag ? config.colorByTag.toUpperCase() : undefined;
49276
49279
  this.bamColorTag = config.bamColorTag === undefined ? "YC" : config.bamColorTag;
49277
49280
  this.hideSmallIndels = config.hideSmallIndels;
@@ -49370,7 +49373,7 @@
49370
49373
  for (let alignment of alignmentRow.alignments) {
49371
49374
  this.hasPairs = this.hasPairs || alignment.isPaired();
49372
49375
 
49373
- if (this.browser.circularView && true === this.browser.circularViewVisible) {
49376
+ if (this.browser.circularView) {
49374
49377
  // This is an expensive check, only do it if needed
49375
49378
  this.hasSupplemental = this.hasSupplemental || alignment.hasTag('SA');
49376
49379
  }
@@ -49626,7 +49629,7 @@
49626
49629
  direction: direction
49627
49630
  };
49628
49631
  this.parent.sortObject = newSortObject;
49629
- sortAlignmentRows(newSortObject, viewport.getCachedFeatures());
49632
+ sortAlignmentRows(newSortObject, viewport.cachedFeatures);
49630
49633
  viewport.repaint();
49631
49634
  };
49632
49635
 
@@ -49678,7 +49681,7 @@
49678
49681
  };
49679
49682
  this.sortByTag = tag;
49680
49683
  this.parent.sortObject = newSortObject;
49681
- sortAlignmentRows(newSortObject, viewport.getCachedFeatures());
49684
+ sortAlignmentRows(newSortObject, viewport.cachedFeatures);
49682
49685
  viewport.repaint();
49683
49686
  }
49684
49687
  }
@@ -49702,8 +49705,12 @@
49702
49705
  const referenceFrame = clickState.viewport.referenceFrame;
49703
49706
 
49704
49707
  if (this.browser.genome.getChromosome(clickedAlignment.mate.chr)) {
49705
- this.highlightedAlignmentReadNamed = clickedAlignment.readName;
49706
- this.browser.presentMultiLocusPanel(clickedAlignment, referenceFrame);
49708
+ this.highlightedAlignmentReadNamed = clickedAlignment.readName; //this.browser.presentMultiLocusPanel(clickedAlignment, referenceFrame)
49709
+
49710
+ const bpWidth = referenceFrame.end - referenceFrame.start;
49711
+ const frameStart = clickedAlignment.mate.position - bpWidth / 2;
49712
+ const frameEnd = clickedAlignment.mate.position + bpWidth / 2;
49713
+ this.browser.addMultiLocusPanel(clickedAlignment.mate.chr, frameStart, frameEnd, referenceFrame);
49707
49714
  } else {
49708
49715
  Alert.presentAlert(`Reference does not contain chromosome: ${clickedAlignment.mate.chr}`);
49709
49716
  }
@@ -49716,9 +49723,7 @@
49716
49723
  list.push({
49717
49724
  label: 'View read sequence',
49718
49725
  click: () => {
49719
- const alignment = clickedAlignment;
49720
- if (!alignment) return;
49721
- const seqstring = alignment.seq; //.map(b => String.fromCharCode(b)).join("");
49726
+ const seqstring = clickedAlignment.seq; //.map(b => String.fromCharCode(b)).join("");
49722
49727
 
49723
49728
  if (!seqstring || "*" === seqstring) {
49724
49729
  Alert.presentAlert("Read sequence: *");
@@ -49731,12 +49736,16 @@
49731
49736
  if (isSecureContext()) {
49732
49737
  list.push({
49733
49738
  label: 'Copy read sequence',
49734
- click: () => {
49735
- const alignment = clickedAlignment;
49736
- if (!alignment) return;
49737
- const seqstring = alignment.seq; //.map(b => String.fromCharCode(b)).join("");
49739
+ click: async () => {
49740
+ const seq = clickedAlignment.seq; //.map(b => String.fromCharCode(b)).join("");
49738
49741
 
49739
- navigator.clipboard.writeText(seqstring);
49742
+ try {
49743
+ //console.log(`seq: ${seq}`)
49744
+ await navigator.clipboard.writeText(seq);
49745
+ } catch (e) {
49746
+ console.error(e);
49747
+ Alert.presentAlert(`error copying sequence to clipboard ${e}`);
49748
+ }
49740
49749
  }
49741
49750
  });
49742
49751
  }
@@ -49746,25 +49755,12 @@
49746
49755
  } // Experimental JBrowse feature
49747
49756
 
49748
49757
 
49749
- if (this.browser.circularView && true === this.browser.circularViewVisible && (this.hasPairs || this.hasSupplemental)) {
49758
+ if (this.browser.circularView && (this.hasPairs || this.hasSupplemental)) {
49750
49759
  if (this.hasPairs) {
49751
49760
  list.push({
49752
49761
  label: 'Add discordant pairs to circular view',
49753
49762
  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
- });
49763
+ this.parent.addPairedChordsForViewport(viewport);
49768
49764
  }
49769
49765
  });
49770
49766
  }
@@ -49773,23 +49769,7 @@
49773
49769
  list.push({
49774
49770
  label: 'Add split reads to circular view',
49775
49771
  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
- });
49772
+ this.parent.addSplitChordsForViewport(viewport);
49793
49773
  }
49794
49774
  });
49795
49775
  }
@@ -49805,7 +49785,7 @@
49805
49785
  const y = clickState.y;
49806
49786
  const genomicLocation = clickState.genomicLocation;
49807
49787
  const showSoftClips = this.parent.showSoftClips;
49808
- let features = viewport.getCachedFeatures();
49788
+ let features = viewport.cachedFeatures;
49809
49789
  if (!features || features.length === 0) return;
49810
49790
  let packedAlignmentRows = features.packedAlignmentRows;
49811
49791
  let downsampledIntervals = features.downsampledIntervals;
@@ -49891,13 +49871,16 @@
49891
49871
  break;
49892
49872
  }
49893
49873
 
49874
+ case "tlen":
49894
49875
  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;
49876
+ if (alignment.mate && alignment.isMateMapped()) {
49877
+ if (alignment.mate.chr !== alignment.chr) {
49878
+ color = getChrColor(alignment.mate.chr);
49879
+ } else if (this.parent.minTemplateLength && Math.abs(alignment.fragmentLength) < this.parent.minTemplateLength) {
49880
+ color = this.smallTLENColor;
49881
+ } else if (this.parent.maxTemplateLength && Math.abs(alignment.fragmentLength) > this.parent.maxTemplateLength) {
49882
+ color = this.largeTLENColor;
49883
+ }
49901
49884
  }
49902
49885
 
49903
49886
  break;
@@ -49939,10 +49922,7 @@
49939
49922
  alignmentContainer.packedAlignmentRows.sort(function (rowA, rowB) {
49940
49923
  const i = rowA.score > rowB.score ? 1 : rowA.score < rowB.score ? -1 : 0;
49941
49924
  return true === direction ? i : -i;
49942
- }); // For debugging
49943
- // for(let r of alignmentContainer.packedAlignmentRows) {
49944
- // console.log(r.score);
49945
- // }
49925
+ });
49946
49926
  }
49947
49927
 
49948
49928
  function shadedBaseColor(qual, baseColor) {
@@ -50097,7 +50077,7 @@
50097
50077
  });
50098
50078
  this.$viewport.append(this.$rulerLabel);
50099
50079
  this.$rulerLabel.click(async () => {
50100
- await this.browser.selectMultiLocusPanel(this.referenceFrame); // const removals = this.browser.referenceFrameList.filter(r => this.referenceFrame !== r)
50080
+ await this.browser.gotoMultilocusPanel(this.referenceFrame); // const removals = this.browser.referenceFrameList.filter(r => this.referenceFrame !== r)
50101
50081
  // for (let referenceFrame of removals) {
50102
50082
  // await this.browser.removeMultiLocusPanel(referenceFrame)
50103
50083
  // }
@@ -50203,7 +50183,10 @@
50203
50183
  currentViewport = this;
50204
50184
  this.$tooltip.show();
50205
50185
  } else if (currentViewport.guid !== this.guid) {
50206
- currentViewport.$tooltip.hide();
50186
+ if (currentViewport.$tooltip) {
50187
+ currentViewport.$tooltip.hide();
50188
+ }
50189
+
50207
50190
  this.$tooltip.show();
50208
50191
  currentViewport = this;
50209
50192
  } else {
@@ -50237,7 +50220,9 @@
50237
50220
  }); // hide tooltip when movement stops
50238
50221
 
50239
50222
  clearTimeout(timer);
50240
- timer = setTimeout(() => this.$tooltip.hide(), toolTipTimeout);
50223
+ timer = setTimeout(() => {
50224
+ if (this.$tooltip) this.$tooltip.hide();
50225
+ }, toolTipTimeout);
50241
50226
  }
50242
50227
  }
50243
50228
 
@@ -50252,65 +50237,6 @@
50252
50237
 
50253
50238
  }
50254
50239
 
50255
- const viewportColumnManager = {
50256
- createColumns: (columnContainer, count) => {
50257
- for (let i = 0; i < count; i++) {
50258
- if (0 === i) {
50259
- createColumn(columnContainer, 'igv-column');
50260
- } else {
50261
- columnContainer.appendChild(div$1({
50262
- class: 'igv-column-shim'
50263
- }));
50264
- createColumn(columnContainer, 'igv-column');
50265
- }
50266
- }
50267
- },
50268
- removeColumnAtIndex: (i, column) => {
50269
- const shim = 0 === i ? column.nextElementSibling : column.previousElementSibling;
50270
- column.remove();
50271
- shim.remove();
50272
- },
50273
- insertAfter: referenceElement => {
50274
- const shim = div$1({
50275
- class: 'igv-column-shim'
50276
- });
50277
- insertElementAfter(shim, referenceElement);
50278
- const column = div$1({
50279
- class: 'igv-column'
50280
- });
50281
- insertElementAfter(column, shim);
50282
- return column;
50283
- },
50284
- insertBefore: (referenceElement, count) => {
50285
- for (let i = 0; i < count; i++) {
50286
- const column = div$1({
50287
- class: 'igv-column'
50288
- });
50289
- insertElementBefore(column, referenceElement);
50290
-
50291
- if (count > 1 && i > 0) {
50292
- const columnShim = div$1({
50293
- class: 'igv-column-shim'
50294
- });
50295
- insertElementBefore(columnShim, column);
50296
- }
50297
- }
50298
- },
50299
- indexOfColumn: (columnContainer, column) => {
50300
- const allColumns = columnContainer.querySelectorAll('.igv-column');
50301
-
50302
- for (let i = 0; i < allColumns.length; i++) {
50303
- const c = allColumns[i];
50304
-
50305
- if (c === column) {
50306
- return i;
50307
- }
50308
- }
50309
-
50310
- return undefined;
50311
- }
50312
- };
50313
-
50314
50240
  /*
50315
50241
  * The MIT License (MIT)
50316
50242
  *
@@ -50342,60 +50268,30 @@
50342
50268
  }
50343
50269
 
50344
50270
  initializationHelper() {
50345
- this.$ideogramCanvas = $$1('<canvas>', {
50346
- class: 'igv-ideogram-canvas'
50347
- });
50348
- this.$ideogramCanvas.insertBefore(this.$canvas);
50349
- const canvas = this.$ideogramCanvas.get(0);
50350
- this.ideogram_ctx = canvas.getContext('2d');
50351
- this.$canvas.remove();
50352
- this.canvas = undefined;
50353
- this.ctx = undefined;
50271
+ this.canvas = document.createElement('canvas');
50272
+ this.canvas.className = 'igv-ideogram-canvas';
50273
+ this.$content.append($$1(this.canvas));
50274
+ this.ideogram_ctx = this.canvas.getContext('2d');
50354
50275
  this.addMouseHandlers();
50355
50276
  }
50356
50277
 
50357
50278
  addMouseHandlers() {
50358
- this.addBrowserObserver();
50359
50279
  this.addViewportClickHandler(this.$viewport.get(0));
50360
50280
  }
50361
50281
 
50362
- removeMouseHandlers() {
50363
- this.removeBrowserObserver();
50364
- this.removeViewportClickHandler(this.$viewport.get(0));
50365
- }
50366
-
50367
- addBrowserObserver() {
50368
- function observerHandler(referenceFrameList) {
50369
- const column = this.$viewport.get(0).parentElement;
50370
-
50371
- if (null !== column) {
50372
- const index = viewportColumnManager.indexOfColumn(this.browser.columnContainer, column); // console.log(`ideogram-viewport - locus-change-handler index(${ index }) ${ referenceFrameList[ index ].getLocusString() } ${ Date.now() } `)
50373
-
50374
- this.update(this.ideogram_ctx, this.$viewport.width(), this.$viewport.height(), referenceFrameList[index]);
50375
- }
50376
- }
50377
-
50378
- this.boundObserverHandler = observerHandler.bind(this);
50379
- this.browser.on('locuschange', this.boundObserverHandler);
50380
- }
50381
-
50382
- removeBrowserObserver() {
50383
- this.browser.off('locuschange', this.boundObserverHandler);
50384
- }
50385
-
50386
50282
  addViewportClickHandler(viewport) {
50283
+ this.boundClickHandler = clickHandler.bind(this);
50284
+ viewport.addEventListener('click', this.boundClickHandler);
50285
+
50387
50286
  function clickHandler(event) {
50388
- const column = viewport.parentElement;
50389
- const index = viewportColumnManager.indexOfColumn(this.browser.columnContainer, column);
50390
- const referenceFrame = this.browser.referenceFrameList[index];
50391
50287
  const {
50392
50288
  xNormalized,
50393
50289
  width
50394
50290
  } = translateMouseCoordinates$1(event, this.ideogram_ctx.canvas);
50395
50291
  const {
50396
50292
  bpLength
50397
- } = this.browser.genome.getChromosome(referenceFrame.chr);
50398
- const locusLength = referenceFrame.bpPerPixel * width;
50293
+ } = this.browser.genome.getChromosome(this.referenceFrame.chr);
50294
+ const locusLength = this.referenceFrame.bpPerPixel * width;
50399
50295
  const chrCoveragePercentage = locusLength / bpLength;
50400
50296
  let xPercentage = xNormalized;
50401
50297
 
@@ -50409,18 +50305,11 @@
50409
50305
 
50410
50306
  const ss = Math.round((xPercentage - chrCoveragePercentage / 2.0) * bpLength);
50411
50307
  const ee = Math.round((xPercentage + chrCoveragePercentage / 2.0) * bpLength);
50412
- referenceFrame.start = ss;
50413
- referenceFrame.end = ee;
50414
- referenceFrame.bpPerPixel = (ee - ss) / width;
50415
- this.browser.updateViews(referenceFrame, this.browser.trackViews, true);
50308
+ this.referenceFrame.start = ss;
50309
+ this.referenceFrame.end = ee;
50310
+ this.referenceFrame.bpPerPixel = (ee - ss) / width;
50311
+ this.browser.updateViews(this.referenceFrame, this.browser.trackViews, true);
50416
50312
  }
50417
-
50418
- this.boundClickHandler = clickHandler.bind(this);
50419
- viewport.addEventListener('click', this.boundClickHandler);
50420
- }
50421
-
50422
- removeViewportClickHandler(viewport) {
50423
- viewport.removeEventListener('click', this.boundClickHandler);
50424
50313
  }
50425
50314
 
50426
50315
  setWidth(width) {
@@ -50438,14 +50327,22 @@
50438
50327
  context.restore();
50439
50328
  }
50440
50329
 
50441
- update(context, pixelWidth, pixelHeight, referenceFrame) {
50442
- this.$canvas.hide();
50443
- IGVGraphics.configureHighDPICanvas(context, pixelWidth, pixelHeight);
50330
+ repaint() {
50331
+ this.draw({
50332
+ referenceFrame: this.referenceFrame
50333
+ });
50334
+ }
50335
+
50336
+ draw(_ref) {
50337
+ let {
50338
+ referenceFrame
50339
+ } = _ref;
50340
+ IGVGraphics.configureHighDPICanvas(this.ideogram_ctx, this.$viewport.width(), this.$viewport.height());
50444
50341
  this.trackView.track.draw({
50445
- context,
50342
+ context: this.ideogram_ctx,
50446
50343
  referenceFrame,
50447
- pixelWidth,
50448
- pixelHeight
50344
+ pixelWidth: this.$viewport.width(),
50345
+ pixelHeight: this.$viewport.height()
50449
50346
  });
50450
50347
  }
50451
50348
 
@@ -50483,7 +50380,7 @@
50483
50380
  function createViewport(trackView, column, referenceFrame, width) {
50484
50381
  if ('ruler' === trackView.track.type) {
50485
50382
  return new RulerViewport(trackView, column, referenceFrame, width);
50486
- } else if ('ideogram' === trackView.track.type) {
50383
+ } else if ('ideogram' === trackView.track.id) {
50487
50384
  return new IdeogramViewport(trackView, column, referenceFrame, width);
50488
50385
  } else {
50489
50386
  return new TrackViewport(trackView, column, referenceFrame, width);
@@ -51010,7 +50907,6 @@
51010
50907
 
51011
50908
  class TrackView {
51012
50909
  constructor(browser, columnContainer, track) {
51013
- this.namespace = `trackview-${guid$2()}`;
51014
50910
  this.browser = browser;
51015
50911
  this.track = track;
51016
50912
  track.trackView = this;
@@ -51036,7 +50932,7 @@
51036
50932
 
51037
50933
  addDOMToColumnContainer(browser, columnContainer, referenceFrameList) {
51038
50934
  // Axis
51039
- this.axis = this.createAxis(browser, this.track); // Track Viewports
50935
+ this.axis = this.createAxis(browser, this.track); // Create a viewport for each reference frame
51040
50936
 
51041
50937
  this.viewports = [];
51042
50938
  const viewportWidth = browser.calculateViewportWidth(referenceFrameList.length);
@@ -51103,7 +50999,6 @@
51103
50999
  this.axis.remove(); // Track Viewports
51104
51000
 
51105
51001
  for (let viewport of this.viewports) {
51106
- viewport.removeMouseHandlers();
51107
51002
  viewport.$viewport.remove();
51108
51003
  } // SampleName Viewport
51109
51004
 
@@ -51221,7 +51116,7 @@
51221
51116
  $viewport.height(newHeight);
51222
51117
  }
51223
51118
 
51224
- this.sampleNameViewport.viewport.style.height = `${newHeight}px`; // If the track does not manage its own content height set it here
51119
+ this.sampleNameViewport.viewport.style.height = `${newHeight}px`; // If the track does not manage its own content height set it equal to the viewport height here
51225
51120
 
51226
51121
  if (typeof this.track.computePixelHeight !== "function") {
51227
51122
  for (let vp of this.viewports) {
@@ -51273,14 +51168,6 @@
51273
51168
  if (viewport.isLoading()) return true;
51274
51169
  }
51275
51170
  }
51276
-
51277
- resize(viewportWidth) {
51278
- for (let viewport of this.viewports) {
51279
- viewport.setWidth(viewportWidth);
51280
- }
51281
-
51282
- this.updateViews(true);
51283
- }
51284
51171
  /**
51285
51172
  * Repaint all viewports without loading any new data. Use this for events that change visual aspect of data,
51286
51173
  * e.g. color, sort order, etc, but do not change the genomic state.
@@ -51289,7 +51176,9 @@
51289
51176
 
51290
51177
  repaintViews() {
51291
51178
  for (let viewport of this.viewports) {
51292
- viewport.repaint();
51179
+ if (viewport.isVisible()) {
51180
+ viewport.repaint();
51181
+ }
51293
51182
  }
51294
51183
 
51295
51184
  if (typeof this.track.paintAxis === 'function') {
@@ -51311,41 +51200,60 @@
51311
51200
  setTrackLabelName(name) {
51312
51201
  this.viewports.forEach(viewport => viewport.setTrackLabel(name));
51313
51202
  }
51203
+ /**
51204
+ * Called in response to a window resize event, change in # of multilocus panels, or other event that changes
51205
+ * the width of the track view.
51206
+ *
51207
+ * @param viewportWidth The width of each viewport in this track view.
51208
+ */
51209
+
51210
+
51211
+ resize(viewportWidth) {
51212
+ for (let viewport of this.viewports) {
51213
+ viewport.setWidth(viewportWidth);
51214
+ }
51215
+ }
51314
51216
  /**
51315
51217
  * Update viewports to reflect current genomic state, possibly loading additional data.
51218
+ *
51219
+ * @param force - if true, force a repaint even if no new data is loaded
51220
+ * @returns {Promise<void>}
51316
51221
  */
51317
51222
 
51318
51223
 
51319
- async updateViews(force) {
51224
+ async updateViews() {
51320
51225
  if (!(this.browser && this.browser.referenceFrameList)) return;
51321
51226
  const visibleViewports = this.viewports.filter(viewport => viewport.isVisible()); // Shift viewports left/right to current genomic state (pans canvas)
51322
51227
 
51323
- visibleViewports.forEach(viewport => viewport.shift());
51324
- const isDragging = this.browser.dragObject;
51228
+ visibleViewports.forEach(viewport => viewport.shift()); // If dragging (panning) return
51325
51229
 
51326
- if (isDragging) {
51230
+ if (this.browser.dragObject) {
51327
51231
  return;
51328
- } // rpv: viewports whose image (canvas) does not fully cover current genomic range
51232
+ } // Get viewports to repaint
51233
+
51329
51234
 
51235
+ let viewportsToRepaint = this.track.autoscale || this.track.autoscaleGroup || this.track.type === 'ruler' ? visibleViewports : visibleViewports.filter(vp => vp.needsRepaint()); // Filter zoomed out views. This has the side effect or turning off or no the zoomed out notice
51330
51236
 
51331
- const reloadableViewports = this.viewportsToReload(force); // Trigger viewport to load features needed to cover current genomic range
51237
+ viewportsToRepaint = viewportsToRepaint.filter(viewport => viewport.checkZoomIn()); // Get viewports that require a data load
51238
+
51239
+ const viewportsToReload = viewportsToRepaint.filter(viewport => viewport.needsReload()); // Trigger viewport to load features needed to cover current genomic range
51332
51240
  // NOTE: these must be loaded synchronously, do not user Promise.all, not all file readers are thread safe
51333
51241
 
51334
- for (let viewport of reloadableViewports) {
51242
+ for (let viewport of viewportsToReload) {
51335
51243
  await viewport.loadFeatures();
51336
51244
  }
51337
51245
 
51338
51246
  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)
51247
+ // Special case for variant tracks in multilocus view. The # of rows to allocate to the variant (site)
51340
51248
  // section depends on data from all the views. We only need to adjust this however if any data was loaded
51341
51249
  // (i.e. reloadableViewports.length > 0)
51342
51250
 
51343
- if (this.track && typeof this.track.variantRowCount === 'function' && reloadableViewports.length > 0) {
51251
+ if (this.track && typeof this.track.variantRowCount === 'function' && viewportsToReload.length > 0) {
51344
51252
  let maxRow = 0;
51345
51253
 
51346
51254
  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));
51255
+ if (viewport.featureCache && viewport.featureCache.features) {
51256
+ maxRow = Math.max(maxRow, viewport.featureCache.features.reduce((a, f) => Math.max(a, f.row || 0), 0));
51349
51257
  }
51350
51258
  }
51351
51259
 
@@ -51368,14 +51276,14 @@
51368
51276
  const start = referenceFrame.start;
51369
51277
  const end = start + referenceFrame.toBP($$1(visibleViewport.contentDiv).width());
51370
51278
 
51371
- if (visibleViewport.tile && visibleViewport.tile.features) {
51372
- if (typeof visibleViewport.tile.features.getMax === 'function') {
51373
- const max = visibleViewport.tile.features.getMax(start, end);
51279
+ if (visibleViewport.featureCache && visibleViewport.featureCache.features) {
51280
+ if (typeof visibleViewport.featureCache.features.getMax === 'function') {
51281
+ const max = visibleViewport.featureCache.features.getMax(start, end);
51374
51282
  allFeatures.push({
51375
51283
  value: max
51376
51284
  });
51377
51285
  } else {
51378
- allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(visibleViewport.tile.features, start, end));
51286
+ allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(visibleViewport.featureCache.features, start, end));
51379
51287
  }
51380
51288
  }
51381
51289
  }
@@ -51385,17 +51293,10 @@
51385
51293
  } else {
51386
51294
  this.track.dataRange = doAutoscale(allFeatures);
51387
51295
  }
51388
- } // Must repaint all viewports if autoscaling
51389
-
51296
+ }
51390
51297
 
51391
- if (!isDragging && (this.track.autoscale || this.track.autoscaleGroup)) {
51392
- for (let visibleViewport of visibleViewports) {
51393
- visibleViewport.repaint();
51394
- }
51395
- } else {
51396
- for (let vp of reloadableViewports) {
51397
- vp.repaint();
51398
- }
51298
+ for (let vp of viewportsToRepaint) {
51299
+ vp.repaint();
51399
51300
  }
51400
51301
 
51401
51302
  this.adjustTrackHeight(); // Repaint sample names last
@@ -51418,36 +51319,34 @@
51418
51319
  }
51419
51320
  }
51420
51321
  /**
51421
- * Return a promise to get all in-view features. Used for group autoscaling.
51322
+ * Return a promise to get all in-view features across all viewports. Used for group autoscaling.
51422
51323
  */
51423
51324
 
51424
51325
 
51425
- async getInViewFeatures(force) {
51326
+ async getInViewFeatures() {
51426
51327
  if (!(this.browser && this.browser.referenceFrameList)) {
51427
51328
  return [];
51428
- } // List of viewports that need reloading
51429
-
51329
+ }
51430
51330
 
51431
- const rpV = this.viewportsToReload(force);
51432
- const promises = rpV.map(function (vp) {
51433
- return vp.loadFeatures();
51434
- });
51435
- await Promise.all(promises);
51436
51331
  let allFeatures = [];
51437
51332
 
51438
51333
  for (let vp of this.viewports) {
51439
- if (vp.tile && vp.tile.features) {
51334
+ if (vp.needsReload()) {
51335
+ await vp.loadFeatures();
51336
+ }
51337
+
51338
+ if (vp.featureCache && vp.featureCache.features) {
51440
51339
  const referenceFrame = vp.referenceFrame;
51441
51340
  const start = referenceFrame.start;
51442
51341
  const end = start + referenceFrame.toBP($$1(vp.contentDiv).width());
51443
51342
 
51444
- if (typeof vp.tile.features.getMax === 'function') {
51445
- const max = vp.tile.features.getMax(start, end);
51343
+ if (typeof vp.featureCache.features.getMax === 'function') {
51344
+ const max = vp.featureCache.features.getMax(start, end);
51446
51345
  allFeatures.push({
51447
51346
  value: max
51448
51347
  });
51449
51348
  } else {
51450
- allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(vp.tile.features, start, end));
51349
+ allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(vp.featureCache.features, start, end));
51451
51350
  }
51452
51351
  }
51453
51352
  }
@@ -51489,27 +51388,6 @@
51489
51388
  }
51490
51389
  }
51491
51390
 
51492
- viewportsToReload(force) {
51493
- // List of viewports that need reloading
51494
- const viewports = this.viewports.filter(viewport => {
51495
- if (!viewport.isVisible()) {
51496
- return false;
51497
- }
51498
-
51499
- if (!viewport.checkZoomIn()) {
51500
- return false;
51501
- } else {
51502
- const referenceFrame = viewport.referenceFrame;
51503
- const chr = viewport.referenceFrame.chr;
51504
- const start = referenceFrame.start;
51505
- const end = start + referenceFrame.toBP($$1(viewport.contentDiv).width());
51506
- const bpPerPixel = referenceFrame.bpPerPixel;
51507
- return force || !viewport.tile || viewport.tile.invalidate || !viewport.tile.containsRange(chr, start, end, bpPerPixel);
51508
- }
51509
- });
51510
- return viewports;
51511
- }
51512
-
51513
51391
  createTrackScrollbar(browser) {
51514
51392
  const outerScroll = div$1();
51515
51393
  browser.columnContainer.querySelector('.igv-scrollbar-column').appendChild(outerScroll);
@@ -51603,7 +51481,7 @@
51603
51481
  }
51604
51482
 
51605
51483
  addTrackDragMouseHandlers(browser) {
51606
- if ('ideogram' === this.track.type || 'ruler' === this.track.type) ; else {
51484
+ if ('ideogram' === this.track.id || 'ruler' === this.track.id) ; else {
51607
51485
  let currentDragHandle = undefined; // Mouse Down
51608
51486
 
51609
51487
  this.boundTrackDragMouseDownHandler = trackDragMouseDownHandler.bind(this);
@@ -51667,7 +51545,7 @@
51667
51545
  }
51668
51546
 
51669
51547
  removeTrackDragMouseHandlers() {
51670
- if ('ideogram' === this.track.type || 'ruler' === this.track.type) ; else {
51548
+ if ('ideogram' === this.track.id || 'ruler' === this.track.id) ; else {
51671
51549
  this.dragHandle.removeEventListener('mousedown', this.boundTrackDragMouseDownHandler);
51672
51550
  document.removeEventListener('mouseup', this.boundDocumentTrackDragMouseUpHandler);
51673
51551
  this.dragHandle.removeEventListener('mouseup', this.boundTrackDragMouseEnterHandler);
@@ -56314,7 +56192,7 @@
56314
56192
  this.genome = genome;
56315
56193
  this.sourceType = config.sourceType === undefined ? "file" : config.sourceType;
56316
56194
  this.maxWGCount = config.maxWGCount || DEFAULT_MAX_WG_COUNT;
56317
- const queryableFormats = new Set(["bigwig", "bw", "bigbed", "bb", "tdf"]);
56195
+ const queryableFormats = new Set(["bigwig", "bw", "bigbed", "bb", "biginteract", "tdf"]);
56318
56196
 
56319
56197
  if (config.features && Array.isArray(config.features)) {
56320
56198
  // Explicit array of features
@@ -56326,7 +56204,7 @@
56326
56204
  }
56327
56205
 
56328
56206
  this.queryable = false;
56329
- this.featureCache = new FeatureCache(features, genome);
56207
+ this.featureCache = new FeatureCache$1(features, genome);
56330
56208
  } else if (config.reader) {
56331
56209
  // Explicit reader implementation
56332
56210
  this.reader = config.reader;
@@ -56497,13 +56375,13 @@
56497
56375
  } // Note - replacing previous cache with new one. genomicInterval is optional (might be undefined => includes all features)
56498
56376
 
56499
56377
 
56500
- this.featureCache = new FeatureCache(features, this.genome, genomicInterval); // If track is marked "searchable"< cache features by name -- use this with caution, memory intensive
56378
+ 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
56379
 
56502
56380
  if (this.config.searchable || this.config.searchableFields) {
56503
56381
  this.addFeaturesToDB(features);
56504
56382
  }
56505
56383
  } else {
56506
- this.featureCache = new FeatureCache([], genomicInterval); // Empty cache
56384
+ this.featureCache = new FeatureCache$1([], genomicInterval); // Empty cache
56507
56385
  }
56508
56386
  }
56509
56387
 
@@ -56727,8 +56605,8 @@
56727
56605
 
56728
56606
  //table chromatinInteract
56729
56607
 
56730
- function getDecoder(definedFieldCount, fieldCount, autoSql) {
56731
- if (autoSql && 'chromatinInteract' === autoSql.table) {
56608
+ function getDecoder(definedFieldCount, fieldCount, autoSql, format) {
56609
+ if (autoSql && 'chromatinInteract' === autoSql.table || "biginteract" === format) {
56732
56610
  return decodeInteract;
56733
56611
  } else {
56734
56612
  const standardFieldCount = definedFieldCount - 3;
@@ -56863,6 +56741,7 @@
56863
56741
  class BWReader {
56864
56742
  constructor(config, genome) {
56865
56743
  this.path = config.url;
56744
+ this.format = config.format || "bigwig";
56866
56745
  this.genome = genome;
56867
56746
  this.rpTreeCache = {};
56868
56747
  this.config = config;
@@ -57441,7 +57320,7 @@
57441
57320
  function getBedDataDecoder() {
57442
57321
  const minSize = 3 * 4 + 1; // Minimum # of bytes required for a bed record
57443
57322
 
57444
- const decoder = getDecoder(this.header.definedFieldCount, this.header.fieldCount, this.autoSql);
57323
+ const decoder = getDecoder(this.header.definedFieldCount, this.header.fieldCount, this.autoSql, this.format);
57445
57324
  return function (data, chrIdx1, bpStart, chrIdx2, bpEnd, featureArray, chrDict) {
57446
57325
  const binaryParser = new BinaryParser(data);
57447
57326
 
@@ -58252,7 +58131,7 @@
58252
58131
  return features;
58253
58132
  }
58254
58133
 
58255
- supportsWholeGenome() {
58134
+ get supportsWholeGenome() {
58256
58135
  return true;
58257
58136
  }
58258
58137
 
@@ -58361,7 +58240,7 @@
58361
58240
  function FeatureSource(config, genome) {
58362
58241
  const format = config.format ? config.format.toLowerCase() : undefined;
58363
58242
 
58364
- if ('bigwig' === format || 'bigbed' === format || 'bb' === format) {
58243
+ if ('bigwig' === format || 'bigbed' === format || 'bb' === format || "biginteract" === format) {
58365
58244
  return new BWSource(config, genome);
58366
58245
  } else if ("tdf" === format) {
58367
58246
  return new TDFSource(config, genome);
@@ -58370,30 +58249,6 @@
58370
58249
  }
58371
58250
  }
58372
58251
 
58373
- const pairs = [['A', 'T'], ['G', 'C'], ['Y', 'R'], ['W', 'S'], ['K', 'M'], ['D', 'H'], ['B', 'V']];
58374
- const complements = new Map();
58375
-
58376
- for (let p of pairs) {
58377
- const p1 = p[0];
58378
- const p2 = p[1];
58379
- complements.set(p1, p2);
58380
- complements.set(p2, p1);
58381
- complements.set(p1.toLowerCase(), p2.toLowerCase());
58382
- complements.set(p2.toLowerCase(), p1.toLowerCase());
58383
- }
58384
-
58385
- function reverseComplementSequence(sequence) {
58386
- let comp = '';
58387
- let idx = sequence.length;
58388
-
58389
- while (idx-- > 0) {
58390
- const base = sequence[idx];
58391
- comp += complements.has(base) ? complements.get(base) : base;
58392
- }
58393
-
58394
- return comp;
58395
- }
58396
-
58397
58252
  const GtexUtils = {
58398
58253
  getTissueInfo: function (datasetId, baseURL) {
58399
58254
  datasetId = datasetId || 'gtex_v8';
@@ -58489,7 +58344,7 @@
58489
58344
  // single-exon transcript
58490
58345
  const xLeft = Math.max(0, coord.px);
58491
58346
  const xRight = Math.min(pixelWidth, coord.px1);
58492
- const width = Math.max(coord.pw, xRight - xLeft);
58347
+ const width = xRight - xLeft;
58493
58348
  ctx.fillRect(xLeft, py, width, h); // Arrows
58494
58349
  // Do not draw if strand is not +/-
58495
58350
 
@@ -58916,7 +58771,7 @@
58916
58771
  return this;
58917
58772
  }
58918
58773
 
58919
- supportsWholeGenome() {
58774
+ get supportsWholeGenome() {
58920
58775
  return (this.config.indexed === false || !this.config.indexURL) && this.config.supportsWholeGenome !== false;
58921
58776
  }
58922
58777
 
@@ -59069,15 +58924,9 @@
59069
58924
  for (let fd of featureData) {
59070
58925
  data.push(fd);
59071
58926
 
59072
- if (infoURL) {
59073
- if (fd.name && fd.name.toLowerCase() === "name" && fd.value && isString$3(fd.value) && !fd.value.startsWith("<")) {
59074
- const url = this.infoURL || this.config.infoURL;
59075
- const href = url.replace("$$", feature.name);
59076
- data.push({
59077
- name: "Info",
59078
- value: `<a target="_blank" href=${href}>${fd.value}</a>`
59079
- });
59080
- }
58927
+ if (infoURL && fd.name && fd.name.toLowerCase() === "name" && fd.value && isString$3(fd.value) && !fd.value.startsWith("<")) {
58928
+ const href = infoURL.replace("$$", feature.name);
58929
+ fd.value = `<a target=_blank href=${href}>${fd.value}</a>`;
59081
58930
  }
59082
58931
  } //Array.prototype.push.apply(data, featureData);
59083
58932
  // If we have clicked over an exon number it.
@@ -59151,17 +59000,31 @@
59151
59000
  }
59152
59001
 
59153
59002
  contextMenuItemList(clickState) {
59154
- if (isSecureContext()) {
59155
- const features = this.clickedFeatures(clickState);
59003
+ const features = this.clickedFeatures(clickState);
59156
59004
 
59157
- if (features.length > 1) {
59158
- features.sort((a, b) => a.end - a.start - (b.end - b.start));
59159
- }
59005
+ if (features.length > 1) {
59006
+ features.sort((a, b) => b.end - b.start - (a.end - a.start));
59007
+ }
59008
+
59009
+ const f = features[0]; // The shortest clicked feature
59010
+
59011
+ if (f.end - f.start <= 1000000) {
59012
+ const list = [{
59013
+ label: 'View feature sequence',
59014
+ click: async () => {
59015
+ let seq = await this.browser.genome.getSequence(f.chr, f.start, f.end);
59016
+
59017
+ if (f.strand === '-') {
59018
+ seq = reverseComplementSequence(seq);
59019
+ }
59160
59020
 
59161
- const f = features[0]; // The longest feature
59021
+ if (!seq) seq = "Unknown sequence";
59022
+ Alert.presentAlert(seq);
59023
+ }
59024
+ }];
59162
59025
 
59163
- if (f.end - f.start <= 1000000) {
59164
- return [{
59026
+ if (isSecureContext() && navigator.clipboard !== undefined) {
59027
+ list.push({
59165
59028
  label: 'Copy feature sequence',
59166
59029
  click: async () => {
59167
59030
  let seq = await this.browser.genome.getSequence(f.chr, f.start, f.end);
@@ -59170,14 +59033,21 @@
59170
59033
  seq = reverseComplementSequence(seq);
59171
59034
  }
59172
59035
 
59173
- navigator.clipboard.writeText(seq);
59036
+ try {
59037
+ await navigator.clipboard.writeText(seq);
59038
+ } catch (e) {
59039
+ console.error(e);
59040
+ Alert.presentAlert(`error copying sequence to clipboard ${e}`);
59041
+ }
59174
59042
  }
59175
- }, '<hr/>'];
59043
+ });
59176
59044
  }
59177
- } // Either not a secure context (i.e. http: protocol), or feature is too long
59178
-
59179
59045
 
59180
- return undefined;
59046
+ list.push('<hr/>');
59047
+ return list;
59048
+ } else {
59049
+ return undefined;
59050
+ }
59181
59051
  }
59182
59052
 
59183
59053
  description() {
@@ -59273,14 +59143,12 @@
59273
59143
  this.featureType = 'numeric';
59274
59144
  this.paintAxis = paintAxis;
59275
59145
  const format = config.format ? config.format.toLowerCase() : config.format;
59146
+ this.flipAxis = config.flipAxis ? config.flipAxis : false;
59147
+ this.logScale = config.logScale ? config.logScale : false;
59276
59148
 
59277
59149
  if ("bigwig" === format) {
59278
- this.flipAxis = config.flipAxis ? config.flipAxis : false;
59279
- this.logScale = config.logScale ? config.logScale : false;
59280
59150
  this.featureSource = new BWSource(config, this.browser.genome);
59281
59151
  } else if ("tdf" === format) {
59282
- this.flipAxis = config.flipAxis ? config.flipAxis : false;
59283
- this.logScale = config.logScale ? config.logScale : false;
59284
59152
  this.featureSource = new TDFSource(config, this.browser.genome);
59285
59153
  } else {
59286
59154
  this.featureSource = FeatureSource(config, this.browser.genome);
@@ -59518,7 +59386,7 @@
59518
59386
  }
59519
59387
  }
59520
59388
 
59521
- supportsWholeGenome() {
59389
+ get supportsWholeGenome() {
59522
59390
  return !this.config.indexURL && this.config.supportsWholeGenome !== false;
59523
59391
  }
59524
59392
  /**
@@ -60057,7 +59925,7 @@
60057
59925
 
60058
59926
  const sortHandler = sort => {
60059
59927
  const viewport = clickState.viewport;
60060
- const features = viewport.getCachedFeatures();
59928
+ const features = viewport.cachedFeatures;
60061
59929
  this.sortSamples(sort.chr, sort.start, sort.end, sort.direction, features);
60062
59930
  };
60063
59931
 
@@ -60077,7 +59945,7 @@
60077
59945
  }];
60078
59946
  }
60079
59947
 
60080
- supportsWholeGenome() {
59948
+ get supportsWholeGenome() {
60081
59949
  return (this.config.indexed === false || !this.config.indexURL) && this.config.supportsWholeGenome !== false;
60082
59950
  }
60083
59951
 
@@ -60163,10 +60031,16 @@
60163
60031
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
60164
60032
  * THE SOFTWARE.
60165
60033
  */
60034
+ /**
60035
+ * Represents 2 or more wig tracks overlaid on a common viewport.
60036
+ */
60166
60037
 
60167
60038
  class MergedTrack extends TrackBase {
60168
60039
  constructor(config, browser) {
60169
60040
  super(config, browser);
60041
+ this.type = "merged";
60042
+ this.featureType = 'numeric';
60043
+ this.paintAxis = paintAxis;
60170
60044
  }
60171
60045
 
60172
60046
  init(config) {
@@ -60177,21 +60051,6 @@
60177
60051
  super.init(config);
60178
60052
  }
60179
60053
 
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
60054
  async postInit() {
60196
60055
  this.tracks = [];
60197
60056
  const p = [];
@@ -60213,45 +60072,83 @@
60213
60072
  }
60214
60073
  }
60215
60074
 
60216
- this.height = this.config.height || 100;
60075
+ this.flipAxis = this.config.flipAxis ? this.config.flipAxis : false;
60076
+ this.logScale = this.config.logScale ? this.config.logScale : false;
60077
+ this.autoscale = this.config.autoscale || this.config.max === undefined;
60078
+
60079
+ if (!this.autoscale) {
60080
+ this.dataRange = {
60081
+ min: this.config.min || 0,
60082
+ max: this.config.max
60083
+ };
60084
+ }
60085
+
60086
+ for (let t of this.tracks) {
60087
+ t.autoscale = false;
60088
+ t.dataRange = this.dataRange;
60089
+ }
60090
+
60091
+ this.height = this.config.height || 50;
60217
60092
  return Promise.all(p);
60218
60093
  }
60219
60094
 
60095
+ get height() {
60096
+ return this._height;
60097
+ }
60098
+
60099
+ set height(h) {
60100
+ this._height = h;
60101
+
60102
+ if (this.tracks) {
60103
+ for (let t of this.tracks) {
60104
+ t.height = h;
60105
+ t.config.height = h;
60106
+ }
60107
+ }
60108
+ }
60109
+
60110
+ menuItemList() {
60111
+ let items = [];
60112
+
60113
+ if (this.flipAxis !== undefined) {
60114
+ items.push({
60115
+ label: "Flip y-axis",
60116
+ click: () => {
60117
+ this.flipAxis = !this.flipAxis;
60118
+ this.trackView.repaintViews();
60119
+ }
60120
+ });
60121
+ }
60122
+
60123
+ items = items.concat(MenuUtils.numericDataMenuItems(this.trackView));
60124
+ return items;
60125
+ }
60126
+
60220
60127
  async getFeatures(chr, bpStart, bpEnd, bpPerPixel) {
60221
60128
  const promises = this.tracks.map(t => t.getFeatures(chr, bpStart, bpEnd, bpPerPixel));
60222
60129
  return Promise.all(promises);
60223
60130
  }
60224
60131
 
60225
60132
  draw(options) {
60226
- var i, len, mergedFeatures, trackOptions, dataRange;
60227
- mergedFeatures = options.features; // Array of feature arrays, 1 for each track
60133
+ const mergedFeatures = options.features; // Array of feature arrays, 1 for each track
60228
60134
 
60229
- dataRange = autoscale(options.referenceFrame.chr, mergedFeatures); //IGVGraphics.fillRect(options.context, 0, options.pixelTop, options.pixelWidth, options.pixelHeight, {'fillStyle': "rgb(255, 255, 255)"});
60135
+ if (this.autoscale) {
60136
+ this.dataRange = autoscale(options.referenceFrame.chr, mergedFeatures);
60137
+ }
60230
60138
 
60231
- for (i = 0, len = this.tracks.length; i < len; i++) {
60232
- trackOptions = Object.assign({}, options);
60139
+ for (let i = 0, len = this.tracks.length; i < len; i++) {
60140
+ const trackOptions = Object.assign({}, options);
60233
60141
  trackOptions.features = mergedFeatures[i];
60234
- this.tracks[i].dataRange = dataRange;
60142
+ this.tracks[i].dataRange = this.dataRange;
60143
+ this.tracks[i].flipAxis = this.flipAxis;
60144
+ this.tracks[i].logScale = this.logScale;
60145
+ this.tracks[i].graphType = this.graphType;
60235
60146
  this.tracks[i].draw(trackOptions);
60236
60147
  }
60237
60148
  }
60238
60149
 
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
60150
  popupData(clickState, features) {
60254
- const featuresArray = features || clickState.viewport.getCachedFeatures();
60151
+ const featuresArray = features || clickState.viewport.cachedFeatures;
60255
60152
 
60256
60153
  if (featuresArray && featuresArray.length === this.tracks.length) {
60257
60154
  // Array of feature arrays, 1 for each track
@@ -60268,40 +60165,24 @@
60268
60165
  }
60269
60166
  }
60270
60167
 
60271
- supportsWholeGenome() {
60272
- const b = this.tracks.every(track => track.supportsWholeGenome());
60273
- return b;
60168
+ get supportsWholeGenome() {
60169
+ return this.tracks.every(track => track.supportsWholeGenome());
60274
60170
  }
60275
60171
 
60276
60172
  }
60277
60173
 
60278
60174
  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 {
60175
+ let min = 0;
60176
+ let max = -Number.MAX_VALUE;
60296
60177
 
60297
- featureArrays.forEach(function (features, i) {
60298
- features.forEach(function (f) {
60178
+ for (let features of featureArrays) {
60179
+ for (let f of features) {
60299
60180
  if (typeof f.value !== 'undefined' && !Number.isNaN(f.value)) {
60300
60181
  min = Math.min(min, f.value);
60301
60182
  max = Math.max(max, f.value);
60302
60183
  }
60303
- });
60304
- }); // }
60184
+ }
60185
+ }
60305
60186
 
60306
60187
  return {
60307
60188
  min: min,
@@ -60422,7 +60303,7 @@
60422
60303
  return this;
60423
60304
  }
60424
60305
 
60425
- supportsWholeGenome() {
60306
+ get supportsWholeGenome() {
60426
60307
  return true;
60427
60308
  }
60428
60309
 
@@ -60843,7 +60724,7 @@
60843
60724
  items = items.concat(MenuUtils.numericDataMenuItems(this.trackView));
60844
60725
  }
60845
60726
 
60846
- if (this.browser.circularView && true === this.browser.circularViewVisible) {
60727
+ if (this.browser.circularView) {
60847
60728
  items.push('<hr/>');
60848
60729
  items.push({
60849
60730
  label: 'Add interactions to circular view',
@@ -60860,7 +60741,7 @@
60860
60741
 
60861
60742
  contextMenuItemList(clickState) {
60862
60743
  // Experimental JBrowse feature
60863
- if (this.browser.circularView && true === this.browser.circularViewVisible) {
60744
+ if (this.browser.circularView) {
60864
60745
  const viewport = clickState.viewport;
60865
60746
  const list = [];
60866
60747
  list.push({
@@ -60885,20 +60766,20 @@
60885
60766
  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
60767
 
60887
60768
  const inView = cachedFeatures.filter(f => f.drawState);
60888
- if (inView.length === 0) erturn;
60889
- 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
- });
60769
+ if (inView.length === 0) return;
60770
+ const chords = makeBedPEChords(inView);
60771
+ sendChords(chords, this, refFrame, 0.5); //
60772
+ //
60773
+ // // for filtered set, distinguishing the chromosomes is more critical than tracks
60774
+ // const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? this.color : getChrColor(refFrame.chr), 0.5)
60775
+ // const trackColor = IGVColor.addAlpha(this.color, 0.5)
60776
+ //
60777
+ // // name the chord set to include locus and filtering information
60778
+ // const encodedName = this.name.replaceAll(' ', '%20')
60779
+ // const chordSetName = "all" === refFrame.chr ?
60780
+ // encodedName :
60781
+ // `${encodedName} (${refFrame.chr}:${refFrame.start}-${refFrame.end} ; range:${this.dataRange.min}-${this.dataRange.max})`
60782
+ // this.browser.circularView.addChords(chords, {track: chordSetName, color: chordSetColor, trackColor: trackColor})
60902
60783
  }
60903
60784
 
60904
60785
  doAutoscale(features) {
@@ -60985,7 +60866,7 @@
60985
60866
  clickedFeatures(clickState, features) {
60986
60867
  // We use the cached features rather than method to avoid async load. If the
60987
60868
  // 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();
60869
+ const featureList = features || clickState.viewport.cachedFeatures;
60989
60870
  const candidates = [];
60990
60871
 
60991
60872
  if (featureList) {
@@ -61356,7 +61237,7 @@
61356
61237
  return this;
61357
61238
  }
61358
61239
 
61359
- supportsWholeGenome() {
61240
+ get supportsWholeGenome() {
61360
61241
  return this.config.indexed === false || this.config.supportsWholeGenome === true;
61361
61242
  }
61362
61243
 
@@ -61571,7 +61452,7 @@
61571
61452
  variantColor = "gray";
61572
61453
  }
61573
61454
  } else if (this._color) {
61574
- variantColor = typeof this._color === "function" ? this._color(v) : this._color;
61455
+ variantColor = this.color;
61575
61456
  } else if ("NONVARIANT" === v.type) {
61576
61457
  variantColor = this.nonRefColor;
61577
61458
  } else if ("MIXED" === v.type) {
@@ -61583,6 +61464,10 @@
61583
61464
  return variantColor;
61584
61465
  }
61585
61466
 
61467
+ get color() {
61468
+ return this._color ? typeof this._color === "function" ? this._color(v) : this._color : this.defaultColor;
61469
+ }
61470
+
61586
61471
  clickedFeatures(clickState, features) {
61587
61472
  let featureList = super.clickedFeatures(clickState, features);
61588
61473
  const vGap = this.displayMode === 'EXPANDED' ? this.expandedVGap : this.squishedVGap;
@@ -61843,29 +61728,15 @@
61843
61728
  } // Experimental JBrowse circular view integration
61844
61729
 
61845
61730
 
61846
- if (this.browser.circularView && true === this.browser.circularViewVisible) {
61731
+ if (this.browser.circularView) {
61847
61732
  menuItems.push('<hr>');
61848
61733
  menuItems.push({
61849
61734
  label: 'Add SVs to circular view',
61850
61735
  click: () => {
61851
- const inView = [];
61852
61736
 
61853
61737
  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
- }
61738
+ this.sendChordsForViewport(viewport);
61861
61739
  }
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
61740
  }
61870
61741
  });
61871
61742
  }
@@ -61875,26 +61746,26 @@
61875
61746
 
61876
61747
  contextMenuItemList(clickState) {
61877
61748
  // Experimental JBrowse circular view integration
61878
- if (this.browser.circularView && true === this.browser.circularViewVisible) {
61749
+ if (this.browser.circularView) {
61879
61750
  const viewport = clickState.viewport;
61880
61751
  const list = [];
61881
61752
  list.push({
61882
61753
  label: 'Add SVs to Circular View',
61883
61754
  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
- });
61755
+ this.sendChordsForViewport(viewport);
61892
61756
  }
61893
61757
  });
61894
61758
  list.push('<hr/>');
61895
61759
  return list;
61896
61760
  }
61897
61761
  }
61762
+
61763
+ sendChordsForViewport(viewport) {
61764
+ const refFrame = viewport.referenceFrame;
61765
+ const inView = "all" === refFrame.chr ? this.featureSource.getAllFeatures() : this.featureSource.featureCache.queryFeatures(refFrame.chr, refFrame.start, refFrame.end);
61766
+ const chords = makeVCFChords(inView);
61767
+ sendChords(chords, this, refFrame, 0.5);
61768
+ }
61898
61769
  /**
61899
61770
  * Create a "color by" checkbox menu item, optionally initially checked
61900
61771
  * @param menuItem
@@ -62197,7 +62068,7 @@
62197
62068
 
62198
62069
 
62199
62070
  popupData(clickState) {
62200
- let features = clickState.viewport.getCachedFeatures();
62071
+ let features = clickState.viewport.cachedFeatures;
62201
62072
  if (!features || features.length === 0) return [];
62202
62073
  const tolerance = 3;
62203
62074
  const tissue = this.name;
@@ -62405,7 +62276,7 @@
62405
62276
  return this;
62406
62277
  }
62407
62278
 
62408
- supportsWholeGenome() {
62279
+ get supportsWholeGenome() {
62409
62280
  return true;
62410
62281
  }
62411
62282
 
@@ -62543,7 +62414,7 @@
62543
62414
  popupData(clickState) {
62544
62415
  let data = [];
62545
62416
  const track = clickState.viewport.trackView.track;
62546
- const features = clickState.viewport.getCachedFeatures();
62417
+ const features = clickState.viewport.cachedFeatures;
62547
62418
 
62548
62419
  if (features) {
62549
62420
  let count = 0;
@@ -62971,7 +62842,7 @@
62971
62842
  return items;
62972
62843
  }
62973
62844
 
62974
- supportsWholeGenome() {
62845
+ get supportsWholeGenome() {
62975
62846
  return false;
62976
62847
  }
62977
62848
 
@@ -63232,7 +63103,7 @@
63232
63103
  if (!this.featureCache) {
63233
63104
  const options = buildOptions(this.config);
63234
63105
  const data = await igvxhr.loadString(this.config.url, options);
63235
- this.featureCache = new FeatureCache(parseBP(data), genome);
63106
+ this.featureCache = new FeatureCache$1(parseBP(data), genome);
63236
63107
  return this.featureCache.queryFeatures(chr, start, end);
63237
63108
  } else {
63238
63109
  return this.featureCache.queryFeatures(chr, start, end);
@@ -63332,12 +63203,15 @@
63332
63203
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
63333
63204
  * THE SOFTWARE.
63334
63205
  */
63206
+ /**
63207
+ * Class represents an ideogram of a chromsome cytobands. It is used for the header of a track panel.
63208
+ *
63209
+ */
63335
63210
 
63336
63211
  class IdeogramTrack {
63337
63212
  constructor(browser) {
63338
63213
  this.browser = browser;
63339
63214
  this.type = 'ideogram';
63340
- this.id = this.type;
63341
63215
  this.height = 16;
63342
63216
  this.order = Number.MIN_SAFE_INTEGER;
63343
63217
  this.disableButtons = true;
@@ -63597,7 +63471,7 @@
63597
63471
  return this;
63598
63472
  }
63599
63473
 
63600
- supportsWholeGenome() {
63474
+ get supportsWholeGenome() {
63601
63475
  return false;
63602
63476
  }
63603
63477
 
@@ -64475,6 +64349,15 @@
64475
64349
  this.id = guid$2();
64476
64350
  }
64477
64351
 
64352
+ extend(locus) {
64353
+ const newStart = Math.min(locus.start, this.start);
64354
+ const newEnd = Math.max(locus.end, this.end);
64355
+ const ratio = (newEnd - newStart) / (this.end - this.start);
64356
+ this.start = newStart;
64357
+ this.end = newEnd;
64358
+ this.bpPerPixel *= ratio;
64359
+ }
64360
+
64478
64361
  calculateEnd(pixels) {
64479
64362
  return this.start + this.bpPerPixel * pixels;
64480
64363
  }
@@ -64559,7 +64442,7 @@
64559
64442
  const viewChanged = start !== this.start || bpPerPixel !== this.bpPerPixel;
64560
64443
 
64561
64444
  if (viewChanged) {
64562
- await browser.updateViews(this);
64445
+ await browser.updateViews(true);
64563
64446
  }
64564
64447
  }
64565
64448
 
@@ -64629,23 +64512,6 @@
64629
64512
  });
64630
64513
  }
64631
64514
 
64632
- function adjustReferenceFrame(scaleFactor, referenceFrame, viewportWidth, alignmentStart, alignmentLength) {
64633
- referenceFrame.bpPerPixel *= scaleFactor;
64634
- const alignmentEE = alignmentStart + alignmentLength;
64635
- const alignmentCC = (alignmentStart + alignmentEE) / 2;
64636
- referenceFrame.start = alignmentCC - referenceFrame.bpPerPixel * (viewportWidth / 2);
64637
- referenceFrame.end = referenceFrame.start + referenceFrame.bpPerPixel * viewportWidth;
64638
- referenceFrame.locusSearchString = referenceFrame.getLocusString();
64639
- }
64640
-
64641
- function createReferenceFrameWithAlignment(genome, chromosomeName, bpp, viewportWidth, alignmentStart, alignmentLength) {
64642
- const alignmentEE = alignmentStart + alignmentLength;
64643
- const alignmentCC = (alignmentStart + alignmentEE) / 2;
64644
- const ss = alignmentCC - bpp * (viewportWidth / 2);
64645
- const ee = ss + bpp * viewportWidth;
64646
- return new ReferenceFrame(genome, chromosomeName, ss, ee, bpp);
64647
- }
64648
-
64649
64515
  const defaultNucleotideColors = {
64650
64516
  "A": "rgb( 0, 200, 0)",
64651
64517
  "C": "rgb( 0,0,200)",
@@ -65692,6 +65558,65 @@
65692
65558
  button.addEventListener('click', () => browser.saveSVGtoFile({}));
65693
65559
  };
65694
65560
 
65561
+ const viewportColumnManager = {
65562
+ createColumns: (columnContainer, count) => {
65563
+ for (let i = 0; i < count; i++) {
65564
+ if (0 === i) {
65565
+ createColumn(columnContainer, 'igv-column');
65566
+ } else {
65567
+ columnContainer.appendChild(div$1({
65568
+ class: 'igv-column-shim'
65569
+ }));
65570
+ createColumn(columnContainer, 'igv-column');
65571
+ }
65572
+ }
65573
+ },
65574
+ removeColumnAtIndex: (i, column) => {
65575
+ const shim = 0 === i ? column.nextElementSibling : column.previousElementSibling;
65576
+ column.remove();
65577
+ shim.remove();
65578
+ },
65579
+ insertAfter: referenceElement => {
65580
+ const shim = div$1({
65581
+ class: 'igv-column-shim'
65582
+ });
65583
+ insertElementAfter(shim, referenceElement);
65584
+ const column = div$1({
65585
+ class: 'igv-column'
65586
+ });
65587
+ insertElementAfter(column, shim);
65588
+ return column;
65589
+ },
65590
+ insertBefore: (referenceElement, count) => {
65591
+ for (let i = 0; i < count; i++) {
65592
+ const column = div$1({
65593
+ class: 'igv-column'
65594
+ });
65595
+ insertElementBefore(column, referenceElement);
65596
+
65597
+ if (count > 1 && i > 0) {
65598
+ const columnShim = div$1({
65599
+ class: 'igv-column-shim'
65600
+ });
65601
+ insertElementBefore(columnShim, column);
65602
+ }
65603
+ }
65604
+ },
65605
+ indexOfColumn: (columnContainer, column) => {
65606
+ const allColumns = columnContainer.querySelectorAll('.igv-column');
65607
+
65608
+ for (let i = 0; i < allColumns.length; i++) {
65609
+ const c = allColumns[i];
65610
+
65611
+ if (c === column) {
65612
+ return i;
65613
+ }
65614
+ }
65615
+
65616
+ return undefined;
65617
+ }
65618
+ };
65619
+
65695
65620
  /*
65696
65621
  * The MIT License (MIT)
65697
65622
  *
@@ -65942,7 +65867,7 @@
65942
65867
  }
65943
65868
  }
65944
65869
 
65945
- supportsWholeGenome() {
65870
+ get supportsWholeGenome() {
65946
65871
  return true;
65947
65872
  }
65948
65873
 
@@ -66474,7 +66399,9 @@
66474
66399
  // deferred because ideogram and ruler are treated as "tracks", and tracks require a reference frame
66475
66400
 
66476
66401
  if (false !== session.showIdeogram) {
66477
- this.trackViews.push(new TrackView(this, this.columnContainer, new IdeogramTrack(this)));
66402
+ const ideogramTrack = new IdeogramTrack(this);
66403
+ ideogramTrack.id = 'ideogram';
66404
+ this.trackViews.push(new TrackView(this, this.columnContainer, ideogramTrack));
66478
66405
  }
66479
66406
 
66480
66407
  if (false !== session.showRuler) {
@@ -66524,7 +66451,12 @@
66524
66451
  }
66525
66452
  }
66526
66453
 
66527
- await this.loadTrackList(trackConfigurations);
66454
+ await this.loadTrackList(trackConfigurations); // The ruler and ideogram tracks are not explicitly loaded, but needs updated nonetheless.
66455
+
66456
+ for (let rtv of this.trackViews.filter(tv => tv.track.type === 'ruler' || tv.track.type === 'ideogram')) {
66457
+ rtv.updateViews();
66458
+ }
66459
+
66528
66460
  this.updateUIWithReferenceFrameList();
66529
66461
  }
66530
66462
 
@@ -66689,26 +66621,22 @@
66689
66621
  }
66690
66622
 
66691
66623
  async loadTrackList(configList) {
66692
- try {
66693
- const promises = [];
66694
-
66695
- for (let config of configList) {
66696
- promises.push(this.loadTrack(config, false));
66697
- }
66624
+ const promises = [];
66698
66625
 
66699
- const loadedTracks = await Promise.all(promises);
66700
- const groupAutoscaleViews = this.trackViews.filter(function (trackView) {
66701
- return trackView.track.autoscaleGroup;
66702
- });
66626
+ for (let config of configList) {
66627
+ promises.push(this.loadTrack(config));
66628
+ }
66703
66629
 
66704
- if (groupAutoscaleViews.length > 0) {
66705
- this.updateViews(groupAutoscaleViews);
66706
- }
66630
+ const loadedTracks = await Promise.all(promises);
66631
+ const groupAutoscaleViews = this.trackViews.filter(function (trackView) {
66632
+ return trackView.track.autoscaleGroup;
66633
+ });
66707
66634
 
66708
- return loadedTracks;
66709
- } finally {
66710
- await this.resize();
66635
+ if (groupAutoscaleViews.length > 0) {
66636
+ this.updateViews();
66711
66637
  }
66638
+
66639
+ return loadedTracks;
66712
66640
  }
66713
66641
 
66714
66642
  async loadROI(config) {
@@ -66722,7 +66650,9 @@
66722
66650
  }
66723
66651
  } else {
66724
66652
  this.roi.push(new ROI(config, this.genome));
66725
- }
66653
+ } // Force reload all views (force = true) to insure ROI features are loaded. Wasteful but this function is
66654
+ // rarely called.
66655
+
66726
66656
 
66727
66657
  await this.updateViews(true);
66728
66658
  }
@@ -66736,7 +66666,7 @@
66736
66666
  }
66737
66667
 
66738
66668
  for (let tv of this.trackViews) {
66739
- tv.updateViews(true);
66669
+ tv.repaintViews();
66740
66670
  }
66741
66671
  }
66742
66672
 
@@ -66744,7 +66674,7 @@
66744
66674
  this.roi = [];
66745
66675
 
66746
66676
  for (let tv of this.trackViews) {
66747
- tv.updateViews(true);
66677
+ tv.repaintViews();
66748
66678
  }
66749
66679
  }
66750
66680
 
@@ -66760,25 +66690,13 @@
66760
66690
  /**
66761
66691
  * Return a promise to load a track.
66762
66692
  *
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
66693
  * @param config
66776
66694
  * @param doResize - undefined by default
66777
66695
  * @returns {*}
66778
66696
  */
66779
66697
 
66780
66698
 
66781
- async loadTrack(config, doResize) {
66699
+ async loadTrack(config) {
66782
66700
  // config might be json
66783
66701
  if (isString$3(config)) {
66784
66702
  config = JSON.parse(config);
@@ -66843,11 +66761,6 @@
66843
66761
 
66844
66762
  msg += ": " + config.url;
66845
66763
  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
66764
  }
66852
66765
  }
66853
66766
  /**
@@ -67080,43 +66993,11 @@
67080
66993
  this.navbarManager.navbarDidResize(this.$navigation.width(), isWGV);
67081
66994
  }
67082
66995
 
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();
66996
+ resize.call(this);
66997
+ await this.updateViews();
67117
66998
  }
67118
66999
 
67119
- async updateViews(force) {
67000
+ async updateViews() {
67120
67001
  const trackViews = this.trackViews;
67121
67002
  this.updateLocusSearchWidget();
67122
67003
 
@@ -67127,7 +67008,7 @@
67127
67008
 
67128
67009
  if (this.dragObject) {
67129
67010
  for (let trackView of trackViews) {
67130
- await trackView.updateViews(force);
67011
+ await trackView.updateViews();
67131
67012
  }
67132
67013
  } else {
67133
67014
  // Group autoscale
@@ -67176,19 +67057,25 @@
67176
67057
  for (let trackView of groupTrackViews) {
67177
67058
  trackView.track.dataRange = dataRange;
67178
67059
  trackView.track.autoscale = false;
67179
- p.push(trackView.updateViews(force));
67060
+ p.push(trackView.updateViews());
67180
67061
  }
67181
67062
 
67182
67063
  await Promise.all(p);
67183
67064
  }
67184
67065
  }
67185
67066
 
67186
- await Promise.all(otherTracks.map(tv => tv.updateViews(force))); // for (let trackView of otherTracks) {
67067
+ await Promise.all(otherTracks.map(tv => tv.updateViews())); // for (let trackView of otherTracks) {
67187
67068
  // await trackView.updateViews(force);
67188
67069
  // }
67189
67070
  }
67190
67071
  }
67191
67072
 
67073
+ repaintViews() {
67074
+ for (let trackView of this.trackViews) {
67075
+ trackView.repaintViews();
67076
+ }
67077
+ }
67078
+
67192
67079
  updateLocusSearchWidget() {
67193
67080
  const referenceFrameList = this.referenceFrameList; // Update end position of reference frames based on pixel widths. This is hacky, but its been done here
67194
67081
  // for a long time, although indirectly.
@@ -67246,41 +67133,53 @@
67246
67133
  referenceFrame.zoomWithScaleFactor(this, scaleFactor, viewportWidth, centerBPOrUndefined);
67247
67134
  }
67248
67135
  }
67136
+ /**
67137
+ * Add a new multi-locus panel for the specified region
67138
+ * @param chr
67139
+ * @param start
67140
+ * @param end
67141
+ * @param referenceFrameLeft - optional, if supplied new panel should be placed to the immediate right
67142
+ */
67143
+
67249
67144
 
67250
- async presentMultiLocusPanel(alignment, referenceFrameLeft) {
67145
+ async addMultiLocusPanel(chr, start, end, referenceFrameLeft) {
67251
67146
  // account for reduced viewport width as a result of adding right mate pair panel
67252
67147
  const viewportWidth = this.calculateViewportWidth(1 + this.referenceFrameList.length);
67253
67148
  const scaleFactor = this.calculateViewportWidth(this.referenceFrameList.length) / this.calculateViewportWidth(1 + this.referenceFrameList.length);
67254
- adjustReferenceFrame(scaleFactor, referenceFrameLeft, viewportWidth, alignment.start, alignment.lengthOnRef); // create right mate pair reference frame
67255
67149
 
67256
- const mateChrName = this.genome.getChromosomeName(alignment.mate.chr);
67257
- const referenceFrameRight = createReferenceFrameWithAlignment(this.genome, mateChrName, referenceFrameLeft.bpPerPixel, viewportWidth, alignment.mate.position, alignment.lengthOnRef); // add right mate panel beside left mate panel
67150
+ for (let refFrame of this.referenceFrameList) {
67151
+ refFrame.bpPerPixel *= scaleFactor;
67152
+ }
67153
+
67154
+ const bpp = (end - start) / viewportWidth;
67155
+ const newReferenceFrame = new ReferenceFrame(this.genome, chr, start, end, bpp);
67156
+ const indexLeft = referenceFrameLeft ? this.referenceFrameList.indexOf(referenceFrameLeft) : this.referenceFrameList.length - 1;
67157
+ const indexRight = 1 + indexLeft; // TODO -- this is really ugly
67258
67158
 
67259
- const indexLeft = this.referenceFrameList.indexOf(referenceFrameLeft);
67260
- const indexRight = 1 + this.referenceFrameList.indexOf(referenceFrameLeft);
67261
67159
  const {
67262
67160
  $viewport
67263
67161
  } = this.trackViews[0].viewports[indexLeft];
67264
67162
  const viewportColumn = viewportColumnManager.insertAfter($viewport.get(0).parentElement);
67265
67163
 
67266
67164
  if (indexRight === this.referenceFrameList.length) {
67267
- this.referenceFrameList.push(referenceFrameRight);
67165
+ this.referenceFrameList.push(newReferenceFrame);
67268
67166
 
67269
67167
  for (let trackView of this.trackViews) {
67270
- const viewport = createViewport(trackView, viewportColumn, referenceFrameRight);
67168
+ const viewport = createViewport(trackView, viewportColumn, newReferenceFrame);
67271
67169
  trackView.viewports.push(viewport);
67272
67170
  }
67273
67171
  } else {
67274
- this.referenceFrameList.splice(indexRight, 0, referenceFrameRight);
67172
+ this.referenceFrameList.splice(indexRight, 0, newReferenceFrame);
67275
67173
 
67276
67174
  for (let trackView of this.trackViews) {
67277
- const viewport = createViewport(trackView, viewportColumn, referenceFrameRight);
67175
+ const viewport = createViewport(trackView, viewportColumn, newReferenceFrame);
67278
67176
  trackView.viewports.splice(indexRight, 0, viewport);
67279
67177
  }
67280
67178
  }
67281
67179
 
67282
67180
  this.centerLineList = this.createCenterLineList(this.columnContainer);
67283
- await this.resize();
67181
+ resize.call(this);
67182
+ await this.updateViews(true);
67284
67183
  }
67285
67184
 
67286
67185
  async removeMultiLocusPanel(referenceFrame) {
@@ -67309,8 +67208,15 @@
67309
67208
  const scaleFactor = this.calculateViewportWidth(1 + this.referenceFrameList.length) / this.calculateViewportWidth(this.referenceFrameList.length);
67310
67209
  await this.rescaleForMultiLocus(scaleFactor);
67311
67210
  }
67211
+ /**
67212
+ * Goto the locus represented by the selected referenceFrame, discarding all other panels
67213
+ *
67214
+ * @param referenceFrame
67215
+ * @returns {Promise<void>}
67216
+ */
67217
+
67312
67218
 
67313
- async selectMultiLocusPanel(referenceFrame) {
67219
+ async gotoMultilocusPanel(referenceFrame) {
67314
67220
  const referenceFrameIndex = this.referenceFrameList.indexOf(referenceFrame); // Remove columns for unselected panels
67315
67221
 
67316
67222
  this.columnContainer.querySelectorAll('.igv-column').forEach((column, c) => {
@@ -67358,7 +67264,7 @@
67358
67264
 
67359
67265
  this.centerLineList = this.createCenterLineList(this.columnContainer);
67360
67266
  this.updateUIWithReferenceFrameList();
67361
- await this.updateViews(true);
67267
+ await this.updateViews();
67362
67268
  }
67363
67269
  /**
67364
67270
  * @deprecated This is a deprecated method with no known usages. To be removed in a future release.
@@ -67611,20 +67517,6 @@
67611
67517
  const surl = (idx > 0 ? path.substring(0, idx) : path) + "?sessionURL=blob:" + this.compressedSession();
67612
67518
  return surl;
67613
67519
  }
67614
-
67615
- currentLoci() {
67616
- const loci = [];
67617
- 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;
67627
- }
67628
67520
  /**
67629
67521
  * Record a mouse click on a specific viewport. This might be the start of a drag operation. Dragging
67630
67522
  * (panning) is handled here so that the mouse can move out of a specific viewport (e.g. stray into another
@@ -67659,10 +67551,22 @@
67659
67551
  this.fireEvent('trackdragend');
67660
67552
  }
67661
67553
  }
67554
+ /**
67555
+ * Track drag here refers to vertical dragging to reorder tracks, not horizontal panning.
67556
+ *
67557
+ * @param trackView
67558
+ */
67559
+
67662
67560
 
67663
67561
  startTrackDrag(trackView) {
67664
67562
  this.dragTrack = trackView;
67665
67563
  }
67564
+ /**
67565
+ * Track drag here refers to vertical dragging to reorder tracks, not horizontal panning.
67566
+ *
67567
+ * @param dragDestination
67568
+ */
67569
+
67666
67570
 
67667
67571
  updateTrackDrag(dragDestination) {
67668
67572
  if (dragDestination && this.dragTrack) {
@@ -67736,12 +67640,9 @@
67736
67640
  }
67737
67641
 
67738
67642
  addWindowResizeHandler() {
67739
- this.boundWindowResizeHandler = windowResizeHandler.bind(this);
67643
+ // Create a copy of the prototype "resize" function bound to this instance. Neccessary to support removing.
67644
+ this.boundWindowResizeHandler = resize.bind(this);
67740
67645
  window.addEventListener('resize', this.boundWindowResizeHandler);
67741
-
67742
- function windowResizeHandler() {
67743
- this.resize();
67744
- }
67745
67646
  }
67746
67647
 
67747
67648
  removeWindowResizeHandler() {
@@ -67820,7 +67721,7 @@
67820
67721
  id: this.genome.id,
67821
67722
  chromosomes: makeCircViewChromosomes(this.genome)
67822
67723
  });
67823
- this.circularViewVisible = show === true;
67724
+ this.circularViewVisible = show;
67824
67725
  }
67825
67726
 
67826
67727
  get circularViewVisible() {
@@ -67835,6 +67736,48 @@
67835
67736
  }
67836
67737
 
67837
67738
  }
67739
+ /**
67740
+ * Function called win window is resized, or visibility changed (e.g. "show" from a tab). This is a function rather
67741
+ * than class method because it needs to be copied and bound to specific instances of browser to support listener
67742
+ * removal
67743
+ *
67744
+ * @returns {Promise<void>}
67745
+ */
67746
+
67747
+
67748
+ async function resize() {
67749
+ const viewportWidth = this.calculateViewportWidth(this.referenceFrameList.length);
67750
+
67751
+ for (let referenceFrame of this.referenceFrameList) {
67752
+ const index = this.referenceFrameList.indexOf(referenceFrame);
67753
+ const {
67754
+ chr,
67755
+ genome
67756
+ } = referenceFrame;
67757
+ const {
67758
+ bpLength
67759
+ } = genome.getChromosome(referenceFrame.chr);
67760
+ const viewportWidthBP = referenceFrame.toBP(viewportWidth); // viewportWidthBP > bpLength occurs when locus is full chromosome and user widens browser
67761
+
67762
+ if (GenomeUtils.isWholeGenomeView(chr) || viewportWidthBP > bpLength) {
67763
+ // console.log(`${ Date.now() } Recalc referenceFrame(${ index }) bpp. viewport ${ StringUtils.numberFormatter(viewportWidthBP) } > ${ StringUtils.numberFormatter(bpLength) }.`)
67764
+ referenceFrame.bpPerPixel = bpLength / viewportWidth;
67765
+ } else {
67766
+ // console.log(`${ Date.now() } Recalc referenceFrame(${ index }) end.`)
67767
+ referenceFrame.end = referenceFrame.start + referenceFrame.toBP(viewportWidth);
67768
+ }
67769
+
67770
+ for (let {
67771
+ viewports
67772
+ } of this.trackViews) {
67773
+ viewports[index].setWidth(viewportWidth);
67774
+ }
67775
+ }
67776
+
67777
+ this.updateUIWithReferenceFrameList(); //TODO -- update view only if needed. Reducing size never needed. Increasing size maybe
67778
+
67779
+ await this.updateViews(true);
67780
+ }
67838
67781
 
67839
67782
  function handleMouseMove(e) {
67840
67783
  e.preventDefault();