igv 2.11.0 → 2.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/igv.esm.js CHANGED
@@ -17624,7 +17624,7 @@ function Node(interval) {
17624
17624
  * @constructor
17625
17625
  */
17626
17626
 
17627
- class FeatureCache {
17627
+ class FeatureCache$1 {
17628
17628
 
17629
17629
  constructor(featureList, genome, range) {
17630
17630
 
@@ -19091,7 +19091,7 @@ function createMenuElements$1(itemList, popover) {
19091
19091
 
19092
19092
  function embedCSS$2() {
19093
19093
 
19094
- var css = '.igv-ui-popover {\n cursor: default;\n position: absolute;\n z-index: 2048;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: 1px;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n background-color: white; }\n .igv-ui-popover > div:first-child {\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee; }\n .igv-ui-popover > div:first-child > div:first-child {\n margin-left: 4px; }\n .igv-ui-popover > div:first-child > div:last-child {\n margin-right: 4px;\n height: 12px;\n width: 12px;\n color: #7F7F7F; }\n .igv-ui-popover > div:first-child > div:last-child:hover {\n cursor: pointer;\n color: #444; }\n .igv-ui-popover > div:last-child {\n overflow-y: auto;\n overflow-x: hidden;\n max-height: 400px;\n max-width: 800px;\n background-color: white; }\n .igv-ui-popover > div:last-child > div {\n -webkit-user-select: all;\n /* Chrome/Safari */\n -moz-user-select: all;\n /* Firefox */\n margin-left: 4px;\n margin-right: 4px;\n min-width: 220px;\n overflow-x: hidden;\n text-overflow: ellipsis;\n white-space: nowrap; }\n .igv-ui-popover > div:last-child > div > span {\n font-weight: bolder; }\n .igv-ui-popover > div:last-child hr {\n width: 100%; }\n\n.igv-ui-alert-dialog-container {\n box-sizing: content-box;\n position: absolute;\n z-index: 2048;\n top: 50%;\n left: 50%;\n width: 400px;\n height: 200px;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n outline: none;\n font-family: \"Open Sans\", sans-serif;\n font-size: 15px;\n font-weight: 400;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center; }\n .igv-ui-alert-dialog-container > div:first-child {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee; }\n .igv-ui-alert-dialog-container > div:first-child div:first-child {\n padding-left: 8px; }\n .igv-ui-alert-dialog-container .igv-ui-alert-dialog-body {\n color: #373737;\n width: 100%;\n height: calc(100% - 24px - 64px);\n overflow-y: scroll; }\n .igv-ui-alert-dialog-container .igv-ui-alert-dialog-body .igv-ui-alert-dialog-body-copy {\n cursor: pointer;\n margin: 16px;\n width: auto;\n height: auto;\n overflow-wrap: break-word;\n word-break: break-word;\n -webkit-user-select: all;\n -moz-user-select: all;\n -ms-user-select: all;\n user-select: all;\n background-color: white;\n border: unset; }\n .igv-ui-alert-dialog-container > div:last-child {\n width: 100%;\n margin-bottom: 10px;\n background-color: white;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: center;\n align-items: center; }\n .igv-ui-alert-dialog-container > div:last-child div {\n margin: unset;\n width: 40px;\n height: 30px;\n line-height: 30px;\n text-align: center;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n font-weight: 400;\n border-color: #2B81AF;\n border-style: solid;\n border-width: thin;\n border-radius: 4px;\n background-color: #2B81AF; }\n .igv-ui-alert-dialog-container > div:last-child div:hover {\n cursor: pointer;\n border-color: #25597f;\n background-color: #25597f; }\n\n.igv-ui-color-swatch {\n position: relative;\n box-sizing: content-box;\n display: flex;\n flex-flow: row;\n flex-wrap: wrap;\n justify-content: center;\n align-items: center;\n width: 32px;\n height: 32px;\n border-style: solid;\n border-width: 2px;\n border-color: white;\n border-radius: 4px; }\n\n.igv-ui-color-swatch:hover {\n border-color: dimgray; }\n\n.igv-ui-colorpicker-menu-close-button {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 32px;\n margin-top: 4px;\n margin-bottom: 4px;\n padding-right: 8px; }\n .igv-ui-colorpicker-menu-close-button i.fa {\n display: block;\n margin-left: 4px;\n margin-right: 4px;\n color: #5f5f5f; }\n .igv-ui-colorpicker-menu-close-button i.fa:hover,\n .igv-ui-colorpicker-menu-close-button i.fa:focus,\n .igv-ui-colorpicker-menu-close-button i.fa:active {\n cursor: pointer;\n color: #0f0f0f; }\n\n.igv-ui-generic-dialog-container {\n box-sizing: content-box;\n position: fixed;\n top: 0;\n left: 0;\n width: 300px;\n height: 200px;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n z-index: 2048;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-header {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-header div {\n margin-right: 4px;\n margin-bottom: 2px;\n height: 12px;\n width: 12px;\n color: #7F7F7F; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-header div:hover {\n cursor: pointer;\n color: #444; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-one-liner {\n color: #373737;\n width: 95%;\n height: 24px;\n line-height: 24px;\n text-align: left;\n margin-top: 8px;\n padding-left: 8px;\n overflow-wrap: break-word;\n background-color: white; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input {\n margin-top: 8px;\n width: 95%;\n height: 24px;\n color: #373737;\n line-height: 24px;\n padding-left: 8px;\n background-color: white;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input div {\n width: 30%;\n height: 100%;\n font-size: 16px;\n text-align: right;\n padding-right: 8px;\n background-color: white; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input input {\n display: block;\n height: 100%;\n width: 100%;\n padding-left: 4px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n color: #373737;\n text-align: left;\n outline: none;\n border-style: solid;\n border-width: thin;\n border-color: #7F7F7F;\n background-color: white; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input input {\n width: 50%;\n font-size: 16px; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-input {\n margin-top: 8px;\n width: calc(100% - 16px);\n height: 24px;\n color: #373737;\n line-height: 24px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-input input {\n display: block;\n height: 100%;\n width: 100%;\n padding-left: 4px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n color: #373737;\n text-align: left;\n outline: none;\n border-style: solid;\n border-width: thin;\n border-color: #7F7F7F;\n background-color: white; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-input input {\n font-size: 16px; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel {\n width: 100%;\n height: 28px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div {\n margin-top: 32px;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n width: 75px;\n height: 28px;\n line-height: 28px;\n text-align: center;\n border-color: transparent;\n border-style: solid;\n border-width: thin;\n border-radius: 2px; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div:first-child {\n margin-left: 32px;\n margin-right: 0;\n background-color: #5ea4e0; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div:last-child {\n margin-left: 0;\n margin-right: 32px;\n background-color: #c4c4c4; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div:first-child:hover {\n cursor: pointer;\n background-color: #3b5c7f; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div:last-child:hover {\n cursor: pointer;\n background-color: #7f7f7f; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok {\n width: 100%;\n height: 36px;\n margin-top: 32px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok div {\n width: 98px;\n height: 36px;\n line-height: 36px;\n text-align: center;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n border-color: white;\n border-style: solid;\n border-width: thin;\n border-radius: 4px;\n background-color: #2B81AF; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok div:hover {\n cursor: pointer;\n background-color: #25597f; }\n\n.igv-ui-generic-container {\n box-sizing: content-box;\n position: absolute;\n z-index: 2048;\n background-color: white;\n cursor: pointer;\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n justify-content: flex-start;\n align-items: center; }\n .igv-ui-generic-container div:first-child {\n cursor: move;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n height: 24px;\n width: 100%;\n background-color: #dddddd; }\n .igv-ui-generic-container div:first-child div {\n display: block;\n color: #5f5f5f;\n cursor: pointer;\n width: 14px;\n height: 14px;\n margin-right: 8px;\n margin-bottom: 4px; }\n\n.igv-ui-dialog {\n z-index: 2048;\n position: fixed;\n width: fit-content;\n height: fit-content;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n background-color: white;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400; }\n .igv-ui-dialog .igv-ui-dialog-header {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee; }\n .igv-ui-dialog .igv-ui-dialog-header div {\n margin-right: 4px;\n margin-bottom: 2px;\n height: 12px;\n width: 12px;\n color: #7F7F7F; }\n .igv-ui-dialog .igv-ui-dialog-header div:hover {\n cursor: pointer;\n color: #444; }\n .igv-ui-dialog .igv-ui-dialog-one-liner {\n width: 95%;\n height: 24px;\n line-height: 24px;\n text-align: left;\n margin: 8px;\n overflow-wrap: break-word;\n background-color: white;\n font-weight: bold; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel {\n width: 100%;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel div {\n margin: 16px;\n margin-top: 32px;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n width: 75px;\n height: 28px;\n line-height: 28px;\n text-align: center;\n border-color: transparent;\n border-style: solid;\n border-width: thin;\n border-radius: 2px; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel div:first-child {\n background-color: #5ea4e0; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel div:last-child {\n background-color: #c4c4c4; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel div:first-child:hover {\n cursor: pointer;\n background-color: #3b5c7f; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel div:last-child:hover {\n cursor: pointer;\n background-color: #7f7f7f; }\n .igv-ui-dialog .igv-ui-dialog-ok {\n width: 100%;\n height: 36px;\n margin-top: 32px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-ui-dialog .igv-ui-dialog-ok div {\n width: 98px;\n height: 36px;\n line-height: 36px;\n text-align: center;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n border-color: white;\n border-style: solid;\n border-width: thin;\n border-radius: 4px;\n background-color: #2B81AF; }\n .igv-ui-dialog .igv-ui-dialog-ok div:hover {\n cursor: pointer;\n background-color: #25597f; }\n\n.igv-ui-panel, .igv-ui-panel-column, .igv-ui-panel-row {\n z-index: 2048;\n background-color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n display: flex;\n justify-content: flex-start;\n align-items: flex-start; }\n\n.igv-ui-panel-column {\n display: flex;\n flex-direction: column; }\n\n.igv-ui-panel-row {\n display: flex;\n flex-direction: row; }\n\n.igv-ui-textbox {\n background-color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n display: flex;\n justify-content: flex-start;\n align-items: flex-start; }\n\n/*# sourceMappingURL=igv-ui.css.map */\n';
19094
+ var css = '.igv-ui-popover {\n cursor: default;\n position: absolute;\n z-index: 2048;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: 1px;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n background-color: white; }\n .igv-ui-popover > div:first-child {\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee; }\n .igv-ui-popover > div:first-child > div:first-child {\n margin-left: 4px; }\n .igv-ui-popover > div:first-child > div:last-child {\n margin-right: 4px;\n height: 12px;\n width: 12px;\n color: #7F7F7F; }\n .igv-ui-popover > div:first-child > div:last-child:hover {\n cursor: pointer;\n color: #444; }\n .igv-ui-popover > div:last-child {\n overflow-y: auto;\n overflow-x: hidden;\n max-height: 400px;\n max-width: 800px;\n background-color: white; }\n .igv-ui-popover > div:last-child > div {\n -webkit-user-select: text;\n -moz-user-select: text;\n -ms-user-select: text;\n user-select: text;\n margin-left: 4px;\n margin-right: 4px;\n min-width: 220px;\n overflow-x: hidden;\n text-overflow: ellipsis;\n white-space: nowrap; }\n .igv-ui-popover > div:last-child > div > span {\n font-weight: bolder; }\n .igv-ui-popover > div:last-child hr {\n width: 100%; }\n\n.igv-ui-alert-dialog-container {\n box-sizing: content-box;\n position: absolute;\n z-index: 2048;\n top: 50%;\n left: 50%;\n width: 400px;\n height: 200px;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n outline: none;\n font-family: \"Open Sans\", sans-serif;\n font-size: 15px;\n font-weight: 400;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center; }\n .igv-ui-alert-dialog-container > div:first-child {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee; }\n .igv-ui-alert-dialog-container > div:first-child div:first-child {\n padding-left: 8px; }\n .igv-ui-alert-dialog-container .igv-ui-alert-dialog-body {\n -webkit-user-select: text;\n -moz-user-select: text;\n -ms-user-select: text;\n user-select: text;\n color: #373737;\n width: 100%;\n height: calc(100% - 24px - 64px);\n overflow-y: scroll; }\n .igv-ui-alert-dialog-container .igv-ui-alert-dialog-body .igv-ui-alert-dialog-body-copy {\n margin: 16px;\n width: auto;\n height: auto;\n overflow-wrap: break-word;\n word-break: break-word;\n background-color: white;\n border: unset; }\n .igv-ui-alert-dialog-container > div:last-child {\n width: 100%;\n margin-bottom: 10px;\n background-color: white;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: center;\n align-items: center; }\n .igv-ui-alert-dialog-container > div:last-child div {\n margin: unset;\n width: 40px;\n height: 30px;\n line-height: 30px;\n text-align: center;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n font-weight: 400;\n border-color: #2B81AF;\n border-style: solid;\n border-width: thin;\n border-radius: 4px;\n background-color: #2B81AF; }\n .igv-ui-alert-dialog-container > div:last-child div:hover {\n cursor: pointer;\n border-color: #25597f;\n background-color: #25597f; }\n\n.igv-ui-color-swatch {\n position: relative;\n box-sizing: content-box;\n display: flex;\n flex-flow: row;\n flex-wrap: wrap;\n justify-content: center;\n align-items: center;\n width: 32px;\n height: 32px;\n border-style: solid;\n border-width: 2px;\n border-color: white;\n border-radius: 4px; }\n\n.igv-ui-color-swatch:hover {\n border-color: dimgray; }\n\n.igv-ui-colorpicker-menu-close-button {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 32px;\n margin-top: 4px;\n margin-bottom: 4px;\n padding-right: 8px; }\n .igv-ui-colorpicker-menu-close-button i.fa {\n display: block;\n margin-left: 4px;\n margin-right: 4px;\n color: #5f5f5f; }\n .igv-ui-colorpicker-menu-close-button i.fa:hover,\n .igv-ui-colorpicker-menu-close-button i.fa:focus,\n .igv-ui-colorpicker-menu-close-button i.fa:active {\n cursor: pointer;\n color: #0f0f0f; }\n\n.igv-ui-generic-dialog-container {\n box-sizing: content-box;\n position: fixed;\n top: 0;\n left: 0;\n width: 300px;\n height: 200px;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n z-index: 2048;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-header {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-header div {\n margin-right: 4px;\n margin-bottom: 2px;\n height: 12px;\n width: 12px;\n color: #7F7F7F; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-header div:hover {\n cursor: pointer;\n color: #444; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-one-liner {\n color: #373737;\n width: 95%;\n height: 24px;\n line-height: 24px;\n text-align: left;\n margin-top: 8px;\n padding-left: 8px;\n overflow-wrap: break-word;\n background-color: white; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input {\n margin-top: 8px;\n width: 95%;\n height: 24px;\n color: #373737;\n line-height: 24px;\n padding-left: 8px;\n background-color: white;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input div {\n width: 30%;\n height: 100%;\n font-size: 16px;\n text-align: right;\n padding-right: 8px;\n background-color: white; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input input {\n display: block;\n height: 100%;\n width: 100%;\n padding-left: 4px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n color: #373737;\n text-align: left;\n outline: none;\n border-style: solid;\n border-width: thin;\n border-color: #7F7F7F;\n background-color: white; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input input {\n width: 50%;\n font-size: 16px; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-input {\n margin-top: 8px;\n width: calc(100% - 16px);\n height: 24px;\n color: #373737;\n line-height: 24px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-input input {\n display: block;\n height: 100%;\n width: 100%;\n padding-left: 4px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n color: #373737;\n text-align: left;\n outline: none;\n border-style: solid;\n border-width: thin;\n border-color: #7F7F7F;\n background-color: white; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-input input {\n font-size: 16px; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel {\n width: 100%;\n height: 28px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div {\n margin-top: 32px;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n width: 75px;\n height: 28px;\n line-height: 28px;\n text-align: center;\n border-color: transparent;\n border-style: solid;\n border-width: thin;\n border-radius: 2px; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div:first-child {\n margin-left: 32px;\n margin-right: 0;\n background-color: #5ea4e0; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div:last-child {\n margin-left: 0;\n margin-right: 32px;\n background-color: #c4c4c4; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div:first-child:hover {\n cursor: pointer;\n background-color: #3b5c7f; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div:last-child:hover {\n cursor: pointer;\n background-color: #7f7f7f; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok {\n width: 100%;\n height: 36px;\n margin-top: 32px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok div {\n width: 98px;\n height: 36px;\n line-height: 36px;\n text-align: center;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n border-color: white;\n border-style: solid;\n border-width: thin;\n border-radius: 4px;\n background-color: #2B81AF; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok div:hover {\n cursor: pointer;\n background-color: #25597f; }\n\n.igv-ui-generic-container {\n box-sizing: content-box;\n position: absolute;\n z-index: 2048;\n background-color: white;\n cursor: pointer;\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n justify-content: flex-start;\n align-items: center; }\n .igv-ui-generic-container > div:first-child {\n cursor: move;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n height: 24px;\n width: 100%;\n background-color: #dddddd; }\n .igv-ui-generic-container > div:first-child > div {\n display: block;\n color: #5f5f5f;\n cursor: pointer;\n width: 14px;\n height: 14px;\n margin-right: 8px;\n margin-bottom: 4px; }\n\n.igv-ui-dialog {\n z-index: 2048;\n position: fixed;\n width: fit-content;\n height: fit-content;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n background-color: white;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400; }\n .igv-ui-dialog .igv-ui-dialog-header {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee; }\n .igv-ui-dialog .igv-ui-dialog-header div {\n margin-right: 4px;\n margin-bottom: 2px;\n height: 12px;\n width: 12px;\n color: #7F7F7F; }\n .igv-ui-dialog .igv-ui-dialog-header div:hover {\n cursor: pointer;\n color: #444; }\n .igv-ui-dialog .igv-ui-dialog-one-liner {\n width: 95%;\n height: 24px;\n line-height: 24px;\n text-align: left;\n margin: 8px;\n overflow-wrap: break-word;\n background-color: white;\n font-weight: bold; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel {\n width: 100%;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel div {\n margin: 16px;\n margin-top: 32px;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n width: 75px;\n height: 28px;\n line-height: 28px;\n text-align: center;\n border-color: transparent;\n border-style: solid;\n border-width: thin;\n border-radius: 2px; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel div:first-child {\n background-color: #5ea4e0; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel div:last-child {\n background-color: #c4c4c4; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel div:first-child:hover {\n cursor: pointer;\n background-color: #3b5c7f; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel div:last-child:hover {\n cursor: pointer;\n background-color: #7f7f7f; }\n .igv-ui-dialog .igv-ui-dialog-ok {\n width: 100%;\n height: 36px;\n margin-top: 32px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-ui-dialog .igv-ui-dialog-ok div {\n width: 98px;\n height: 36px;\n line-height: 36px;\n text-align: center;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n border-color: white;\n border-style: solid;\n border-width: thin;\n border-radius: 4px;\n background-color: #2B81AF; }\n .igv-ui-dialog .igv-ui-dialog-ok div:hover {\n cursor: pointer;\n background-color: #25597f; }\n\n.igv-ui-panel, .igv-ui-panel-column, .igv-ui-panel-row {\n z-index: 2048;\n background-color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n display: flex;\n justify-content: flex-start;\n align-items: flex-start; }\n\n.igv-ui-panel-column {\n display: flex;\n flex-direction: column; }\n\n.igv-ui-panel-row {\n display: flex;\n flex-direction: row; }\n\n.igv-ui-textbox {\n background-color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n display: flex;\n justify-content: flex-start;\n align-items: flex-start; }\n\n/*# sourceMappingURL=igv-ui.css.map */\n';
19095
19095
 
19096
19096
  var style = document.createElement('style');
19097
19097
  style.setAttribute('type', 'text/css');
@@ -19711,6 +19711,7 @@ const knownFileExtensions = new Set([
19711
19711
  "vcf",
19712
19712
  "bb",
19713
19713
  "bigbed",
19714
+ "biginteract",
19714
19715
  "bw",
19715
19716
  "bigwig",
19716
19717
  "bam",
@@ -19861,6 +19862,7 @@ function inferTrackType(config) {
19861
19862
  case "bed":
19862
19863
  case "bigbed":
19863
19864
  case "bb":
19865
+ case "biginteract":
19864
19866
  return "bedtype"
19865
19867
  default:
19866
19868
  return "annotation"
@@ -20586,14 +20588,13 @@ class Viewport {
20586
20588
  console.log('Viewport - draw(drawConfiguration, features, roiFeatures)');
20587
20589
  }
20588
20590
 
20589
- checkContentHeight() {
20591
+ checkContentHeight(features) {
20590
20592
 
20591
20593
  let track = this.trackView.track;
20592
-
20594
+ features = features || this.cachedFeatures;
20593
20595
  if ("FILL" === track.displayMode) {
20594
20596
  this.setContentHeight(this.$viewport.height());
20595
20597
  } else if (typeof track.computePixelHeight === 'function') {
20596
- let features = this.cachedFeatures;
20597
20598
  if (features && features.length > 0) {
20598
20599
  let requiredContentHeight = track.computePixelHeight(features);
20599
20600
  let currentContentHeight = this.$content.height();
@@ -20609,12 +20610,11 @@ class Viewport {
20609
20610
  }
20610
20611
 
20611
20612
  setContentHeight(contentHeight) {
20613
+
20612
20614
  // Maximum height of a canvas is ~32,000 pixels on Chrome, possibly smaller on other platforms
20613
20615
  contentHeight = Math.min(contentHeight, 32000);
20614
-
20615
20616
  this.$content.height(contentHeight);
20616
-
20617
- if (this.tile) this.tile.invalidate = true;
20617
+ if (this.canvas._data) this.canvas._data.invalidate = true;
20618
20618
  }
20619
20619
 
20620
20620
  isLoading() {
@@ -20631,8 +20631,6 @@ class Viewport {
20631
20631
 
20632
20632
  setWidth(width) {
20633
20633
  this.$viewport.width(width);
20634
- this.canvas.style.width = (`${width}px`);
20635
- this.canvas.setAttribute('width', width);
20636
20634
  }
20637
20635
 
20638
20636
  getWidth() {
@@ -22759,7 +22757,7 @@ const Cytoband = function (start, end, name, typestain) {
22759
22757
  }
22760
22758
  };
22761
22759
 
22762
- const _version = "2.11.0";
22760
+ const _version = "2.11.1";
22763
22761
  function version() {
22764
22762
  return _version
22765
22763
  }
@@ -23294,17 +23292,21 @@ class TrackViewport extends Viewport {
23294
23292
  checkZoomIn() {
23295
23293
 
23296
23294
  const showZoomInNotice = () => {
23297
- const referenceFrame = this.referenceFrame;
23298
23295
  if (this.referenceFrame.chr.toLowerCase() === "all" && !this.trackView.track.supportsWholeGenome()) {
23299
23296
  return true
23300
23297
  } else {
23301
23298
  const visibilityWindow = this.trackView.track.visibilityWindow;
23302
23299
  return (
23303
23300
  visibilityWindow !== undefined && visibilityWindow > 0 &&
23304
- (referenceFrame.bpPerPixel * this.$viewport.width() > visibilityWindow))
23301
+ (this.referenceFrame.bpPerPixel * this.$viewport.width() > visibilityWindow))
23305
23302
  }
23306
23303
  };
23307
23304
 
23305
+ if (this.trackView.track && "sequence" === this.trackView.track.type && this.referenceFrame.bpPerPixel > 1) {
23306
+ if (this.canvas) ;
23307
+ return false
23308
+ }
23309
+
23308
23310
  if (!(this.viewIsReady())) {
23309
23311
  return false
23310
23312
  }
@@ -23314,7 +23316,8 @@ class TrackViewport extends Viewport {
23314
23316
  // Out of visibility window
23315
23317
  if (this.canvas) {
23316
23318
  this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
23317
- this.tile = undefined;
23319
+ this.canvas._data = undefined;
23320
+ this.featureCache = undefined;
23318
23321
  }
23319
23322
  this.$zoomInNotice.show();
23320
23323
 
@@ -23331,21 +23334,19 @@ class TrackViewport extends Viewport {
23331
23334
  }
23332
23335
 
23333
23336
  return true
23334
-
23335
-
23336
23337
  }
23337
23338
 
23339
+ /**
23340
+ * Adjust the canvas to the current genomic state.
23341
+ */
23338
23342
  shift() {
23339
- const self = this;
23340
- const referenceFrame = self.referenceFrame;
23341
-
23342
- if (self.canvas &&
23343
- self.tile &&
23344
- self.tile.chr === self.referenceFrame.chr &&
23345
- self.tile.bpPerPixel === referenceFrame.bpPerPixel) {
23346
-
23347
- const pixelOffset = Math.round((self.tile.startBP - referenceFrame.start) / referenceFrame.bpPerPixel);
23348
- self.canvas.style.left = pixelOffset + "px";
23343
+ const referenceFrame = this.referenceFrame;
23344
+ if (this.canvas &&
23345
+ this.canvas._data &&
23346
+ this.canvas._data.chr === this.referenceFrame.chr &&
23347
+ this.canvas._data.bpPerPixel === referenceFrame.bpPerPixel) {
23348
+ const pixelOffset = Math.round((this.canvas._data.startBP - referenceFrame.start) / referenceFrame.bpPerPixel);
23349
+ this.canvas.style.left = pixelOffset + "px";
23349
23350
  }
23350
23351
  }
23351
23352
 
@@ -23368,22 +23369,23 @@ class TrackViewport extends Viewport {
23368
23369
  this.startSpinner();
23369
23370
 
23370
23371
  try {
23371
- const features = await this.getFeatures(this.trackView.track, chr, bpStart, bpEnd, referenceFrame.bpPerPixel);
23372
+ const track = this.trackView.track;
23373
+ const features = await this.getFeatures(track, chr, bpStart, bpEnd, referenceFrame.bpPerPixel);
23372
23374
  let roiFeatures = [];
23373
- const roi = mergeArrays(this.browser.roi, this.trackView.track.roi);
23375
+ const roi = mergeArrays(this.browser.roi, track.roi);
23374
23376
  if (roi) {
23375
23377
  for (let r of roi) {
23376
- const f = await
23377
- r.getFeatures(chr, bpStart, bpEnd, referenceFrame.bpPerPixel);
23378
+ const f = await r.getFeatures(chr, bpStart, bpEnd, referenceFrame.bpPerPixel);
23378
23379
  roiFeatures.push({track: r, features: f});
23379
23380
  }
23380
23381
  }
23381
23382
 
23382
- this.tile = new Tile(chr, bpStart, bpEnd, referenceFrame.bpPerPixel, features, roiFeatures);
23383
+ const mr = track && ("wig" === track.type || "merged" === track.type); // wig tracks are potentially multiresolution (e.g. bigwig)
23384
+ this.featureCache = new FeatureCache(chr, bpStart, bpEnd, referenceFrame.bpPerPixel, features, roiFeatures, mr);
23383
23385
  this.loading = false;
23384
23386
  this.hideMessage();
23385
23387
  this.stopSpinner();
23386
- return this.tile
23388
+ return this.featureCache
23387
23389
  } catch (error) {
23388
23390
  // Track might have been removed during load
23389
23391
  if (this.trackView && this.trackView.disposed !== true) {
@@ -23397,25 +23399,31 @@ class TrackViewport extends Viewport {
23397
23399
  }
23398
23400
  }
23399
23401
 
23400
- async repaint() {
23402
+ /**
23403
+ * Repaint the canvas for the current genomic state.
23404
+ *
23405
+ * @returns {Promise<void>}
23406
+ */
23407
+ repaint() {
23401
23408
 
23402
- if (undefined === this.tile) {
23409
+ if (undefined === this.featureCache) {
23403
23410
  return
23404
23411
  }
23405
23412
 
23406
- let {features, roiFeatures, bpPerPixel, startBP, endBP} = this.tile;
23413
+ let {features, roiFeatures} = this.featureCache;
23414
+ //this.tile.bpPerPixel = this.referenceFrame.bpPerPixel
23407
23415
 
23408
23416
  // const isWGV = GenomeUtils.isWholeGenomeView(this.browser.referenceFrameList[0].chr)
23409
23417
  const isWGV = GenomeUtils.isWholeGenomeView(this.referenceFrame.chr);
23410
- let pixelWidth;
23411
23418
 
23419
+ let pixelWidth;
23420
+ const startBP = this.featureCache.startBP;
23421
+ const endBP = this.featureCache.endBP;
23422
+ let bpPerPixel = this.referenceFrame.bpPerPixel;
23412
23423
  if (isWGV) {
23413
- bpPerPixel = this.referenceFrame.end / this.$viewport.width();
23414
- startBP = 0;
23415
- endBP = this.referenceFrame.end;
23416
23424
  pixelWidth = this.$viewport.width();
23417
23425
  } else {
23418
- pixelWidth = Math.ceil((endBP - startBP) / bpPerPixel);
23426
+ pixelWidth = 3 * this.$viewport.width();
23419
23427
  }
23420
23428
 
23421
23429
  // For deep tracks we paint a canvas == 3*viewportHeight centered on the current vertical scroll position
@@ -23475,17 +23483,27 @@ class TrackViewport extends Viewport {
23475
23483
 
23476
23484
  this.draw(drawConfiguration, features, roiFeatures);
23477
23485
 
23478
- this.canvasVerticalRange = {top: canvasTop, bottom: canvasTop + pixelHeight};
23486
+ // Attach metadata to canvas
23487
+ newCanvas._data = {
23488
+ chr: this.referenceFrame.chr, startBP, endBP, bpPerPixel, top: canvasTop, bottom: canvasTop + pixelHeight
23489
+ };
23479
23490
 
23480
- if (this.$canvas) {
23481
- this.$canvas.remove();
23491
+ if (this.canvas) {
23492
+ $$1(this.canvas).remove();
23482
23493
  }
23483
- this.$canvas = $$1(newCanvas);
23484
- this.$content.append(this.$canvas);
23485
23494
  this.canvas = newCanvas;
23486
23495
  this.ctx = ctx;
23496
+ this.$content.append($$1(newCanvas));
23497
+
23487
23498
  }
23488
23499
 
23500
+ /**
23501
+ * Draw the associated track.
23502
+ *
23503
+ * @param drawConfiguration
23504
+ * @param features
23505
+ * @param roiFeatures
23506
+ */
23489
23507
  draw(drawConfiguration, features, roiFeatures) {
23490
23508
 
23491
23509
  // console.log(`${ Date.now() } viewport draw(). track ${ this.trackView.track.type }. content-css-top ${ this.$content.css('top') }. canvas-top ${ drawConfiguration.pixelTop }.`)
@@ -23502,60 +23520,6 @@ class TrackViewport extends Viewport {
23502
23520
  }
23503
23521
  }
23504
23522
 
23505
- // TODO: Nolonger used. Will discard
23506
- async toSVG(tile) {
23507
-
23508
- // Nothing to do if zoomInNotice is active
23509
- if (this.$zoomInNotice && this.$zoomInNotice.is(":visible")) {
23510
- return
23511
- }
23512
-
23513
- const referenceFrame = this.referenceFrame;
23514
- const bpPerPixel = tile.bpPerPixel;
23515
- const features = tile.features;
23516
- const roiFeatures = tile.roiFeatures;
23517
- const pixelWidth = this.$viewport.width();
23518
- const pixelHeight = this.$viewport.height();
23519
- const bpStart = referenceFrame.start;
23520
- const bpEnd = referenceFrame.start + pixelWidth * referenceFrame.bpPerPixel;
23521
-
23522
- const ctx$1 = new ctx(
23523
- {
23524
- // svg
23525
- width: pixelWidth,
23526
- height: pixelHeight,
23527
- viewbox:
23528
- {
23529
- x: 0,
23530
- y: -this.$content.position().top,
23531
- width: pixelWidth,
23532
- height: pixelHeight
23533
- }
23534
-
23535
- });
23536
-
23537
- const drawConfiguration =
23538
- {
23539
- viewport: this,
23540
- context: ctx$1,
23541
- top: -this.$content.position().top,
23542
- pixelTop: 0, // for compatibility with canvas draw
23543
- pixelWidth,
23544
- pixelHeight,
23545
- bpStart,
23546
- bpEnd,
23547
- bpPerPixel,
23548
- referenceFrame: this.referenceFrame,
23549
- selection: this.selection,
23550
- viewportWidth: pixelWidth,
23551
- };
23552
-
23553
- this.draw(drawConfiguration, features, roiFeatures);
23554
-
23555
- return ctx$1.getSerializedSvg(true)
23556
-
23557
- }
23558
-
23559
23523
  containsPosition(chr, position) {
23560
23524
  if (this.referenceFrame.chr === chr && position >= this.referenceFrame.start) {
23561
23525
  return position <= this.referenceFrame.calculateEnd(this.getWidth())
@@ -23568,11 +23532,12 @@ class TrackViewport extends Viewport {
23568
23532
  return this.loading
23569
23533
  }
23570
23534
 
23571
- saveImage() {
23535
+ savePNG() {
23572
23536
 
23573
23537
  if (!this.ctx) return
23574
23538
 
23575
- const canvasTop = this.canvasVerticalRange ? this.canvasVerticalRange.top : 0;
23539
+ const canvasMetadata = this.canvas._data;
23540
+ const canvasTop = canvasMetadata ? canvasMetadata.top : 0;
23576
23541
  const devicePixelRatio = window.devicePixelRatio;
23577
23542
  const w = this.$viewport.width() * devicePixelRatio;
23578
23543
  const h = this.$viewport.height() * devicePixelRatio;
@@ -23702,26 +23667,25 @@ class TrackViewport extends Viewport {
23702
23667
  selection: this.selection
23703
23668
  };
23704
23669
 
23705
- const features = this.tile ? this.tile.features : [];
23706
- const roiFeatures = this.tile ? this.tile.roiFeatures : undefined;
23670
+ const features = this.featureCache ? this.featureCache.features : [];
23671
+ const roiFeatures = this.featureCache ? this.featureCache.roiFeatures : undefined;
23707
23672
  this.draw(config, features, roiFeatures);
23708
23673
 
23709
23674
  context.restore();
23710
23675
 
23711
23676
  }
23712
23677
 
23713
- getCachedFeatures() {
23714
- return this.tile ? this.tile.features : []
23678
+ get cachedFeatures() {
23679
+ return this.featureCache ? this.featureCache.features : []
23715
23680
  }
23716
23681
 
23717
23682
  async getFeatures(track, chr, start, end, bpPerPixel) {
23718
23683
 
23719
- if (this.tile && this.tile.containsRange(chr, start, end, bpPerPixel)) {
23720
- return this.tile.features
23684
+ if (this.featureCache && this.featureCache.containsRange(chr, start, end, bpPerPixel)) {
23685
+ return this.featureCache.features
23721
23686
  } else if (typeof track.getFeatures === "function") {
23722
23687
  const features = await track.getFeatures(chr, start, end, bpPerPixel, this);
23723
- this.cachedFeatures = features;
23724
- this.checkContentHeight();
23688
+ this.checkContentHeight(features);
23725
23689
  return features
23726
23690
  } else {
23727
23691
  return undefined
@@ -23959,9 +23923,7 @@ class TrackViewport extends Viewport {
23959
23923
  lastClickTime = time;
23960
23924
 
23961
23925
  }
23962
-
23963
23926
  }
23964
-
23965
23927
  }
23966
23928
 
23967
23929
  removeViewportClickHandler(viewport) {
@@ -24025,16 +23987,10 @@ function mouseUpHandler(event) {
24025
23987
  function createClickState(event, viewport) {
24026
23988
 
24027
23989
  const referenceFrame = viewport.referenceFrame;
24028
-
24029
23990
  const viewportCoords = translateMouseCoordinates$1(event, viewport.contentDiv);
24030
23991
  const canvasCoords = translateMouseCoordinates$1(event, viewport.canvas);
24031
-
24032
23992
  const genomicLocation = ((referenceFrame.start) + referenceFrame.toBP(viewportCoords.x));
24033
23993
 
24034
- if (undefined === genomicLocation || null === viewport.tile) {
24035
- return undefined
24036
- }
24037
-
24038
23994
  return {
24039
23995
  event,
24040
23996
  viewport,
@@ -24095,22 +24051,30 @@ function formatPopoverText(nameValues) {
24095
24051
  return rows.join('')
24096
24052
  }
24097
24053
 
24098
- var Tile = function (chr, tileStart, tileEnd, bpPerPixel, features, roiFeatures) {
24099
- this.chr = chr;
24100
- this.startBP = tileStart;
24101
- this.endBP = tileEnd;
24102
- this.bpPerPixel = bpPerPixel;
24103
- this.features = features;
24104
- this.roiFeatures = roiFeatures;
24105
- };
24054
+ class FeatureCache {
24106
24055
 
24107
- Tile.prototype.containsRange = function (chr, start, end, bpPerPixel) {
24108
- return this.bpPerPixel === bpPerPixel && start >= this.startBP && end <= this.endBP && chr === this.chr
24109
- };
24056
+ constructor(chr, tileStart, tileEnd, bpPerPixel, features, roiFeatures, multiresolution) {
24057
+ this.chr = chr;
24058
+ this.startBP = tileStart;
24059
+ this.endBP = tileEnd;
24060
+ this.bpPerPixel = bpPerPixel;
24061
+ this.features = features;
24062
+ this.roiFeatures = roiFeatures;
24063
+ this.multiresolution = multiresolution;
24064
+ }
24110
24065
 
24111
- Tile.prototype.overlapsRange = function (chr, start, end) {
24112
- return this.chr === chr && end >= this.startBP && start <= this.endBP
24113
- };
24066
+ containsRange(chr, start, end, bpPerPixel) {
24067
+
24068
+ // For multi-resolution tracks allow for a 2X change in bpPerPixel
24069
+ const r = this.multiresolution ? this.bpPerPixel / bpPerPixel : 1;
24070
+
24071
+ return start >= this.startBP && end <= this.endBP && chr === this.chr && r > 0.5 && r < 2
24072
+ }
24073
+
24074
+ overlapsRange(chr, start, end) {
24075
+ return this.chr === chr && end >= this.startBP && start <= this.endBP
24076
+ }
24077
+ }
24114
24078
 
24115
24079
 
24116
24080
  /**
@@ -24417,6 +24381,18 @@ class PairedAlignment {
24417
24381
  return true // By definition
24418
24382
  }
24419
24383
 
24384
+ isMateMapped() {
24385
+ return true // By definition
24386
+ }
24387
+
24388
+ isProperPair() {
24389
+ return this.firstAlignment.isProperPair()
24390
+ }
24391
+
24392
+ get tlen() {
24393
+ return Math.abs(this.firstAlignment.fragmentLength)
24394
+ }
24395
+
24420
24396
  firstOfPairStrand() {
24421
24397
 
24422
24398
  if (this.firstAlignment.isFirstOfPair()) {
@@ -24758,7 +24734,10 @@ function packAlignmentRows(alignments, start, end, showSoftClips) {
24758
24734
 
24759
24735
 
24760
24736
  class AlignmentContainer {
24761
- constructor(chr, start, end, samplingWindowSize, samplingDepth, pairsSupported, alleleFreqThreshold) {
24737
+
24738
+ // this.config.samplingWindowSize, this.config.samplingDepth,
24739
+ // this.config.pairsSupported, this.config.alleleFreqThreshold)
24740
+ constructor(chr, start, end, {samplingWindowSize, samplingDepth, pairsSupported, alleleFreqThreshold}) {
24762
24741
 
24763
24742
  this.chr = chr;
24764
24743
  this.start = Math.floor(start);
@@ -24786,17 +24765,12 @@ class AlignmentContainer {
24786
24765
  return alignment.isMapped() && !alignment.isFailsVendorQualityCheck()
24787
24766
  };
24788
24767
 
24789
- this.pairedEndStats = new PairedEndStats();
24790
24768
  }
24791
24769
 
24792
24770
  push(alignment) {
24793
24771
 
24794
24772
  if (this.filter(alignment) === false) return
24795
24773
 
24796
- if (alignment.isPaired()) {
24797
- this.pairedEndStats.push(alignment);
24798
- }
24799
-
24800
24774
  this.coverageMap.incCounts(alignment); // Count coverage before any downsampling
24801
24775
 
24802
24776
  if (this.pairsSupported && this.downsampledReads.has(alignment.readName)) {
@@ -24828,8 +24802,6 @@ class AlignmentContainer {
24828
24802
 
24829
24803
  this.pairsCache = undefined;
24830
24804
  this.downsampledReads = undefined;
24831
-
24832
- this.pairedEndStats.compute();
24833
24805
  }
24834
24806
 
24835
24807
  contains(chr, start, end) {
@@ -25140,69 +25112,6 @@ class DownsampledInterval {
25140
25112
  }
25141
25113
  }
25142
25114
 
25143
- class PairedEndStats {
25144
-
25145
- constructor(lowerPercentile, upperPercentile) {
25146
- this.totalCount = 0;
25147
- this.frCount = 0;
25148
- this.rfCount = 0;
25149
- this.ffCount = 0;
25150
- this.sumF = 0;
25151
- this.sumF2 = 0;
25152
- //this.lp = lowerPercentile === undefined ? 0.005 : lowerPercentile;
25153
- //this.up = upperPercentile === undefined ? 0.995 : upperPercentile;
25154
- //this.digest = new Digest();
25155
- }
25156
-
25157
- push(alignment) {
25158
-
25159
- if (alignment.isProperPair()) {
25160
-
25161
- var fragmentLength = Math.abs(alignment.fragmentLength);
25162
- //this.digest.push(fragmentLength);
25163
- this.sumF += fragmentLength;
25164
- this.sumF2 += fragmentLength * fragmentLength;
25165
-
25166
- var po = alignment.pairOrientation;
25167
-
25168
- if (typeof po === "string" && po.length === 4) {
25169
- var tmp = '' + po.charAt(0) + po.charAt(2);
25170
- switch (tmp) {
25171
- case 'FF':
25172
- case 'RR':
25173
- this.ffCount++;
25174
- break
25175
- case "FR":
25176
- this.frCount++;
25177
- break
25178
- case"RF":
25179
- this.rfCount++;
25180
- }
25181
- }
25182
- this.totalCount++;
25183
- }
25184
- }
25185
-
25186
- compute() {
25187
-
25188
- if (this.totalCount > 100) {
25189
- if (this.ffCount / this.totalCount > 0.9) this.orienation = "ff";
25190
- else if (this.frCount / this.totalCount > 0.9) this.orienation = "fr";
25191
- else if (this.rfCount / this.totalCount > 0.9) this.orienation = "rf";
25192
-
25193
-
25194
- var fMean = this.sumF / this.totalCount;
25195
- var stdDev = Math.sqrt((this.totalCount * this.sumF2 - this.sumF * this.sumF) / (this.totalCount * this.totalCount));
25196
- this.lowerFragmentLength = fMean - 3 * stdDev;
25197
- this.upperFragmentLength = fMean + 3 * stdDev;
25198
-
25199
- //this.lowerFragmentLength = this.digest.percentile(this.lp);
25200
- //this.upperFragmentLength = this.digest.percentile(this.up);
25201
- //this.digest = undefined;
25202
- }
25203
- }
25204
- }
25205
-
25206
25115
  class SupplementaryAlignment {
25207
25116
 
25208
25117
  constructor(rec) {
@@ -26392,7 +26301,7 @@ class BamReaderNonIndexed {
26392
26301
  const header = this.header;
26393
26302
  const queryChr = header.chrAliasTable.hasOwnProperty(chr) ? header.chrAliasTable[chr] : chr;
26394
26303
  const qAlignments = this.alignmentCache.queryFeatures(queryChr, bpStart, bpEnd);
26395
- const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.samplingWindowSize, this.samplingDepth, this.pairsSupported, this.alleleFreqThreshold);
26304
+ const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config);
26396
26305
  for (let a of qAlignments) {
26397
26306
  alignmentContainer.push(a);
26398
26307
  }
@@ -26419,13 +26328,13 @@ class BamReaderNonIndexed {
26419
26328
  const alignments = [];
26420
26329
  this.header = BamUtils.decodeBamHeader(data);
26421
26330
  BamUtils.decodeBamRecords(data, this.header.size, alignments, this.header.chrNames);
26422
- this.alignmentCache = new FeatureCache(alignments, this.genome);
26331
+ this.alignmentCache = new FeatureCache$1(alignments, this.genome);
26423
26332
  }
26424
26333
 
26425
26334
  fetchAlignments(chr, bpStart, bpEnd) {
26426
26335
  const queryChr = this.header.chrAliasTable.hasOwnProperty(chr) ? this.header.chrAliasTable[chr] : chr;
26427
26336
  const features = this.alignmentCache.queryFeatures(queryChr, bpStart, bpEnd);
26428
- const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.samplingWindowSize, this.samplingDepth, this.pairsSupported);
26337
+ const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config);
26429
26338
  for (let feature of features) {
26430
26339
  alignmentContainer.push(feature);
26431
26340
  }
@@ -27416,9 +27325,7 @@ class BamReader {
27416
27325
  const chrToIndex = await this.getChrIndex();
27417
27326
  const queryChr = this.chrAliasTable.hasOwnProperty(chr) ? this.chrAliasTable[chr] : chr;
27418
27327
  const chrId = chrToIndex[queryChr];
27419
- const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd,
27420
- this.config.samplingWindowSize, this.config.samplingDepth,
27421
- this.config.pairsSupported, this.config.alleleFreqThreshold);
27328
+ const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config);
27422
27329
 
27423
27330
  if (chrId === undefined) {
27424
27331
  return alignmentContainer
@@ -27550,7 +27457,7 @@ class ShardedBamReader {
27550
27457
  async readAlignments(chr, start, end) {
27551
27458
 
27552
27459
  if (!this.bamReaders.hasOwnProperty(chr)) {
27553
- return new AlignmentContainer(chr, start, end)
27460
+ return new AlignmentContainer(chr, start, end, this.config)
27554
27461
  } else {
27555
27462
 
27556
27463
  let reader = this.bamReaders[chr];
@@ -27641,7 +27548,7 @@ BamWebserviceReader.prototype.readAlignments = function (chr, bpStart, bpEnd) {
27641
27548
 
27642
27549
  header.chrToIndex[queryChr];
27643
27550
 
27644
- alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, self.samplingWindowSize, self.samplingDepth, self.pairsSupported, self.alleleFreqThreshold);
27551
+ alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, self.config);
27645
27552
 
27646
27553
  BamUtils.decodeSamRecords(sam, alignmentContainer, queryChr, bpStart, bpEnd, self.filter);
27647
27554
 
@@ -27895,7 +27802,7 @@ class HtsgetBamReader extends HtsgetReader {
27895
27802
  const ba = unbgzf(compressedData.buffer);
27896
27803
 
27897
27804
  const chrIdx = this.header.chrToIndex[chr];
27898
- const alignmentContainer = new AlignmentContainer(chr, start, end, this.samplingWindowSize, this.samplingDepth, this.pairsSupported, this.alleleFreqThreshold);
27805
+ const alignmentContainer = new AlignmentContainer(chr, start, end, this.config);
27899
27806
  BamUtils.decodeBamRecords(ba, this.header.size, alignmentContainer, this.header.chrNames, chrIdx, start, end);
27900
27807
  alignmentContainer.finish();
27901
27808
 
@@ -28061,8 +27968,7 @@ class CramReader {
28061
27968
  const header = await this.getHeader();
28062
27969
  const queryChr = header.chrAliasTable.hasOwnProperty(chr) ? header.chrAliasTable[chr] : chr;
28063
27970
  const chrIdx = header.chrToIndex[queryChr];
28064
- const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd,
28065
- this.samplingWindowSize, this.samplingDepth, this.pairsSupported, this.alleleFreqThreshold);
27971
+ const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config);
28066
27972
 
28067
27973
  if (chrIdx === undefined) {
28068
27974
  return alignmentContainer
@@ -28678,7 +28584,7 @@ Ga4ghAlignmentReader.prototype.readAlignments = function (chr, bpStart, bpEnd) {
28678
28584
  "pageSize": "10000"
28679
28585
  },
28680
28586
  decode: decodeGa4ghReads,
28681
- results: new AlignmentContainer(chr, bpStart, bpEnd, self.samplingWindowSize, self.samplingDepth, self.pairsSupported, self.alleleFreqThreshold)
28587
+ results: new AlignmentContainer(chr, bpStart, bpEnd, self.config)
28682
28588
  })
28683
28589
  })
28684
28590
 
@@ -29011,7 +28917,6 @@ class BamSource {
29011
28917
 
29012
28918
  this.config = config;
29013
28919
  this.genome = genome;
29014
- this.alignmentContainer = undefined;
29015
28920
 
29016
28921
  if (isDataURL(config.url)) {
29017
28922
  if ("cram" === config.format) {
@@ -29059,19 +28964,11 @@ class BamSource {
29059
28964
  }
29060
28965
 
29061
28966
  setViewAsPairs(bool) {
29062
-
29063
- if (this.viewAsPairs !== bool) {
29064
- this.viewAsPairs = bool;
29065
- // if (this.alignmentContainer) {
29066
- // this.alignmentContainer.setViewAsPairs(bool);
29067
- // }
29068
- }
28967
+ this.viewAsPairs = bool;
29069
28968
  }
29070
28969
 
29071
28970
  setShowSoftClips(bool) {
29072
- if (this.showSoftClips !== bool) {
29073
- this.showSoftClips = bool;
29074
- }
28971
+ this.showSoftClips = bool;
29075
28972
  }
29076
28973
 
29077
28974
  async getAlignments(chr, bpStart, bpEnd) {
@@ -29079,33 +28976,28 @@ class BamSource {
29079
28976
  const genome = this.genome;
29080
28977
  const showSoftClips = this.showSoftClips;
29081
28978
 
29082
- if (this.alignmentContainer && this.alignmentContainer.contains(chr, bpStart, bpEnd)) {
29083
- return this.alignmentContainer
28979
+ const alignmentContainer = await this.bamReader.readAlignments(chr, bpStart, bpEnd);
28980
+ let alignments = alignmentContainer.alignments;
28981
+ if (!this.viewAsPairs) {
28982
+ alignments = unpairAlignments([{alignments: alignments}]);
28983
+ }
28984
+ const hasAlignments = alignments.length > 0;
28985
+ alignmentContainer.packedAlignmentRows = packAlignmentRows(alignments, alignmentContainer.start, alignmentContainer.end, showSoftClips);
29084
28986
 
29085
- } else {
29086
- const alignmentContainer = await this.bamReader.readAlignments(chr, bpStart, bpEnd);
29087
- let alignments = alignmentContainer.alignments;
29088
- if (!this.viewAsPairs) {
29089
- alignments = unpairAlignments([{alignments: alignments}]);
29090
- }
29091
- const hasAlignments = alignments.length > 0;
29092
- alignmentContainer.packedAlignmentRows = packAlignmentRows(alignments, alignmentContainer.start, alignmentContainer.end, showSoftClips);
29093
- alignmentContainer.alignments = undefined; // Don't need to hold onto these anymore
29094
-
29095
- this.alignmentContainer = alignmentContainer;
29096
-
29097
- if (hasAlignments) {
29098
- const sequence = await genome.sequence.getSequence(chr, alignmentContainer.start, alignmentContainer.end);
29099
- if (sequence) {
29100
- alignmentContainer.coverageMap.refSeq = sequence; // TODO -- fix this
29101
- alignmentContainer.sequence = sequence; // TODO -- fix this
29102
- return alignmentContainer
29103
- } else {
29104
- console.error("No sequence for: " + chr + ":" + alignmentContainer.start + "-" + alignmentContainer.end);
29105
- }
28987
+ this.alignmentContainer = alignmentContainer;
28988
+
28989
+ if (hasAlignments) {
28990
+ const sequence = await genome.sequence.getSequence(chr, alignmentContainer.start, alignmentContainer.end);
28991
+ if (sequence) {
28992
+ alignmentContainer.coverageMap.refSeq = sequence; // TODO -- fix this
28993
+ alignmentContainer.sequence = sequence; // TODO -- fix this
28994
+ return alignmentContainer
28995
+ } else {
28996
+ console.error("No sequence for: " + chr + ":" + alignmentContainer.start + "-" + alignmentContainer.end);
29106
28997
  }
29107
- return alignmentContainer
29108
28998
  }
28999
+ return alignmentContainer
29000
+
29109
29001
  }
29110
29002
  }
29111
29003
 
@@ -29423,7 +29315,7 @@ class TrackBase {
29423
29315
 
29424
29316
  // We use the cached features rather than method to avoid async load. If the
29425
29317
  // feature is not already loaded this won't work, but the user wouldn't be mousing over it either.
29426
- if (!features) features = clickState.viewport.getCachedFeatures();
29318
+ if (!features) features = clickState.viewport.cachedFeatures;
29427
29319
 
29428
29320
  if (!features || features.length === 0) {
29429
29321
  return []
@@ -29927,8 +29819,7 @@ class Locus {
29927
29819
  }
29928
29820
 
29929
29821
  overlaps(locus) {
29930
- return locus.chr === this.chr
29931
- && !(locus.end < this.start || locus.start > this.end)
29822
+ return locus.chr === this.chr && !(locus.end < this.start || locus.start > this.end)
29932
29823
  }
29933
29824
 
29934
29825
  extend(l) {
@@ -31805,6 +31696,19 @@ function makeCircViewChromosomes(genome) {
31805
31696
  return regions
31806
31697
  }
31807
31698
 
31699
+ function sendChords(chords, track, refFrame, alpha) {
31700
+
31701
+ const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? track.color : getChrColor(refFrame.chr), alpha);
31702
+ const trackColor = IGVColor.addAlpha(track.color || 'rgb(0,0,255)', alpha);
31703
+
31704
+ // name the chord set to include locus and filtering information
31705
+ const encodedName = track.name.replaceAll(' ', '%20');
31706
+ const chordSetName = "all" === refFrame.chr ? encodedName :
31707
+ `${encodedName} ${refFrame.chr}:${refFrame.start}-${refFrame.end}`;
31708
+ track.browser.circularView.addChords(chords, {track: chordSetName, color: chordSetColor, trackColor: trackColor});
31709
+
31710
+ }
31711
+
31808
31712
 
31809
31713
  function createCircularView(el, browser) {
31810
31714
 
@@ -31814,42 +31718,102 @@ function createCircularView(el, browser) {
31814
31718
 
31815
31719
  const f1 = feature.data;
31816
31720
  const f2 = f1.mate;
31817
- const flanking = 2000;
31721
+ addFrameForFeature(f1);
31722
+ addFrameForFeature(f2);
31723
+
31724
+ function addFrameForFeature(feature) {
31725
+
31726
+ feature.chr = browser.genome.getChromosomeName(feature.refName);
31727
+
31728
+ for (let referenceFrame of browser.currentReferenceFrames()) {
31729
+ const l = Locus.fromLocusString(referenceFrame.getLocusString());
31730
+ if (l.contains(feature)) {
31731
+ break
31732
+ } else if (l.overlaps(feature)) {
31733
+ referenceFrame.extend(feature);
31734
+ break
31735
+ } else {
31736
+ const flanking = 2000;
31737
+ const center = (feature.start + feature.end) / 2;
31738
+ browser.addMultiLocusPanel(feature.chr, center - flanking, center + flanking);
31739
+ break
31740
+ }
31741
+ }
31742
+ }
31743
+ }
31744
+ });
31818
31745
 
31819
- const l1 = new Locus({chr: browser.genome.getChromosomeName(f1.refName), start: f1.start, end: f1.end});
31820
- const l2 = new Locus({chr: browser.genome.getChromosomeName(f2.refName), start: f2.start, end: f2.end});
31746
+ return circularView
31747
+ }
31821
31748
 
31822
- let loci;
31749
+ class PairedEndStats {
31823
31750
 
31824
- // If there is overlap with current loci
31751
+ constructor(alignments, {minTLENPercentile, maxTLENPercentile}) {
31752
+ this.totalCount = 0;
31753
+ this.frCount = 0;
31754
+ this.rfCount = 0;
31755
+ this.ffCount = 0;
31756
+ this.sumF = 0;
31757
+ this.sumF2 = 0;
31758
+ this.lp = minTLENPercentile === undefined ? 0.1 : minTLENPercentile;
31759
+ this.up = maxTLENPercentile === undefined ? 99.5 : maxTLENPercentile;
31760
+ this.isizes = [];
31761
+ this.compute(alignments);
31762
+ }
31825
31763
 
31826
- loci = browser.currentLoci().map(str => Locus.fromLocusString(str));
31764
+ compute(alignments) {
31827
31765
 
31828
- if (loci.some(locus => locus.contains(l1)) || loci.some(locus => locus.contains(l2))) {
31829
- for (let l of [l1, l2]) {
31830
- if (!loci.some(locus => {
31831
- return locus.contains(l)
31832
- })) {
31833
- // add flanking
31834
- l.start = Math.max(0, l.start - flanking);
31835
- l.end += flanking;
31836
- loci.push(l);
31766
+ for (let alignment of alignments) {
31767
+ if (alignment.isProperPair()) {
31768
+ var tlen = Math.abs(alignment.fragmentLength);
31769
+ this.sumF += tlen;
31770
+ this.sumF2 += tlen * tlen;
31771
+ this.isizes.push(tlen);
31772
+
31773
+ var po = alignment.pairOrientation;
31774
+
31775
+ if (typeof po === "string" && po.length === 4) {
31776
+ var tmp = '' + po.charAt(0) + po.charAt(2);
31777
+ switch (tmp) {
31778
+ case 'FF':
31779
+ case 'RR':
31780
+ this.ffCount++;
31781
+ break
31782
+ case "FR":
31783
+ this.frCount++;
31784
+ break
31785
+ case"RF":
31786
+ this.rfCount++;
31837
31787
  }
31838
31788
  }
31839
- } else {
31840
- l1.start = Math.max(0, l1.start - flanking);
31841
- l1.end += flanking;
31842
- l2.start = Math.max(0, l2.start - flanking);
31843
- l2.end += flanking;
31844
- loci = [l1, l2];
31789
+ this.totalCount++;
31845
31790
  }
31846
-
31847
- const searchString = loci.map(l => l.getLocusString()).join(" ");
31848
- browser.search(searchString);
31849
31791
  }
31792
+
31793
+ if (this.ffCount / this.totalCount > 0.9) this.orienation = "ff";
31794
+ else if (this.frCount / this.totalCount > 0.9) this.orienation = "fr";
31795
+ else if (this.rfCount / this.totalCount > 0.9) this.orienation = "rf";
31796
+
31797
+ this.minTLEN = this.lp === 0 ? 0 : percentile(this.isizes, this.lp);
31798
+ this.maxTLEN = percentile(this.isizes, this.up);
31799
+
31800
+ // var fMean = this.sumF / this.totalCount
31801
+ // var stdDev = Math.sqrt((this.totalCount * this.sumF2 - this.sumF * this.sumF) / (this.totalCount * this.totalCount))
31802
+ // this.minTLEN = fMean - 3 * stdDev
31803
+ // this.maxTLEN = fMean + 3 * stdDev
31804
+
31805
+ }
31806
+ }
31807
+
31808
+ function percentile(array, p) {
31809
+
31810
+ if (array.length === 0) return undefined
31811
+ var k = Math.floor(array.length * (p / 100));
31812
+ array.sort(function (a, b) {
31813
+ return a - b
31850
31814
  });
31815
+ return array[k]
31851
31816
 
31852
- return circularView
31853
31817
  }
31854
31818
 
31855
31819
  /*
@@ -31916,8 +31880,6 @@ class BAMTrack extends TrackBase {
31916
31880
  this.showMismatches = false !== config.showMismatches;
31917
31881
  this.color = config.color;
31918
31882
  this.coverageColor = config.coverageColor;
31919
- this.minFragmentLength = config.minFragmentLength; // Optional, might be undefined
31920
- this.maxFragmentLength = config.maxFragmentLength || 1000;
31921
31883
 
31922
31884
  // The sort object can be an array in the case of multi-locus view, however if multiple sort positions
31923
31885
  // are present for a given reference frame the last one will take precedence
@@ -31945,12 +31907,24 @@ class BAMTrack extends TrackBase {
31945
31907
  return this._height
31946
31908
  }
31947
31909
 
31910
+ get minTemplateLength() {
31911
+ const configMinTLEN = this.config.minTLEN !== undefined ? this.config.minTLEN : this.config.minFragmentLength;
31912
+ return (configMinTLEN !== undefined) ? configMinTLEN :
31913
+ this._pairedEndStats ? this._pairedEndStats.minTLEN : 0
31914
+ }
31915
+
31916
+ get maxTemplateLength() {
31917
+ const configMaxTLEN = this.config.maxTLEN !== undefined ? this.config.maxTLEN : this.config.maxFragmentLength;
31918
+ return (configMaxTLEN !== undefined) ? configMaxTLEN :
31919
+ this._pairedEndStats ? this._pairedEndStats.maxTLEN : 1000
31920
+ }
31921
+
31948
31922
  sort(options) {
31949
31923
  options = this.assignSort(options);
31950
31924
 
31951
31925
  for (let vp of this.trackView.viewports) {
31952
31926
  if (vp.containsPosition(options.chr, options.position)) {
31953
- const alignmentContainer = vp.getCachedFeatures();
31927
+ const alignmentContainer = vp.cachedFeatures;
31954
31928
  if (alignmentContainer) {
31955
31929
  sortAlignmentRows(options, alignmentContainer);
31956
31930
  vp.repaint();
@@ -31985,14 +31959,13 @@ class BAMTrack extends TrackBase {
31985
31959
 
31986
31960
  const alignmentContainer = await this.featureSource.getAlignments(chr, bpStart, bpEnd);
31987
31961
 
31988
- if (alignmentContainer.alignments && alignmentContainer.alignments.length > 99) {
31989
- if (undefined === this.minFragmentLength) {
31990
- this.minFragmentLength = alignmentContainer.pairedEndStats.lowerFragmentLength;
31991
- }
31992
- if (undefined === this.maxFragmentLength) {
31993
- this.maxFragmentLength = alignmentContainer.pairedEndStats.upperFragmentLength;
31962
+ if (alignmentContainer.paired && !this._pairedEndStats && !this.config.maxFragmentLength) {
31963
+ const pairedEndStats = new PairedEndStats(alignmentContainer.alignments, this.config);
31964
+ if (pairedEndStats.totalCount > 99) {
31965
+ this._pairedEndStats = pairedEndStats;
31994
31966
  }
31995
31967
  }
31968
+ alignmentContainer.alignments = undefined; // Don't need to hold onto these anymore
31996
31969
 
31997
31970
  const sort = this.sortObject;
31998
31971
  if (sort) {
@@ -32089,7 +32062,7 @@ class BAMTrack extends TrackBase {
32089
32062
  if (this.alignmentTrack.hasPairs) {
32090
32063
  colorByMenuItems.push({key: 'firstOfPairStrand', label: 'first-of-pair strand'});
32091
32064
  colorByMenuItems.push({key: 'pairOrientation', label: 'pair orientation'});
32092
- colorByMenuItems.push({key: 'fragmentLength', label: 'insert size (TLEN)'});
32065
+ colorByMenuItems.push({key: 'tlen', label: 'insert size (TLEN)'});
32093
32066
  colorByMenuItems.push({key: 'unexpectedPair', label: 'pair orientation & insert size (TLEN)'});
32094
32067
  }
32095
32068
  const tagLabel = 'tag' + (this.alignmentTrack.colorByTag ? ' (' + this.alignmentTrack.colorByTag + ')' : '');
@@ -32195,7 +32168,7 @@ class BAMTrack extends TrackBase {
32195
32168
  });
32196
32169
  }
32197
32170
 
32198
- // Experimental JBrowse feature
32171
+ // Add chords to JBrowse circular view, if present
32199
32172
  if (this.browser.circularView && true === this.browser.circularViewVisible &&
32200
32173
  (this.alignmentTrack.hasPairs || this.alignmentTrack.hasSupplemental)) {
32201
32174
  menuItems.push('<hr/>');
@@ -32203,24 +32176,9 @@ class BAMTrack extends TrackBase {
32203
32176
  menuItems.push({
32204
32177
  label: 'Add discordant pairs to circular view',
32205
32178
  click: () => {
32206
- const maxFragmentLength = this.maxFragmentLength;
32207
- const inView = [];
32208
32179
  for (let viewport of this.trackView.viewports) {
32209
- for (let a of viewport.getCachedFeatures().allAlignments()) {
32210
- const referenceFrame = viewport.referenceFrame;
32211
- if (a.end >= referenceFrame.start
32212
- && a.start <= referenceFrame.end
32213
- && a.mate
32214
- && a.mate.chr
32215
- && (a.mate.chr !== a.chr || Math.max(a.fragmentLength) > maxFragmentLength)) {
32216
- inView.push(a);
32217
- }
32218
- }
32180
+ this.addPairedChordsForViewport(viewport);
32219
32181
  }
32220
- this.browser.circularViewVisible = true;
32221
- const chords = makePairedAlignmentChords(inView);
32222
- const color = IGVColor.addAlpha(this.color || 'rgb(0,0,255)', 0.02);
32223
- this.browser.circularView.addChords(chords, {track: this.name, color: color});
32224
32182
  }
32225
32183
  });
32226
32184
  }
@@ -32228,19 +32186,9 @@ class BAMTrack extends TrackBase {
32228
32186
  menuItems.push({
32229
32187
  label: 'Add split reads to circular view',
32230
32188
  click: () => {
32231
- const inView = [];
32232
32189
  for (let viewport of this.trackView.viewports) {
32233
- for (let a of viewport.getCachedFeatures().allAlignments()) {
32234
- const referenceFrame = viewport.referenceFrame;
32235
- const sa = a.hasTag('SA');
32236
- if (a.end >= referenceFrame.start && a.start <= referenceFrame.end && sa) {
32237
- inView.push(a);
32238
- }
32239
- }
32190
+ this.addSplitChordsForViewport(viewport);
32240
32191
  }
32241
- const chords = makeSupplementalAlignmentChords(inView);
32242
- const color = IGVColor.addAlpha(this.color || 'rgb(0,0,255)', 0.1);
32243
- this.browser.circularView.addChords(chords, {track: this.name, color: color});
32244
32192
  }
32245
32193
  });
32246
32194
  }
@@ -32352,7 +32300,7 @@ class BAMTrack extends TrackBase {
32352
32300
  }
32353
32301
 
32354
32302
  getCachedAlignmentContainers() {
32355
- return this.trackView.viewports.map(vp => vp.getCachedFeatures())
32303
+ return this.trackView.viewports.map(vp => vp.cachedFeatures)
32356
32304
  }
32357
32305
 
32358
32306
  get dataRange() {
@@ -32378,6 +32326,62 @@ class BAMTrack extends TrackBase {
32378
32326
  set autoscale(autoscale) {
32379
32327
  this.coverageTrack.autoscale = autoscale;
32380
32328
  }
32329
+
32330
+ /**
32331
+ * Add chords to the circular view for the given viewport, represented by its reference frame
32332
+ * @param refFrame
32333
+ */
32334
+ addPairedChordsForViewport(viewport) {
32335
+
32336
+ const maxTemplateLength = this.maxTemplateLength;
32337
+ const inView = [];
32338
+ const refFrame = viewport.referenceFrame;
32339
+ for (let a of viewport.cachedFeatures.allAlignments()) {
32340
+ if (a.end >= refFrame.start
32341
+ && a.start <= refFrame.end
32342
+ && a.mate
32343
+ && a.mate.chr
32344
+ && (a.mate.chr !== a.chr || Math.max(a.fragmentLength) > maxTemplateLength)) {
32345
+ inView.push(a);
32346
+ }
32347
+ }
32348
+ const chords = makePairedAlignmentChords(inView);
32349
+ sendChords(chords, this, refFrame, 0.02);
32350
+
32351
+ // const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? this.color : getChrColor(refFrame.chr), 0.02)
32352
+ // const trackColor = IGVColor.addAlpha(this.color || 'rgb(0,0,255)', 0.02)
32353
+ //
32354
+ // // name the chord set to include track name and locus
32355
+ // const encodedName = this.name.replaceAll(' ', '%20')
32356
+ // const chordSetName = "all" === refFrame.chr ? encodedName :
32357
+ // `${encodedName} (${refFrame.chr}:${refFrame.start}-${refFrame.end}`
32358
+ // this.browser.circularView.addChords(chords, {name: chordSetName, color: chordSetColor, trackColor: trackColor})
32359
+ }
32360
+
32361
+ addSplitChordsForViewport(viewport) {
32362
+
32363
+ const inView = [];
32364
+ const refFrame = viewport.referenceFrame;
32365
+ for (let a of viewport.cachedFeatures.allAlignments()) {
32366
+
32367
+ const sa = a.hasTag('SA');
32368
+ if (a.end >= refFrame.start && a.start <= refFrame.end && sa) {
32369
+ inView.push(a);
32370
+ }
32371
+ }
32372
+
32373
+ const chords = makeSupplementalAlignmentChords(inView);
32374
+ sendChords(chords, this, refFrame, 0.02);
32375
+
32376
+ // const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? this.color : getChrColor(refFrame.chr), 0.02)
32377
+ // const trackColor = IGVColor.addAlpha(this.color || 'rgb(0,0,255)', 0.02)
32378
+ //
32379
+ // // name the chord set to include track name and locus
32380
+ // const encodedName = this.name.replaceAll(' ', '%20')
32381
+ // const chordSetName = "all" === refFrame.chr ? encodedName :
32382
+ // `${encodedName} (${refFrame.chr}:${refFrame.start}-${refFrame.end}`
32383
+ // this.browser.circularView.addChords(chords, {name: chordSetName, color: chordSetColor, trackColor: trackColor})
32384
+ }
32381
32385
  }
32382
32386
 
32383
32387
 
@@ -32498,7 +32502,7 @@ class CoverageTrack {
32498
32502
 
32499
32503
  getClickedObject(clickState) {
32500
32504
 
32501
- let features = clickState.viewport.getCachedFeatures();
32505
+ let features = clickState.viewport.cachedFeatures;
32502
32506
  if (!features || features.length === 0) return
32503
32507
 
32504
32508
  const genomicLocation = Math.floor(clickState.genomicLocation);
@@ -32574,8 +32578,8 @@ class AlignmentTrack {
32574
32578
  this.skippedColor = config.skippedColor || "rgb(150, 170, 170)";
32575
32579
  this.pairConnectorColor = config.pairConnectorColor;
32576
32580
 
32577
- this.smallFragmentLengthColor = config.smallFragmentLengthColor || "rgb(0, 0, 150)";
32578
- this.largeFragmentLengthColor = config.largeFragmentLengthColor || "rgb(200, 0, 0)";
32581
+ this.smallTLENColor = config.smallTLENColor || config.smallFragmentLengthColor || "rgb(0, 0, 150)";
32582
+ this.largeTLENColor = config.largeTLENColor || config.largeFragmentLengthColor || "rgb(200, 0, 0)";
32579
32583
 
32580
32584
  this.pairOrientation = config.pairOrienation || 'fr';
32581
32585
  this.pairColors = {};
@@ -32583,7 +32587,7 @@ class AlignmentTrack {
32583
32587
  this.pairColors["RR"] = config.rrColor || "rgb(20, 50, 200)";
32584
32588
  this.pairColors["LL"] = config.llColor || "rgb(0, 150, 150)";
32585
32589
 
32586
- this.colorBy = config.colorBy || "pairOrientation";
32590
+ this.colorBy = config.colorBy || "unexpectedPair";
32587
32591
  this.colorByTag = config.colorByTag ? config.colorByTag.toUpperCase() : undefined;
32588
32592
  this.bamColorTag = config.bamColorTag === undefined ? "YC" : config.bamColorTag;
32589
32593
 
@@ -32975,7 +32979,7 @@ class AlignmentTrack {
32975
32979
  direction: direction
32976
32980
  };
32977
32981
  this.parent.sortObject = newSortObject;
32978
- sortAlignmentRows(newSortObject, viewport.getCachedFeatures());
32982
+ sortAlignmentRows(newSortObject, viewport.cachedFeatures);
32979
32983
  viewport.repaint();
32980
32984
  };
32981
32985
  list.push('<b>Sort by...</b>');
@@ -33005,7 +33009,7 @@ class AlignmentTrack {
33005
33009
  };
33006
33010
  this.sortByTag = tag;
33007
33011
  this.parent.sortObject = newSortObject;
33008
- sortAlignmentRows(newSortObject, viewport.getCachedFeatures());
33012
+ sortAlignmentRows(newSortObject, viewport.cachedFeatures);
33009
33013
  viewport.repaint();
33010
33014
  }
33011
33015
  }
@@ -33032,7 +33036,11 @@ class AlignmentTrack {
33032
33036
  const referenceFrame = clickState.viewport.referenceFrame;
33033
33037
  if (this.browser.genome.getChromosome(clickedAlignment.mate.chr)) {
33034
33038
  this.highlightedAlignmentReadNamed = clickedAlignment.readName;
33035
- this.browser.presentMultiLocusPanel(clickedAlignment, referenceFrame);
33039
+ //this.browser.presentMultiLocusPanel(clickedAlignment, referenceFrame)
33040
+ const bpWidth = referenceFrame.end - referenceFrame.start;
33041
+ const frameStart = clickedAlignment.mate.position - bpWidth / 2;
33042
+ const frameEnd = clickedAlignment.mate.position + bpWidth / 2;
33043
+ this.browser.addMultiLocusPanel(clickedAlignment.mate.chr, frameStart, frameEnd, referenceFrame);
33036
33044
  } else {
33037
33045
  Alert.presentAlert(`Reference does not contain chromosome: ${clickedAlignment.mate.chr}`);
33038
33046
  }
@@ -33080,19 +33088,7 @@ class AlignmentTrack {
33080
33088
  list.push({
33081
33089
  label: 'Add discordant pairs to circular view',
33082
33090
  click: () => {
33083
- const maxFragmentLength = this.parent.maxFragmentLength;
33084
- const {referenceFrame} = viewport;
33085
- const inView = viewport.getCachedFeatures().allAlignments().filter(a => {
33086
- return a.end >= referenceFrame.start
33087
- && a.start <= referenceFrame.end
33088
- && a.mate
33089
- && a.mate.chr
33090
- && (a.mate.chr !== a.chr || Math.max(a.fragmentLength) > maxFragmentLength)
33091
- });
33092
- this.browser.circularViewVisible = true;
33093
- const chords = makePairedAlignmentChords(inView);
33094
- const color = IGVColor.addAlpha(this.parent.color || 'rgb(0,0,255)', 0.02);
33095
- this.browser.circularView.addChords(chords, {track: this.parent.name, color: color});
33091
+ this.parent.addPairedChordsForViewport(viewport);
33096
33092
  }
33097
33093
  });
33098
33094
  }
@@ -33100,17 +33096,7 @@ class AlignmentTrack {
33100
33096
  list.push({
33101
33097
  label: 'Add split reads to circular view',
33102
33098
  click: () => {
33103
- const inView = [];
33104
- for (let a of viewport.getCachedFeatures().allAlignments()) {
33105
- const referenceFrame = viewport.referenceFrame;
33106
- const sa = a.hasTag('SA');
33107
- if (a.end >= referenceFrame.start && a.start <= referenceFrame.end && sa) {
33108
- inView.push(a);
33109
- }
33110
- }
33111
- const chords = makeSupplementalAlignmentChords(inView);
33112
- const color = IGVColor.addAlpha(this.color || 'rgb(0,0,255)', 0.1);
33113
- this.browser.circularView.addChords(chords, {track: this.name, color: color});
33099
+ this.parent.addSplitChordsForViewport(viewport);
33114
33100
  }
33115
33101
  });
33116
33102
  }
@@ -33129,7 +33115,7 @@ class AlignmentTrack {
33129
33115
 
33130
33116
  const showSoftClips = this.parent.showSoftClips;
33131
33117
 
33132
- let features = viewport.getCachedFeatures();
33118
+ let features = viewport.cachedFeatures;
33133
33119
  if (!features || features.length === 0) return
33134
33120
 
33135
33121
  let packedAlignmentRows = features.packedAlignmentRows;
@@ -33220,14 +33206,17 @@ class AlignmentTrack {
33220
33206
  break
33221
33207
  }
33222
33208
 
33209
+ case "tlen":
33223
33210
  case "fragmentLength":
33224
33211
 
33225
- if (alignment.mate && alignment.isMateMapped() && alignment.mate.chr !== alignment.chr) {
33226
- color = getChrColor(alignment.mate.chr);
33227
- } else if (this.parent.minFragmentLength && Math.abs(alignment.fragmentLength) < this.parent.minFragmentLength) {
33228
- color = this.smallFragmentLengthColor;
33229
- } else if (this.parent.maxFragmentLength && Math.abs(alignment.fragmentLength) > this.parent.maxFragmentLength) {
33230
- color = this.largeFragmentLengthColor;
33212
+ if (alignment.mate && alignment.isMateMapped()) {
33213
+ if (alignment.mate.chr !== alignment.chr) {
33214
+ color = getChrColor(alignment.mate.chr);
33215
+ } else if (this.parent.minTemplateLength && Math.abs(alignment.fragmentLength) < this.parent.minTemplateLength) {
33216
+ color = this.smallTLENColor;
33217
+ } else if (this.parent.maxTemplateLength && Math.abs(alignment.fragmentLength) > this.parent.maxTemplateLength) {
33218
+ color = this.largeTLENColor;
33219
+ }
33231
33220
  }
33232
33221
  break
33233
33222
 
@@ -33269,11 +33258,6 @@ function sortAlignmentRows(options, alignmentContainer) {
33269
33258
  return true === direction ? i : -i
33270
33259
  });
33271
33260
 
33272
- // For debugging
33273
- // for(let r of alignmentContainer.packedAlignmentRows) {
33274
- // console.log(r.score);
33275
- // }
33276
-
33277
33261
  }
33278
33262
 
33279
33263
  function shadedBaseColor(qual, baseColor) {
@@ -33433,7 +33417,7 @@ class RulerViewport extends TrackViewport {
33433
33417
 
33434
33418
  this.$rulerLabel.click(async () => {
33435
33419
 
33436
- await this.browser.selectMultiLocusPanel(this.referenceFrame);
33420
+ await this.browser.gotoMultilocusPanel(this.referenceFrame);
33437
33421
 
33438
33422
  // const removals = this.browser.referenceFrameList.filter(r => this.referenceFrame !== r)
33439
33423
  // for (let referenceFrame of removals) {
@@ -33771,7 +33755,7 @@ class IdeogramViewport extends TrackViewport {
33771
33755
  referenceFrame.end = ee;
33772
33756
  referenceFrame.bpPerPixel = (ee - ss) / width;
33773
33757
 
33774
- this.browser.updateViews(referenceFrame, this.browser.trackViews, true);
33758
+ this.browser.updateViews(true);
33775
33759
 
33776
33760
  }
33777
33761
 
@@ -34369,15 +34353,11 @@ const colorPickerExclusionTypes = new Set(['ruler', 'sequence', 'ideogram']);
34369
34353
  class TrackView {
34370
34354
 
34371
34355
  constructor(browser, columnContainer, track) {
34372
-
34373
34356
  this.namespace = `trackview-${guid$2()}`;
34374
-
34375
34357
  this.browser = browser;
34376
34358
  this.track = track;
34377
34359
  track.trackView = this;
34378
-
34379
34360
  this.addDOMToColumnContainer(browser, columnContainer, browser.referenceFrameList);
34380
-
34381
34361
  }
34382
34362
 
34383
34363
  /**
@@ -34540,19 +34520,14 @@ class TrackView {
34540
34520
  if (false === colorPickerExclusionTypes.has(this.track.type)) {
34541
34521
 
34542
34522
  const trackColors = [];
34543
-
34544
34523
  const color = this.track.color || this.track.defaultColor;
34545
-
34546
34524
  if (isString$3(color)) {
34547
34525
  trackColors.push(color);
34548
34526
  }
34549
-
34550
34527
  if (this.track.altColor && isString$3(this.track.altColor)) {
34551
34528
  trackColors.push(this.track.altColor);
34552
34529
  }
34553
-
34554
34530
  const defaultColors = trackColors.map(c => c.startsWith("#") ? c : c.startsWith("rgb(") ? IGVColor.rgbToHex(c) : IGVColor.colorNameToHex(c));
34555
-
34556
34531
  const colorHandlers =
34557
34532
  {
34558
34533
  color: color => {
@@ -34565,7 +34540,6 @@ class TrackView {
34565
34540
  }
34566
34541
 
34567
34542
  };
34568
-
34569
34543
  this.browser.genericColorPicker.configure(defaultColors, colorHandlers);
34570
34544
  this.browser.genericColorPicker.setActiveColorHandler(key);
34571
34545
  this.browser.genericColorPicker.show();
@@ -34579,7 +34553,6 @@ class TrackView {
34579
34553
  if (this.track.minHeight) {
34580
34554
  newHeight = Math.max(this.track.minHeight, newHeight);
34581
34555
  }
34582
-
34583
34556
  if (this.track.maxHeight) {
34584
34557
  newHeight = Math.min(this.track.maxHeight, newHeight);
34585
34558
  }
@@ -34674,7 +34647,9 @@ class TrackView {
34674
34647
  repaintViews() {
34675
34648
 
34676
34649
  for (let viewport of this.viewports) {
34677
- viewport.repaint();
34650
+ if(viewport.isVisible()) {
34651
+ viewport.repaint();
34652
+ }
34678
34653
  }
34679
34654
 
34680
34655
  if (typeof this.track.paintAxis === 'function') {
@@ -34701,6 +34676,9 @@ class TrackView {
34701
34676
 
34702
34677
  /**
34703
34678
  * Update viewports to reflect current genomic state, possibly loading additional data.
34679
+ *
34680
+ * @param force - if true, force a repaint even if no new data is loaded
34681
+ * @returns {Promise<void>}
34704
34682
  */
34705
34683
  async updateViews(force) {
34706
34684
 
@@ -34718,24 +34696,24 @@ class TrackView {
34718
34696
  }
34719
34697
 
34720
34698
  // rpv: viewports whose image (canvas) does not fully cover current genomic range
34721
- const reloadableViewports = this.viewportsToReload(force);
34699
+ const reloadableViewports = force ? visibleViewports : this.viewportsToReload();
34722
34700
 
34723
34701
  // Trigger viewport to load features needed to cover current genomic range
34724
34702
  // NOTE: these must be loaded synchronously, do not user Promise.all, not all file readers are thread safe
34725
34703
  for (let viewport of reloadableViewports) {
34726
34704
  await viewport.loadFeatures();
34727
34705
  }
34728
-
34706
+
34729
34707
  if (this.disposed) return // Track was removed during load
34730
34708
 
34731
- // Very special case for variant tracks in multilocus view. The # of rows to allocate to the variant (site)
34709
+ // Special case for variant tracks in multilocus view. The # of rows to allocate to the variant (site)
34732
34710
  // section depends on data from all the views. We only need to adjust this however if any data was loaded
34733
34711
  // (i.e. reloadableViewports.length > 0)
34734
34712
  if (this.track && typeof this.track.variantRowCount === 'function' && reloadableViewports.length > 0) {
34735
34713
  let maxRow = 0;
34736
34714
  for (let viewport of this.viewports) {
34737
- if (viewport.tile && viewport.tile.features) {
34738
- maxRow = Math.max(maxRow, viewport.tile.features.reduce((a, f) => Math.max(a, f.row || 0), 0));
34715
+ if (viewport.featureCache && viewport.featureCache.features) {
34716
+ maxRow = Math.max(maxRow, viewport.featureCache.features.reduce((a, f) => Math.max(a, f.row || 0), 0));
34739
34717
  }
34740
34718
  }
34741
34719
  const current = this.track.nVariantRows;
@@ -34747,19 +34725,18 @@ class TrackView {
34747
34725
  }
34748
34726
  }
34749
34727
 
34750
-
34751
34728
  if (this.track.autoscale) {
34752
34729
  let allFeatures = [];
34753
34730
  for (let visibleViewport of visibleViewports) {
34754
34731
  const referenceFrame = visibleViewport.referenceFrame;
34755
34732
  const start = referenceFrame.start;
34756
34733
  const end = start + referenceFrame.toBP($$1(visibleViewport.contentDiv).width());
34757
- if (visibleViewport.tile && visibleViewport.tile.features) {
34758
- if (typeof visibleViewport.tile.features.getMax === 'function') {
34759
- const max = visibleViewport.tile.features.getMax(start, end);
34734
+ if (visibleViewport.featureCache && visibleViewport.featureCache.features) {
34735
+ if (typeof visibleViewport.featureCache.features.getMax === 'function') {
34736
+ const max = visibleViewport.featureCache.features.getMax(start, end);
34760
34737
  allFeatures.push({value: max});
34761
34738
  } else {
34762
- allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(visibleViewport.tile.features, start, end));
34739
+ allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(visibleViewport.featureCache.features, start, end));
34763
34740
  }
34764
34741
  }
34765
34742
  }
@@ -34770,14 +34747,18 @@ class TrackView {
34770
34747
  }
34771
34748
  }
34772
34749
 
34773
- // Must repaint all viewports if autoscaling
34774
- if (!isDragging && (this.track.autoscale || this.track.autoscaleGroup)) {
34775
- for (let visibleViewport of visibleViewports) {
34776
- visibleViewport.repaint();
34750
+ // Must repaint all viewports if autoscaling. Always repaint ruler.
34751
+ if (this.track.autoscale || this.track.autoscaleGroup || this.track.type === 'ruler' || force) {
34752
+ for (let vp of visibleViewports) {
34753
+ vp.repaint();
34777
34754
  }
34778
34755
  } else {
34779
- for (let vp of reloadableViewports) {
34780
- vp.repaint();
34756
+ const reloadedViewports = new Set(reloadableViewports);
34757
+ for (let vp of visibleViewports) {
34758
+ const invalid = vp.canvas && vp.canvas._data && vp.canvas._data.invalidate;
34759
+ if (invalid || reloadedViewports.has(vp)) {
34760
+ vp.repaint();
34761
+ }
34781
34762
  }
34782
34763
  }
34783
34764
 
@@ -34808,14 +34789,14 @@ class TrackView {
34808
34789
  /**
34809
34790
  * Return a promise to get all in-view features. Used for group autoscaling.
34810
34791
  */
34811
- async getInViewFeatures(force) {
34792
+ async getInViewFeatures() {
34812
34793
 
34813
34794
  if (!(this.browser && this.browser.referenceFrameList)) {
34814
34795
  return []
34815
34796
  }
34816
34797
 
34817
34798
  // List of viewports that need reloading
34818
- const rpV = this.viewportsToReload(force);
34799
+ const rpV = this.viewportsToReload();
34819
34800
  const promises = rpV.map(function (vp) {
34820
34801
  return vp.loadFeatures()
34821
34802
  });
@@ -34824,16 +34805,16 @@ class TrackView {
34824
34805
 
34825
34806
  let allFeatures = [];
34826
34807
  for (let vp of this.viewports) {
34827
- if (vp.tile && vp.tile.features) {
34808
+ if (vp.featureCache && vp.featureCache.features) {
34828
34809
  const referenceFrame = vp.referenceFrame;
34829
34810
  const start = referenceFrame.start;
34830
34811
  const end = start + referenceFrame.toBP($$1(vp.contentDiv).width());
34831
34812
 
34832
- if (typeof vp.tile.features.getMax === 'function') {
34833
- const max = vp.tile.features.getMax(start, end);
34813
+ if (typeof vp.featureCache.features.getMax === 'function') {
34814
+ const max = vp.featureCache.features.getMax(start, end);
34834
34815
  allFeatures.push({value: max});
34835
34816
  } else {
34836
- allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(vp.tile.features, start, end));
34817
+ allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(vp.featureCache.features, start, end));
34837
34818
  }
34838
34819
  }
34839
34820
  }
@@ -34889,7 +34870,7 @@ class TrackView {
34889
34870
  const start = referenceFrame.start;
34890
34871
  const end = start + referenceFrame.toBP($$1(viewport.contentDiv).width());
34891
34872
  const bpPerPixel = referenceFrame.bpPerPixel;
34892
- return force || (!viewport.tile || viewport.tile.invalidate || !viewport.tile.containsRange(chr, start, end, bpPerPixel))
34873
+ return (!viewport.featureCache || !viewport.featureCache.containsRange(chr, start, end, bpPerPixel))
34893
34874
  }
34894
34875
  });
34895
34876
  return viewports
@@ -39662,7 +39643,7 @@ class TextFeatureSource {
39662
39643
  this.sourceType = (config.sourceType === undefined ? "file" : config.sourceType);
39663
39644
  this.maxWGCount = config.maxWGCount || DEFAULT_MAX_WG_COUNT;
39664
39645
 
39665
- const queryableFormats = new Set(["bigwig", "bw", "bigbed", "bb", "tdf"]);
39646
+ const queryableFormats = new Set(["bigwig", "bw", "bigbed", "bb", "biginteract", "tdf"]);
39666
39647
 
39667
39648
  if (config.features && Array.isArray(config.features)) {
39668
39649
  // Explicit array of features
@@ -39672,7 +39653,7 @@ class TextFeatureSource {
39672
39653
  mapProperties(features, config.mappings);
39673
39654
  }
39674
39655
  this.queryable = false;
39675
- this.featureCache = new FeatureCache(features, genome);
39656
+ this.featureCache = new FeatureCache$1(features, genome);
39676
39657
  } else if (config.reader) {
39677
39658
  // Explicit reader implementation
39678
39659
  this.reader = config.reader;
@@ -39839,14 +39820,14 @@ class TextFeatureSource {
39839
39820
  }
39840
39821
 
39841
39822
  // Note - replacing previous cache with new one. genomicInterval is optional (might be undefined => includes all features)
39842
- this.featureCache = new FeatureCache(features, this.genome, genomicInterval);
39823
+ this.featureCache = new FeatureCache$1(features, this.genome, genomicInterval);
39843
39824
 
39844
39825
  // If track is marked "searchable"< cache features by name -- use this with caution, memory intensive
39845
39826
  if (this.config.searchable || this.config.searchableFields) {
39846
39827
  this.addFeaturesToDB(features);
39847
39828
  }
39848
39829
  } else {
39849
- this.featureCache = new FeatureCache([], genomicInterval); // Empty cache
39830
+ this.featureCache = new FeatureCache$1([], genomicInterval); // Empty cache
39850
39831
  }
39851
39832
  }
39852
39833
 
@@ -40084,9 +40065,9 @@ class BufferedReader {
40084
40065
 
40085
40066
  //table chromatinInteract
40086
40067
 
40087
- function getDecoder(definedFieldCount, fieldCount, autoSql) {
40068
+ function getDecoder(definedFieldCount, fieldCount, autoSql, format) {
40088
40069
 
40089
- if (autoSql && 'chromatinInteract' === autoSql.table) {
40070
+ if (autoSql && 'chromatinInteract' === autoSql.table || "biginteract" === format) {
40090
40071
  return decodeInteract
40091
40072
  } else {
40092
40073
  const standardFieldCount = definedFieldCount - 3;
@@ -40233,6 +40214,7 @@ class BWReader {
40233
40214
 
40234
40215
  constructor(config, genome) {
40235
40216
  this.path = config.url;
40217
+ this.format = config.format || "bigwig";
40236
40218
  this.genome = genome;
40237
40219
  this.rpTreeCache = {};
40238
40220
  this.config = config;
@@ -40833,7 +40815,7 @@ function decodeWigData(data, chrIdx1, bpStart, chrIdx2, bpEnd, featureArray, chr
40833
40815
  function getBedDataDecoder() {
40834
40816
 
40835
40817
  const minSize = 3 * 4 + 1; // Minimum # of bytes required for a bed record
40836
- const decoder = getDecoder(this.header.definedFieldCount, this.header.fieldCount, this.autoSql);
40818
+ const decoder = getDecoder(this.header.definedFieldCount, this.header.fieldCount, this.autoSql, this.format);
40837
40819
  return function (data, chrIdx1, bpStart, chrIdx2, bpEnd, featureArray, chrDict) {
40838
40820
  const binaryParser = new BinaryParser(data);
40839
40821
  while (binaryParser.remLength() >= minSize) {
@@ -41701,7 +41683,7 @@ function zoomLevelForScale(chr, bpPerPixel, genome) {
41701
41683
  function FeatureSource(config, genome) {
41702
41684
 
41703
41685
  const format = config.format ? config.format.toLowerCase() : undefined;
41704
- if ('bigwig' === format || 'bigbed' === format || 'bb' === format) {
41686
+ if ('bigwig' === format || 'bigbed' === format || 'bb' === format || "biginteract" === format) {
41705
41687
  return new BWSource(config, genome)
41706
41688
  } else if ("tdf" === format) {
41707
41689
  return new TDFSource(config, genome)
@@ -42648,13 +42630,11 @@ class WigTrack extends TrackBase {
42648
42630
  this.paintAxis = paintAxis;
42649
42631
 
42650
42632
  const format = config.format ? config.format.toLowerCase() : config.format;
42633
+ this.flipAxis = config.flipAxis ? config.flipAxis : false;
42634
+ this.logScale = config.logScale ? config.logScale : false;
42651
42635
  if ("bigwig" === format) {
42652
- this.flipAxis = config.flipAxis ? config.flipAxis : false;
42653
- this.logScale = config.logScale ? config.logScale : false;
42654
42636
  this.featureSource = new BWSource(config, this.browser.genome);
42655
42637
  } else if ("tdf" === format) {
42656
- this.flipAxis = config.flipAxis ? config.flipAxis : false;
42657
- this.logScale = config.logScale ? config.logScale : false;
42658
42638
  this.featureSource = new TDFSource(config, this.browser.genome);
42659
42639
  } else {
42660
42640
  this.featureSource = FeatureSource(config, this.browser.genome);
@@ -42706,7 +42686,7 @@ class WigTrack extends TrackBase {
42706
42686
  let items = [];
42707
42687
  if (this.flipAxis !== undefined) {
42708
42688
  items.push({
42709
- label:"Flip y-axis",
42689
+ label: "Flip y-axis",
42710
42690
  click: () => {
42711
42691
  this.flipAxis = !this.flipAxis;
42712
42692
  this.trackView.repaintViews();
@@ -43434,7 +43414,7 @@ class SegTrack extends TrackBase {
43434
43414
 
43435
43415
  const sortHandler = (sort) => {
43436
43416
  const viewport = clickState.viewport;
43437
- const features = viewport.getCachedFeatures();
43417
+ const features = viewport.cachedFeatures;
43438
43418
  this.sortSamples(sort.chr, sort.start, sort.end, sort.direction, features);
43439
43419
  };
43440
43420
 
@@ -43552,10 +43532,16 @@ const MUT_COLORS = {
43552
43532
  * THE SOFTWARE.
43553
43533
  */
43554
43534
 
43535
+ /**
43536
+ * Represents 2 or more wig tracks overlaid on a common viewport.
43537
+ */
43555
43538
  class MergedTrack extends TrackBase {
43556
43539
 
43557
43540
  constructor(config, browser) {
43558
43541
  super(config, browser);
43542
+ this.type = "merged";
43543
+ this.featureType = 'numeric';
43544
+ this.paintAxis = paintAxis;
43559
43545
  }
43560
43546
 
43561
43547
  init(config) {
@@ -43566,21 +43552,6 @@ class MergedTrack extends TrackBase {
43566
43552
  super.init(config);
43567
43553
  }
43568
43554
 
43569
- get height() {
43570
- return this._height
43571
- }
43572
-
43573
- set height(h) {
43574
- this._height = h;
43575
- if (this.tracks) {
43576
- for (let t of this.tracks) {
43577
- t.height = h;
43578
- t.config.height = h;
43579
- }
43580
- }
43581
- }
43582
-
43583
-
43584
43555
  async postInit() {
43585
43556
 
43586
43557
  this.tracks = [];
@@ -43600,11 +43571,55 @@ class MergedTrack extends TrackBase {
43600
43571
  }
43601
43572
  }
43602
43573
 
43603
- this.height = this.config.height || 100;
43574
+ this.flipAxis = this.config.flipAxis ? this.config.flipAxis : false;
43575
+ this.logScale = this.config.logScale ? this.config.logScale : false;
43576
+ this.autoscale = this.config.autoscale || this.config.max === undefined;
43577
+ if (!this.autoscale) {
43578
+ this.dataRange = {
43579
+ min: this.config.min || 0,
43580
+ max: this.config.max
43581
+ };
43582
+ }
43583
+ for (let t of this.tracks) {
43584
+ t.autoscale = false;
43585
+ t.dataRange = this.dataRange;
43586
+ }
43587
+
43588
+ this.height = this.config.height || 50;
43604
43589
 
43605
43590
  return Promise.all(p)
43606
43591
  }
43607
43592
 
43593
+ get height() {
43594
+ return this._height
43595
+ }
43596
+
43597
+ set height(h) {
43598
+ this._height = h;
43599
+ if (this.tracks) {
43600
+ for (let t of this.tracks) {
43601
+ t.height = h;
43602
+ t.config.height = h;
43603
+ }
43604
+ }
43605
+ }
43606
+
43607
+ menuItemList() {
43608
+ let items = [];
43609
+ if (this.flipAxis !== undefined) {
43610
+ items.push({
43611
+ label: "Flip y-axis",
43612
+ click: () => {
43613
+ this.flipAxis = !this.flipAxis;
43614
+ this.trackView.repaintViews();
43615
+ }
43616
+ });
43617
+ }
43618
+
43619
+ items = items.concat(MenuUtils.numericDataMenuItems(this.trackView));
43620
+
43621
+ return items
43622
+ }
43608
43623
 
43609
43624
  async getFeatures(chr, bpStart, bpEnd, bpPerPixel) {
43610
43625
 
@@ -43614,44 +43629,26 @@ class MergedTrack extends TrackBase {
43614
43629
 
43615
43630
  draw(options) {
43616
43631
 
43617
- var i, len, mergedFeatures, trackOptions, dataRange;
43618
-
43619
- mergedFeatures = options.features; // Array of feature arrays, 1 for each track
43620
-
43621
- dataRange = autoscale(options.referenceFrame.chr, mergedFeatures);
43632
+ const mergedFeatures = options.features; // Array of feature arrays, 1 for each track
43622
43633
 
43623
- //IGVGraphics.fillRect(options.context, 0, options.pixelTop, options.pixelWidth, options.pixelHeight, {'fillStyle': "rgb(255, 255, 255)"});
43624
-
43625
- for (i = 0, len = this.tracks.length; i < len; i++) {
43634
+ if (this.autoscale) {
43635
+ this.dataRange = autoscale(options.referenceFrame.chr, mergedFeatures);
43636
+ }
43626
43637
 
43627
- trackOptions = Object.assign({}, options);
43638
+ for (let i = 0, len = this.tracks.length; i < len; i++) {
43639
+ const trackOptions = Object.assign({}, options);
43628
43640
  trackOptions.features = mergedFeatures[i];
43629
- this.tracks[i].dataRange = dataRange;
43641
+ this.tracks[i].dataRange = this.dataRange;
43642
+ this.tracks[i].flipAxis = this.flipAxis;
43643
+ this.tracks[i].logScale = this.logScale;
43644
+ this.tracks[i].graphType = this.graphType;
43630
43645
  this.tracks[i].draw(trackOptions);
43631
43646
  }
43632
-
43633
- }
43634
-
43635
- paintAxis(ctx, pixelWidth, pixelHeight) {
43636
-
43637
- var i, len, autoscale, track;
43638
-
43639
- autoscale = true; // Hardcoded for now
43640
-
43641
- for (i = 0, len = this.tracks.length; i < len; i++) {
43642
-
43643
- track = this.tracks[i];
43644
-
43645
- if (typeof track.paintAxis === 'function') {
43646
- track.paintAxis(ctx, pixelWidth, pixelHeight);
43647
- if (autoscale) break
43648
- }
43649
- }
43650
43647
  }
43651
43648
 
43652
43649
  popupData(clickState, features) {
43653
43650
 
43654
- const featuresArray = features || clickState.viewport.getCachedFeatures();
43651
+ const featuresArray = features || clickState.viewport.cachedFeatures;
43655
43652
 
43656
43653
  if (featuresArray && featuresArray.length === this.tracks.length) {
43657
43654
  // Array of feature arrays, 1 for each track
@@ -43669,41 +43666,22 @@ class MergedTrack extends TrackBase {
43669
43666
 
43670
43667
 
43671
43668
  supportsWholeGenome() {
43672
- const b = this.tracks.every(track => track.supportsWholeGenome());
43673
- return b
43669
+ return this.tracks.every(track => track.supportsWholeGenome())
43674
43670
  }
43675
43671
  }
43676
43672
 
43677
43673
  function autoscale(chr, featureArrays) {
43678
43674
 
43679
-
43680
- var min = 0,
43681
- max = -Number.MAX_VALUE;
43682
-
43683
- // if (chr === 'all') {
43684
- // allValues = [];
43685
- // featureArrays.forEach(function (features) {
43686
- // features.forEach(function (f) {
43687
- // if (!Number.isNaN(f.value)) {
43688
- // allValues.push(f.value);
43689
- // }
43690
- // });
43691
- // });
43692
- //
43693
- // min = Math.min(0, IGVMath.percentile(allValues, .1));
43694
- // max = IGVMath.percentile(allValues, 99.9);
43695
- //
43696
- // }
43697
- // else {
43698
- featureArrays.forEach(function (features, i) {
43699
- features.forEach(function (f) {
43675
+ let min = 0;
43676
+ let max = -Number.MAX_VALUE;
43677
+ for(let features of featureArrays) {
43678
+ for(let f of features) {
43700
43679
  if (typeof f.value !== 'undefined' && !Number.isNaN(f.value)) {
43701
43680
  min = Math.min(min, f.value);
43702
43681
  max = Math.max(max, f.value);
43703
43682
  }
43704
- });
43705
- });
43706
- // }
43683
+ }
43684
+ }
43707
43685
  return {min: min, max: max}
43708
43686
  }
43709
43687
 
@@ -44245,21 +44223,23 @@ class InteractionTrack extends TrackBase {
44245
44223
 
44246
44224
  // inView features are simply features that have been drawn, i.e. have a drawState
44247
44225
  const inView = cachedFeatures.filter(f => f.drawState);
44248
- if(inView.length === 0) erturn;
44226
+ if(inView.length === 0) return;
44249
44227
 
44250
44228
  this.browser.circularViewVisible = true;
44251
44229
  const chords = makeBedPEChords(inView);
44252
-
44253
- // for filtered set, distinguishing the chromosomes is more critical than tracks
44254
- const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? this.color : getChrColor(refFrame.chr), 0.5);
44255
- const trackColor = IGVColor.addAlpha(this.color, 0.5);
44256
-
44257
- // name the chord set to include filtering information
44258
- const encodedName = this.name.replaceAll(' ', '%20');
44259
- const chordSetName = "all" === refFrame.chr ?
44260
- encodedName :
44261
- `${encodedName} (${refFrame.chr}:${refFrame.start}-${refFrame.end} ; range:${this.dataRange.min}-${this.dataRange.max})`;
44262
- this.browser.circularView.addChords(chords, {track: chordSetName, color: chordSetColor, trackColor: trackColor});
44230
+ sendChords(chords, this, refFrame, 0.5);
44231
+ //
44232
+ //
44233
+ // // for filtered set, distinguishing the chromosomes is more critical than tracks
44234
+ // const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? this.color : getChrColor(refFrame.chr), 0.5)
44235
+ // const trackColor = IGVColor.addAlpha(this.color, 0.5)
44236
+ //
44237
+ // // name the chord set to include locus and filtering information
44238
+ // const encodedName = this.name.replaceAll(' ', '%20')
44239
+ // const chordSetName = "all" === refFrame.chr ?
44240
+ // encodedName :
44241
+ // `${encodedName} (${refFrame.chr}:${refFrame.start}-${refFrame.end} ; range:${this.dataRange.min}-${this.dataRange.max})`
44242
+ // this.browser.circularView.addChords(chords, {track: chordSetName, color: chordSetColor, trackColor: trackColor})
44263
44243
  }
44264
44244
 
44265
44245
  doAutoscale(features) {
@@ -44324,7 +44304,7 @@ class InteractionTrack extends TrackBase {
44324
44304
 
44325
44305
  // We use the cached features rather than method to avoid async load. If the
44326
44306
  // feature is not already loaded this won't work, but the user wouldn't be mousing over it either.
44327
- const featureList = features || clickState.viewport.getCachedFeatures();
44307
+ const featureList = features || clickState.viewport.cachedFeatures;
44328
44308
  const candidates = [];
44329
44309
  if (featureList) {
44330
44310
  const proportional = (this.arcType === "proportional" || this.arcType === "inView" || this.arcType === "partialInView");
@@ -44850,7 +44830,7 @@ class VariantTrack extends TrackBase {
44850
44830
  }
44851
44831
 
44852
44832
  } else if (this._color) {
44853
- variantColor = (typeof this._color === "function") ? this._color(v) : this._color;
44833
+ variantColor = this.color;
44854
44834
  } else if ("NONVARIANT" === v.type) {
44855
44835
  variantColor = this.nonRefColor;
44856
44836
  } else if ("MIXED" === v.type) {
@@ -44861,6 +44841,10 @@ class VariantTrack extends TrackBase {
44861
44841
  return variantColor
44862
44842
  }
44863
44843
 
44844
+ get color() {
44845
+ return this._color ? ((typeof this._color === "function") ? this._color(v) : this._color) : this.defaultColor
44846
+ }
44847
+
44864
44848
  clickedFeatures(clickState, features) {
44865
44849
 
44866
44850
  let featureList = super.clickedFeatures(clickState, features);
@@ -45093,19 +45077,9 @@ class VariantTrack extends TrackBase {
45093
45077
  menuItems.push({
45094
45078
  label: 'Add SVs to circular view',
45095
45079
  click: () => {
45096
- const inView = [];
45097
45080
  for (let viewport of this.trackView.viewports) {
45098
- const refFrame = viewport.referenceFrame;
45099
- for (let f of viewport.getCachedFeatures()) {
45100
- if (f.end >= refFrame.start && f.start <= refFrame.end) {
45101
- inView.push(f);
45102
- }
45103
- }
45081
+ this.sendChordsForViewport(viewport);
45104
45082
  }
45105
-
45106
- const chords = makeVCFChords(inView);
45107
- const color = IGVColor.addAlpha(this._color || this.defaultColor, 0.5);
45108
- this.browser.circularView.addChords(chords, {track: this.name, color: color});
45109
45083
  }
45110
45084
  });
45111
45085
  }
@@ -45124,13 +45098,7 @@ class VariantTrack extends TrackBase {
45124
45098
  list.push({
45125
45099
  label: 'Add SVs to Circular View',
45126
45100
  click: () => {
45127
- const refFrame = viewport.referenceFrame;
45128
- const inView = "all" === refFrame.chr ?
45129
- this.featureSource.getAllFeatures() :
45130
- this.featureSource.featureCache.queryFeatures(refFrame.chr, refFrame.start, refFrame.end);
45131
- const chords = makeVCFChords(inView);
45132
- const color = IGVColor.addAlpha(this._color || this.defaultColor, 0.5);
45133
- this.browser.circularView.addChords(chords, {track: this.name, color: color});
45101
+ this.sendChordsForViewport(viewport);
45134
45102
  }
45135
45103
  });
45136
45104
 
@@ -45140,6 +45108,15 @@ class VariantTrack extends TrackBase {
45140
45108
  }
45141
45109
 
45142
45110
 
45111
+ sendChordsForViewport(viewport) {
45112
+ const refFrame = viewport.referenceFrame;
45113
+ const inView = "all" === refFrame.chr ?
45114
+ this.featureSource.getAllFeatures() :
45115
+ this.featureSource.featureCache.queryFeatures(refFrame.chr, refFrame.start, refFrame.end);
45116
+ const chords = makeVCFChords(inView);
45117
+ sendChords(chords, this, refFrame, 0.5);
45118
+ }
45119
+
45143
45120
  /**
45144
45121
  * Create a "color by" checkbox menu item, optionally initially checked
45145
45122
  * @param menuItem
@@ -45436,7 +45413,7 @@ class EqtlTrack extends TrackBase {
45436
45413
  */
45437
45414
  popupData(clickState) {
45438
45415
 
45439
- let features = clickState.viewport.getCachedFeatures();
45416
+ let features = clickState.viewport.cachedFeatures;
45440
45417
  if (!features || features.length === 0) return []
45441
45418
 
45442
45419
  const tolerance = 3;
@@ -45766,7 +45743,7 @@ class GWASTrack extends TrackBase {
45766
45743
 
45767
45744
  let data = [];
45768
45745
  const track = clickState.viewport.trackView.track;
45769
- const features = clickState.viewport.getCachedFeatures();
45746
+ const features = clickState.viewport.cachedFeatures;
45770
45747
 
45771
45748
  if (features) {
45772
45749
  let count = 0;
@@ -46449,7 +46426,7 @@ class RNAFeatureSource {
46449
46426
 
46450
46427
  const data = await igvxhr.loadString(this.config.url, options);
46451
46428
 
46452
- this.featureCache = new FeatureCache(parseBP(data), genome);
46429
+ this.featureCache = new FeatureCache$1(parseBP(data), genome);
46453
46430
 
46454
46431
  return this.featureCache.queryFeatures(chr, start, end)
46455
46432
 
@@ -47731,6 +47708,15 @@ class ReferenceFrame {
47731
47708
  this.id = guid$2();
47732
47709
  }
47733
47710
 
47711
+ extend(locus) {
47712
+ const newStart = Math.min(locus.start, this.start);
47713
+ const newEnd = Math.max(locus.end, this.end);
47714
+ const ratio = (newEnd - newStart) / (this.end - this.start);
47715
+ this.start = newStart;
47716
+ this.end = newEnd;
47717
+ this.bpPerPixel *= ratio;
47718
+ }
47719
+
47734
47720
  calculateEnd(pixels) {
47735
47721
  return this.start + this.bpPerPixel * pixels
47736
47722
  }
@@ -47817,7 +47803,7 @@ class ReferenceFrame {
47817
47803
 
47818
47804
  const viewChanged = start !== this.start || bpPerPixel !== this.bpPerPixel;
47819
47805
  if (viewChanged) {
47820
- await browser.updateViews(this);
47806
+ await browser.updateViews(true);
47821
47807
  }
47822
47808
 
47823
47809
  }
@@ -49619,8 +49605,8 @@ class Browser {
49619
49605
  this.svgSaveControl = new SVGSaveControl($toggle_button_container.get(0), this);
49620
49606
  }
49621
49607
 
49622
- if(config.customButtons) {
49623
- for(let b of config.customButtons) {
49608
+ if (config.customButtons) {
49609
+ for (let b of config.customButtons) {
49624
49610
  new CustomButton($toggle_button_container.get(0), this, b);
49625
49611
  }
49626
49612
  }
@@ -49781,7 +49767,6 @@ class Browser {
49781
49767
  return undefined
49782
49768
  }
49783
49769
  }
49784
-
49785
49770
  }
49786
49771
  }
49787
49772
 
@@ -49871,6 +49856,11 @@ class Browser {
49871
49856
 
49872
49857
  await this.loadTrackList(trackConfigurations);
49873
49858
 
49859
+ // The ruler track is not explicitly loaded, but needs updated nonetheless.
49860
+ for (let rtv of this.trackViews.filter((tv) => tv.track.type === 'ruler')) {
49861
+ rtv.updateViews();
49862
+ }
49863
+
49874
49864
  this.updateUIWithReferenceFrameList();
49875
49865
 
49876
49866
  }
@@ -50049,23 +50039,19 @@ class Browser {
50049
50039
 
50050
50040
  async loadTrackList(configList) {
50051
50041
 
50052
- try {
50053
- const promises = [];
50054
- for (let config of configList) {
50055
- promises.push(this.loadTrack(config, false));
50056
- }
50042
+ const promises = [];
50043
+ for (let config of configList) {
50044
+ promises.push(this.loadTrack(config));
50045
+ }
50057
50046
 
50058
- const loadedTracks = await Promise.all(promises);
50059
- const groupAutoscaleViews = this.trackViews.filter(function (trackView) {
50060
- return trackView.track.autoscaleGroup
50061
- });
50062
- if (groupAutoscaleViews.length > 0) {
50063
- this.updateViews(groupAutoscaleViews);
50064
- }
50065
- return loadedTracks
50066
- } finally {
50067
- await this.resize();
50047
+ const loadedTracks = await Promise.all(promises);
50048
+ const groupAutoscaleViews = this.trackViews.filter(function (trackView) {
50049
+ return trackView.track.autoscaleGroup
50050
+ });
50051
+ if (groupAutoscaleViews.length > 0) {
50052
+ this.updateViews();
50068
50053
  }
50054
+ return loadedTracks
50069
50055
  }
50070
50056
 
50071
50057
  async loadROI(config) {
@@ -50079,6 +50065,8 @@ class Browser {
50079
50065
  } else {
50080
50066
  this.roi.push(new ROI(config, this.genome));
50081
50067
  }
50068
+ // Force reload all views (force = true) to insure ROI features are loaded. Wasteful but this function is
50069
+ // rarely called.
50082
50070
  await this.updateViews(true);
50083
50071
  }
50084
50072
 
@@ -50090,14 +50078,14 @@ class Browser {
50090
50078
  }
50091
50079
  }
50092
50080
  for (let tv of this.trackViews) {
50093
- tv.updateViews(true);
50081
+ tv.repaintViews();
50094
50082
  }
50095
50083
  }
50096
50084
 
50097
50085
  clearROIs() {
50098
50086
  this.roi = [];
50099
50087
  for (let tv of this.trackViews) {
50100
- tv.updateViews(true);
50088
+ tv.repaintViews();
50101
50089
  }
50102
50090
  }
50103
50091
 
@@ -50109,24 +50097,12 @@ class Browser {
50109
50097
  /**
50110
50098
  * Return a promise to load a track.
50111
50099
  *
50112
- * Each track is associated with the following DOM elements
50113
- *
50114
- * leftHandGutter - div on the left for track controls and legend
50115
- * contentDiv - a div element wrapping all the track content. Height can be > viewportDiv height
50116
- * viewportDiv - a div element through which the track is viewed. This might have a vertical scrollbar
50117
- * canvas - canvas element upon which the track is drawn. Child of contentDiv
50118
- *
50119
- * The width of all elements should be equal. Height of the viewportDiv is controlled by the user, but never
50120
- * greater than the contentDiv height. Height of contentDiv and canvas are equal, and governed by the data
50121
- * loaded.
50122
- *
50123
- *
50124
50100
  * @param config
50125
50101
  * @param doResize - undefined by default
50126
50102
  * @returns {*}
50127
50103
  */
50128
50104
 
50129
- async loadTrack(config, doResize) {
50105
+ async loadTrack(config) {
50130
50106
 
50131
50107
 
50132
50108
  // config might be json
@@ -50194,11 +50170,6 @@ class Browser {
50194
50170
  }
50195
50171
  msg += (": " + config.url);
50196
50172
  Alert.presentAlert(new Error(msg), undefined);
50197
- } finally {
50198
- // TODO: If loadTrack() is called individually - not via loadTrackList() - call this.resize()
50199
- if (false === doResize) ; else {
50200
- await this.resize();
50201
- }
50202
50173
  }
50203
50174
  }
50204
50175
 
@@ -50422,41 +50393,7 @@ class Browser {
50422
50393
  this.navbarManager.navbarDidResize(this.$navigation.width(), isWGV);
50423
50394
  }
50424
50395
 
50425
- await this.resize();
50426
- }
50427
-
50428
- async resize() {
50429
-
50430
- const viewportWidth = this.calculateViewportWidth(this.referenceFrameList.length);
50431
-
50432
- for (let referenceFrame of this.referenceFrameList) {
50433
-
50434
- const index = this.referenceFrameList.indexOf(referenceFrame);
50435
-
50436
- const {chr, genome} = referenceFrame;
50437
-
50438
- const {bpLength} = genome.getChromosome(referenceFrame.chr);
50439
-
50440
- const viewportWidthBP = referenceFrame.toBP(viewportWidth);
50441
-
50442
- // viewportWidthBP > bpLength occurs when locus is full chromosome and user widens browser
50443
- if (GenomeUtils.isWholeGenomeView(chr) || viewportWidthBP > bpLength) {
50444
- // console.log(`${ Date.now() } Recalc referenceFrame(${ index }) bpp. viewport ${ StringUtils.numberFormatter(viewportWidthBP) } > ${ StringUtils.numberFormatter(bpLength) }.`)
50445
- referenceFrame.bpPerPixel = bpLength / viewportWidth;
50446
- } else {
50447
- // console.log(`${ Date.now() } Recalc referenceFrame(${ index }) end.`)
50448
- referenceFrame.end = referenceFrame.start + referenceFrame.toBP(viewportWidth);
50449
- }
50450
-
50451
- for (let {viewports} of this.trackViews) {
50452
- viewports[index].setWidth(viewportWidth);
50453
- }
50454
-
50455
- }
50456
-
50457
- await this.updateViews(true);
50458
-
50459
- this.updateUIWithReferenceFrameList();
50396
+ await resize.call(this);
50460
50397
  }
50461
50398
 
50462
50399
  async updateViews(force) {
@@ -50532,6 +50469,12 @@ class Browser {
50532
50469
 
50533
50470
  }
50534
50471
 
50472
+ repaintViews() {
50473
+ for (let trackView of this.trackViews) {
50474
+ trackView.repaintViews();
50475
+ }
50476
+ }
50477
+
50535
50478
  updateLocusSearchWidget() {
50536
50479
 
50537
50480
  const referenceFrameList = this.referenceFrameList;
@@ -50633,12 +50576,56 @@ class Browser {
50633
50576
  const viewport = createViewport(trackView, viewportColumn, referenceFrameRight);
50634
50577
  trackView.viewports.splice(indexRight, 0, viewport);
50635
50578
  }
50579
+ }
50580
+
50581
+ this.centerLineList = this.createCenterLineList(this.columnContainer);
50582
+
50583
+ await resize.call(this);
50584
+ }
50585
+
50586
+ /**
50587
+ * Add a new multi-locus panel for the specified region
50588
+ * @param chr
50589
+ * @param start
50590
+ * @param end
50591
+ * @param referenceFrameLeft - optional, if supplied new panel should be placed to the immediate right
50592
+ */
50593
+ async addMultiLocusPanel(chr, start, end, referenceFrameLeft) {
50594
+
50595
+ // account for reduced viewport width as a result of adding right mate pair panel
50596
+ const viewportWidth = this.calculateViewportWidth(1 + this.referenceFrameList.length);
50597
+ const scaleFactor = this.calculateViewportWidth(this.referenceFrameList.length) / this.calculateViewportWidth(1 + this.referenceFrameList.length);
50598
+ for (let refFrame of this.referenceFrameList) {
50599
+ refFrame.bpPerPixel *= scaleFactor;
50600
+ }
50601
+
50602
+ const bpp = (end - start) / viewportWidth;
50603
+ const newReferenceFrame = new ReferenceFrame(this.genome, chr, start, end, bpp);
50604
+ const indexLeft = referenceFrameLeft ? this.referenceFrameList.indexOf(referenceFrameLeft) : this.referenceFrameList.length - 1;
50605
+ const indexRight = 1 + indexLeft;
50636
50606
 
50607
+ // TODO -- this is really ugly
50608
+ const {$viewport} = this.trackViews[0].viewports[indexLeft];
50609
+ const viewportColumn = viewportColumnManager.insertAfter($viewport.get(0).parentElement);
50610
+
50611
+ if (indexRight === this.referenceFrameList.length) {
50612
+ this.referenceFrameList.push(newReferenceFrame);
50613
+ for (let trackView of this.trackViews) {
50614
+ const viewport = createViewport(trackView, viewportColumn, newReferenceFrame);
50615
+ trackView.viewports.push(viewport);
50616
+ }
50617
+ } else {
50618
+ this.referenceFrameList.splice(indexRight, 0, newReferenceFrame);
50619
+ for (let trackView of this.trackViews) {
50620
+ const viewport = createViewport(trackView, viewportColumn, newReferenceFrame);
50621
+ trackView.viewports.splice(indexRight, 0, viewport);
50622
+ }
50637
50623
  }
50638
50624
 
50625
+
50639
50626
  this.centerLineList = this.createCenterLineList(this.columnContainer);
50640
50627
 
50641
- await this.resize();
50628
+ resize.call(this);
50642
50629
  }
50643
50630
 
50644
50631
  async removeMultiLocusPanel(referenceFrame) {
@@ -50667,7 +50654,13 @@ class Browser {
50667
50654
 
50668
50655
  }
50669
50656
 
50670
- async selectMultiLocusPanel(referenceFrame) {
50657
+ /**
50658
+ * Goto the locus represented by the selected referenceFrame, discarding all other panels
50659
+ *
50660
+ * @param referenceFrame
50661
+ * @returns {Promise<void>}
50662
+ */
50663
+ async gotoMultilocusPanel(referenceFrame) {
50671
50664
 
50672
50665
  const referenceFrameIndex = this.referenceFrameList.indexOf(referenceFrame);
50673
50666
 
@@ -50721,7 +50714,7 @@ class Browser {
50721
50714
 
50722
50715
  this.updateUIWithReferenceFrameList();
50723
50716
 
50724
- await this.updateViews(true);
50717
+ await this.updateViews();
50725
50718
 
50726
50719
  }
50727
50720
 
@@ -50944,7 +50937,6 @@ class Browser {
50944
50937
  }
50945
50938
 
50946
50939
 
50947
-
50948
50940
  json["tracks"] = trackJson;
50949
50941
 
50950
50942
  return json // This is an object, not a json string
@@ -50963,14 +50955,9 @@ class Browser {
50963
50955
  return surl
50964
50956
  }
50965
50957
 
50966
- currentLoci() {
50967
- const loci = [];
50958
+ currentReferenceFrames() {
50968
50959
  const anyTrackView = this.trackViews[0];
50969
- for (let {referenceFrame} of anyTrackView.viewports) {
50970
- const locusString = referenceFrame.getLocusString();
50971
- loci.push(locusString);
50972
- }
50973
- return loci
50960
+ return anyTrackView.viewports.map(vp => vp.referenceFrame)
50974
50961
  }
50975
50962
 
50976
50963
  /**
@@ -51090,12 +51077,9 @@ class Browser {
51090
51077
  }
51091
51078
 
51092
51079
  addWindowResizeHandler() {
51093
- this.boundWindowResizeHandler = windowResizeHandler.bind(this);
51080
+ // Create a copy of the prototype "resize" function bound to this instance. Neccessary to support removing.
51081
+ this.boundWindowResizeHandler = resize.bind(this);
51094
51082
  window.addEventListener('resize', this.boundWindowResizeHandler);
51095
-
51096
- function windowResizeHandler() {
51097
- this.resize();
51098
- }
51099
51083
  }
51100
51084
 
51101
51085
  removeWindowResizeHandler() {
@@ -51194,6 +51178,48 @@ class Browser {
51194
51178
  }
51195
51179
  }
51196
51180
 
51181
+ /**
51182
+ * Function called win window is resized, or visibility changed (e.g. "show" from a tab). This is a function rather
51183
+ * than class method because it needs to be copied and bound to specific instances of browser to support listener
51184
+ * removal
51185
+ *
51186
+ * @returns {Promise<void>}
51187
+ */
51188
+ async function resize() {
51189
+
51190
+ const viewportWidth = this.calculateViewportWidth(this.referenceFrameList.length);
51191
+
51192
+ for (let referenceFrame of this.referenceFrameList) {
51193
+
51194
+ const index = this.referenceFrameList.indexOf(referenceFrame);
51195
+
51196
+ const {chr, genome} = referenceFrame;
51197
+
51198
+ const {bpLength} = genome.getChromosome(referenceFrame.chr);
51199
+
51200
+ const viewportWidthBP = referenceFrame.toBP(viewportWidth);
51201
+
51202
+ // viewportWidthBP > bpLength occurs when locus is full chromosome and user widens browser
51203
+ if (GenomeUtils.isWholeGenomeView(chr) || viewportWidthBP > bpLength) {
51204
+ // console.log(`${ Date.now() } Recalc referenceFrame(${ index }) bpp. viewport ${ StringUtils.numberFormatter(viewportWidthBP) } > ${ StringUtils.numberFormatter(bpLength) }.`)
51205
+ referenceFrame.bpPerPixel = bpLength / viewportWidth;
51206
+ } else {
51207
+ // console.log(`${ Date.now() } Recalc referenceFrame(${ index }) end.`)
51208
+ referenceFrame.end = referenceFrame.start + referenceFrame.toBP(viewportWidth);
51209
+ }
51210
+
51211
+ for (let {viewports} of this.trackViews) {
51212
+ viewports[index].setWidth(viewportWidth);
51213
+ }
51214
+
51215
+ }
51216
+
51217
+ this.updateUIWithReferenceFrameList();
51218
+
51219
+ await this.updateViews(true);
51220
+ }
51221
+
51222
+
51197
51223
  function handleMouseMove(e) {
51198
51224
 
51199
51225
  e.preventDefault();