igv 2.11.0 → 2.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -23
- package/dist/igv.esm.js +1002 -1124
- package/dist/igv.esm.min.js +9 -9
- package/dist/igv.esm.min.js.map +1 -1
- package/dist/igv.js +991 -1048
- package/dist/igv.min.js +10 -10
- package/dist/igv.min.js.map +1 -1
- package/package.json +4 -4
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",
|
|
@@ -19853,6 +19854,7 @@ function inferTrackType(config) {
|
|
|
19853
19854
|
return "alignment"
|
|
19854
19855
|
case "bedpe":
|
|
19855
19856
|
case "bedpe-loop":
|
|
19857
|
+
case "biginteract":
|
|
19856
19858
|
return "interact"
|
|
19857
19859
|
case "bp":
|
|
19858
19860
|
return "arc"
|
|
@@ -20111,6 +20113,38 @@ function isSecureContext() {
|
|
|
20111
20113
|
return window.location.protocol === "https:" || window.location.hostname === "localhost"
|
|
20112
20114
|
}
|
|
20113
20115
|
|
|
20116
|
+
const pairs =
|
|
20117
|
+
[
|
|
20118
|
+
['A', 'T'],
|
|
20119
|
+
['G', 'C'],
|
|
20120
|
+
['Y', 'R'],
|
|
20121
|
+
['W', 'S'],
|
|
20122
|
+
['K', 'M'],
|
|
20123
|
+
['D', 'H'],
|
|
20124
|
+
['B', 'V']
|
|
20125
|
+
];
|
|
20126
|
+
|
|
20127
|
+
const complements = new Map();
|
|
20128
|
+
for (let p of pairs) {
|
|
20129
|
+
const p1 = p[0];
|
|
20130
|
+
const p2 = p[1];
|
|
20131
|
+
complements.set(p1, p2);
|
|
20132
|
+
complements.set(p2, p1);
|
|
20133
|
+
complements.set(p1.toLowerCase(), p2.toLowerCase());
|
|
20134
|
+
complements.set(p2.toLowerCase(), p1.toLowerCase());
|
|
20135
|
+
}
|
|
20136
|
+
|
|
20137
|
+
function reverseComplementSequence(sequence) {
|
|
20138
|
+
|
|
20139
|
+
let comp = '';
|
|
20140
|
+
let idx = sequence.length;
|
|
20141
|
+
while (idx-- > 0) {
|
|
20142
|
+
const base = sequence[idx];
|
|
20143
|
+
comp += complements.has(base) ? complements.get(base) : base;
|
|
20144
|
+
}
|
|
20145
|
+
return comp
|
|
20146
|
+
}
|
|
20147
|
+
|
|
20114
20148
|
/*
|
|
20115
20149
|
* The MIT License (MIT)
|
|
20116
20150
|
*
|
|
@@ -20235,7 +20269,6 @@ class SequenceTrack {
|
|
|
20235
20269
|
}
|
|
20236
20270
|
|
|
20237
20271
|
menuItemList() {
|
|
20238
|
-
|
|
20239
20272
|
return [
|
|
20240
20273
|
{
|
|
20241
20274
|
name: this.reversed ? "Forward" : "Reverse",
|
|
@@ -20270,17 +20303,20 @@ class SequenceTrack {
|
|
|
20270
20303
|
contextMenuItemList(clickState) {
|
|
20271
20304
|
const viewport = clickState.viewport;
|
|
20272
20305
|
if (viewport.referenceFrame.bpPerPixel <= 1) {
|
|
20306
|
+
const pixelWidth = viewport.getWidth();
|
|
20307
|
+
const bpWindow = pixelWidth * viewport.referenceFrame.bpPerPixel;
|
|
20308
|
+
const chr = viewport.referenceFrame.chr;
|
|
20309
|
+
const start = Math.floor(viewport.referenceFrame.start);
|
|
20310
|
+
const end = Math.ceil(start + bpWindow);
|
|
20273
20311
|
const items = [
|
|
20274
20312
|
{
|
|
20275
|
-
label: 'View visible sequence...',
|
|
20313
|
+
label: this.reversed ? 'View visible sequence (reversed)...' : 'View visible sequence...',
|
|
20276
20314
|
click: async () => {
|
|
20277
|
-
|
|
20278
|
-
|
|
20279
|
-
|
|
20280
|
-
|
|
20281
|
-
|
|
20282
|
-
const sequence = await this.browser.genome.sequence.getSequence(chr, start, end);
|
|
20283
|
-
Alert.presentAlert(sequence);
|
|
20315
|
+
let seq = await this.browser.genome.sequence.getSequence(chr, start, end);
|
|
20316
|
+
if (this.reversed) {
|
|
20317
|
+
seq = reverseComplementSequence(seq);
|
|
20318
|
+
}
|
|
20319
|
+
Alert.presentAlert(seq);
|
|
20284
20320
|
}
|
|
20285
20321
|
}
|
|
20286
20322
|
];
|
|
@@ -20288,14 +20324,18 @@ class SequenceTrack {
|
|
|
20288
20324
|
items.push({
|
|
20289
20325
|
label: 'Copy visible sequence',
|
|
20290
20326
|
click: async () => {
|
|
20291
|
-
|
|
20292
|
-
|
|
20293
|
-
|
|
20294
|
-
|
|
20295
|
-
|
|
20296
|
-
|
|
20297
|
-
|
|
20327
|
+
let seq = await this.browser.genome.sequence.getSequence(chr, start, end);
|
|
20328
|
+
if (this.reversed) {
|
|
20329
|
+
seq = reverseComplementSequence(seq);
|
|
20330
|
+
}
|
|
20331
|
+
try {
|
|
20332
|
+
await navigator.clipboard.writeText(seq);
|
|
20333
|
+
} catch (e) {
|
|
20334
|
+
console.error(e);
|
|
20335
|
+
Alert.presentAlert(`error copying sequence to clipboard ${e}`);
|
|
20336
|
+
}
|
|
20298
20337
|
}
|
|
20338
|
+
|
|
20299
20339
|
});
|
|
20300
20340
|
}
|
|
20301
20341
|
items.push('<hr/>');
|
|
@@ -20439,7 +20479,7 @@ class SequenceTrack {
|
|
|
20439
20479
|
}
|
|
20440
20480
|
}
|
|
20441
20481
|
|
|
20442
|
-
supportsWholeGenome() {
|
|
20482
|
+
get supportsWholeGenome() {
|
|
20443
20483
|
return false
|
|
20444
20484
|
}
|
|
20445
20485
|
|
|
@@ -20513,13 +20553,13 @@ class Viewport {
|
|
|
20513
20553
|
this.$content.height(this.$viewport.height());
|
|
20514
20554
|
this.contentDiv = this.$content.get(0);
|
|
20515
20555
|
|
|
20516
|
-
this.$canvas =
|
|
20517
|
-
this.$content.append(this.$canvas)
|
|
20518
|
-
|
|
20519
|
-
this.canvas = this.$canvas.get(0)
|
|
20520
|
-
this.ctx = this.canvas.getContext("2d")
|
|
20556
|
+
// this.$canvas = $('<canvas>')
|
|
20557
|
+
// this.$content.append(this.$canvas)
|
|
20558
|
+
//
|
|
20559
|
+
// this.canvas = this.$canvas.get(0)
|
|
20560
|
+
// this.ctx = this.canvas.getContext("2d")
|
|
20521
20561
|
|
|
20522
|
-
this.
|
|
20562
|
+
this.$viewport.width(width);
|
|
20523
20563
|
|
|
20524
20564
|
this.initializationHelper();
|
|
20525
20565
|
|
|
@@ -20586,14 +20626,13 @@ class Viewport {
|
|
|
20586
20626
|
console.log('Viewport - draw(drawConfiguration, features, roiFeatures)');
|
|
20587
20627
|
}
|
|
20588
20628
|
|
|
20589
|
-
checkContentHeight() {
|
|
20629
|
+
checkContentHeight(features) {
|
|
20590
20630
|
|
|
20591
20631
|
let track = this.trackView.track;
|
|
20592
|
-
|
|
20632
|
+
features = features || this.cachedFeatures;
|
|
20593
20633
|
if ("FILL" === track.displayMode) {
|
|
20594
20634
|
this.setContentHeight(this.$viewport.height());
|
|
20595
20635
|
} else if (typeof track.computePixelHeight === 'function') {
|
|
20596
|
-
let features = this.cachedFeatures;
|
|
20597
20636
|
if (features && features.length > 0) {
|
|
20598
20637
|
let requiredContentHeight = track.computePixelHeight(features);
|
|
20599
20638
|
let currentContentHeight = this.$content.height();
|
|
@@ -20609,12 +20648,10 @@ class Viewport {
|
|
|
20609
20648
|
}
|
|
20610
20649
|
|
|
20611
20650
|
setContentHeight(contentHeight) {
|
|
20651
|
+
|
|
20612
20652
|
// Maximum height of a canvas is ~32,000 pixels on Chrome, possibly smaller on other platforms
|
|
20613
20653
|
contentHeight = Math.min(contentHeight, 32000);
|
|
20614
|
-
|
|
20615
20654
|
this.$content.height(contentHeight);
|
|
20616
|
-
|
|
20617
|
-
if (this.tile) this.tile.invalidate = true;
|
|
20618
20655
|
}
|
|
20619
20656
|
|
|
20620
20657
|
isLoading() {
|
|
@@ -20631,8 +20668,6 @@ class Viewport {
|
|
|
20631
20668
|
|
|
20632
20669
|
setWidth(width) {
|
|
20633
20670
|
this.$viewport.width(width);
|
|
20634
|
-
this.canvas.style.width = (`${width}px`);
|
|
20635
|
-
this.canvas.setAttribute('width', width);
|
|
20636
20671
|
}
|
|
20637
20672
|
|
|
20638
20673
|
getWidth() {
|
|
@@ -20662,8 +20697,6 @@ class Viewport {
|
|
|
20662
20697
|
this.popover.dispose();
|
|
20663
20698
|
}
|
|
20664
20699
|
|
|
20665
|
-
this.removeMouseHandlers();
|
|
20666
|
-
|
|
20667
20700
|
this.$viewport.get(0).remove();
|
|
20668
20701
|
|
|
20669
20702
|
// Null out all properties -- this should not be neccessary, but just in case there is a
|
|
@@ -22759,7 +22792,7 @@ const Cytoband = function (start, end, name, typestain) {
|
|
|
22759
22792
|
}
|
|
22760
22793
|
};
|
|
22761
22794
|
|
|
22762
|
-
const _version = "2.
|
|
22795
|
+
const _version = "2.12.0";
|
|
22763
22796
|
function version() {
|
|
22764
22797
|
return _version
|
|
22765
22798
|
}
|
|
@@ -23113,6 +23146,7 @@ class Genome {
|
|
|
23113
23146
|
}
|
|
23114
23147
|
|
|
23115
23148
|
async getSequence(chr, start, end) {
|
|
23149
|
+
chr = this.getChromosomeName(chr);
|
|
23116
23150
|
return this.sequence.getSequence(chr, start, end)
|
|
23117
23151
|
}
|
|
23118
23152
|
}
|
|
@@ -23248,28 +23282,27 @@ class TrackViewport extends Viewport {
|
|
|
23248
23282
|
this.$viewport.append(this.$spinner);
|
|
23249
23283
|
this.$spinner.append($$1('<div>'));
|
|
23250
23284
|
|
|
23251
|
-
const
|
|
23252
|
-
|
|
23285
|
+
const track = this.trackView.track;
|
|
23253
23286
|
if ('sequence' !== track.type) {
|
|
23254
23287
|
this.$zoomInNotice = this.createZoomInNotice(this.$content);
|
|
23255
23288
|
}
|
|
23256
23289
|
|
|
23257
|
-
if (track.name && "sequence" !== track.
|
|
23258
|
-
|
|
23290
|
+
if (track.name && "sequence" !== track.id) {
|
|
23259
23291
|
this.$trackLabel = $$1('<div class="igv-track-label">');
|
|
23260
23292
|
this.$viewport.append(this.$trackLabel);
|
|
23261
23293
|
this.setTrackLabel(track.name);
|
|
23262
|
-
|
|
23263
23294
|
if (false === this.browser.trackLabelsVisible) {
|
|
23264
23295
|
this.$trackLabel.hide();
|
|
23265
23296
|
}
|
|
23266
|
-
|
|
23267
23297
|
}
|
|
23268
23298
|
|
|
23269
23299
|
this.stopSpinner();
|
|
23270
|
-
|
|
23271
23300
|
this.addMouseHandlers();
|
|
23301
|
+
}
|
|
23272
23302
|
|
|
23303
|
+
setContentHeight(contentHeight) {
|
|
23304
|
+
super.setContentHeight(contentHeight);
|
|
23305
|
+
if (this.featureCache) this.featureCache.redraw = true;
|
|
23273
23306
|
}
|
|
23274
23307
|
|
|
23275
23308
|
setTrackLabel(label) {
|
|
@@ -23291,61 +23324,74 @@ class TrackViewport extends Viewport {
|
|
|
23291
23324
|
}
|
|
23292
23325
|
}
|
|
23293
23326
|
|
|
23327
|
+
/**
|
|
23328
|
+
* Test to determine if we are zoomed in far enough to see features. Applicable to tracks with visibility windows.
|
|
23329
|
+
*
|
|
23330
|
+
* As a side effect the viewports canvas is removed if zoomed out.
|
|
23331
|
+
*
|
|
23332
|
+
* @returns {boolean} true if we are zoomed in past visibility window, false otherwise
|
|
23333
|
+
*/
|
|
23294
23334
|
checkZoomIn() {
|
|
23295
23335
|
|
|
23296
|
-
const
|
|
23297
|
-
|
|
23298
|
-
if (this.referenceFrame.chr.toLowerCase() === "all" && !this.trackView.track.supportsWholeGenome()) {
|
|
23336
|
+
const zoomedOutOfWindow = () => {
|
|
23337
|
+
if (this.referenceFrame.chr.toLowerCase() === "all" && !this.trackView.track.supportsWholeGenome) {
|
|
23299
23338
|
return true
|
|
23300
23339
|
} else {
|
|
23301
23340
|
const visibilityWindow = this.trackView.track.visibilityWindow;
|
|
23302
23341
|
return (
|
|
23303
23342
|
visibilityWindow !== undefined && visibilityWindow > 0 &&
|
|
23304
|
-
(referenceFrame.bpPerPixel * this.$viewport.width() > visibilityWindow))
|
|
23343
|
+
(this.referenceFrame.bpPerPixel * this.$viewport.width() > visibilityWindow))
|
|
23305
23344
|
}
|
|
23306
23345
|
};
|
|
23307
23346
|
|
|
23347
|
+
if (this.trackView.track && "sequence" === this.trackView.track.type && this.referenceFrame.bpPerPixel > 1) {
|
|
23348
|
+
$$1(this.canvas).remove();
|
|
23349
|
+
this.canvas = undefined;
|
|
23350
|
+
//this.featureCache = undefined
|
|
23351
|
+
return false
|
|
23352
|
+
}
|
|
23353
|
+
|
|
23308
23354
|
if (!(this.viewIsReady())) {
|
|
23309
23355
|
return false
|
|
23310
23356
|
}
|
|
23311
23357
|
|
|
23312
|
-
if (this.$zoomInNotice) {
|
|
23313
|
-
if (showZoomInNotice()) {
|
|
23314
|
-
// Out of visibility window
|
|
23315
|
-
if (this.canvas) {
|
|
23316
|
-
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
23317
|
-
this.tile = undefined;
|
|
23318
|
-
}
|
|
23319
|
-
this.$zoomInNotice.show();
|
|
23320
23358
|
|
|
23321
|
-
|
|
23322
|
-
const minHeight = this.trackView.minHeight || 0;
|
|
23323
|
-
this.setContentHeight(minHeight);
|
|
23324
|
-
}
|
|
23359
|
+
if (zoomedOutOfWindow()) {
|
|
23325
23360
|
|
|
23326
|
-
|
|
23327
|
-
|
|
23361
|
+
// Out of visibility window
|
|
23362
|
+
if (this.canvas) {
|
|
23363
|
+
$$1(this.canvas).remove();
|
|
23364
|
+
this.canvas = undefined;
|
|
23365
|
+
//this.featureCache = undefined
|
|
23366
|
+
}
|
|
23367
|
+
if (this.trackView.track.autoHeight) {
|
|
23368
|
+
const minHeight = this.trackView.minHeight || 0;
|
|
23369
|
+
this.setContentHeight(minHeight);
|
|
23370
|
+
}
|
|
23371
|
+
if (this.$zoomInNotice) {
|
|
23372
|
+
this.$zoomInNotice.show();
|
|
23373
|
+
}
|
|
23374
|
+
return false
|
|
23375
|
+
} else {
|
|
23376
|
+
if (this.$zoomInNotice) {
|
|
23328
23377
|
this.$zoomInNotice.hide();
|
|
23329
|
-
return true
|
|
23330
23378
|
}
|
|
23379
|
+
return true
|
|
23331
23380
|
}
|
|
23332
23381
|
|
|
23333
|
-
return true
|
|
23334
|
-
|
|
23335
|
-
|
|
23336
23382
|
}
|
|
23337
23383
|
|
|
23384
|
+
/**
|
|
23385
|
+
* Adjust the canvas to the current genomic state.
|
|
23386
|
+
*/
|
|
23338
23387
|
shift() {
|
|
23339
|
-
const
|
|
23340
|
-
|
|
23341
|
-
|
|
23342
|
-
|
|
23343
|
-
|
|
23344
|
-
|
|
23345
|
-
|
|
23346
|
-
|
|
23347
|
-
const pixelOffset = Math.round((self.tile.startBP - referenceFrame.start) / referenceFrame.bpPerPixel);
|
|
23348
|
-
self.canvas.style.left = pixelOffset + "px";
|
|
23388
|
+
const referenceFrame = this.referenceFrame;
|
|
23389
|
+
if (this.canvas &&
|
|
23390
|
+
this.canvas._data &&
|
|
23391
|
+
this.canvas._data.chr === this.referenceFrame.chr &&
|
|
23392
|
+
this.canvas._data.bpPerPixel === referenceFrame.bpPerPixel) {
|
|
23393
|
+
const pixelOffset = Math.round((this.canvas._data.startBP - referenceFrame.start) / referenceFrame.bpPerPixel);
|
|
23394
|
+
this.canvas.style.left = pixelOffset + "px";
|
|
23349
23395
|
}
|
|
23350
23396
|
}
|
|
23351
23397
|
|
|
@@ -23368,22 +23414,23 @@ class TrackViewport extends Viewport {
|
|
|
23368
23414
|
this.startSpinner();
|
|
23369
23415
|
|
|
23370
23416
|
try {
|
|
23371
|
-
const
|
|
23417
|
+
const track = this.trackView.track;
|
|
23418
|
+
const features = await this.getFeatures(track, chr, bpStart, bpEnd, referenceFrame.bpPerPixel);
|
|
23372
23419
|
let roiFeatures = [];
|
|
23373
|
-
const roi = mergeArrays(this.browser.roi,
|
|
23420
|
+
const roi = mergeArrays(this.browser.roi, track.roi);
|
|
23374
23421
|
if (roi) {
|
|
23375
23422
|
for (let r of roi) {
|
|
23376
|
-
const f = await
|
|
23377
|
-
r.getFeatures(chr, bpStart, bpEnd, referenceFrame.bpPerPixel);
|
|
23423
|
+
const f = await r.getFeatures(chr, bpStart, bpEnd, referenceFrame.bpPerPixel);
|
|
23378
23424
|
roiFeatures.push({track: r, features: f});
|
|
23379
23425
|
}
|
|
23380
23426
|
}
|
|
23381
23427
|
|
|
23382
|
-
|
|
23428
|
+
const mr = track && ("wig" === track.type || "merged" === track.type); // wig tracks are potentially multiresolution (e.g. bigwig)
|
|
23429
|
+
this.featureCache = new FeatureCache(chr, bpStart, bpEnd, referenceFrame.bpPerPixel, features, roiFeatures, mr);
|
|
23383
23430
|
this.loading = false;
|
|
23384
23431
|
this.hideMessage();
|
|
23385
23432
|
this.stopSpinner();
|
|
23386
|
-
return this.
|
|
23433
|
+
return this.featureCache
|
|
23387
23434
|
} catch (error) {
|
|
23388
23435
|
// Track might have been removed during load
|
|
23389
23436
|
if (this.trackView && this.trackView.disposed !== true) {
|
|
@@ -23397,32 +23444,29 @@ class TrackViewport extends Viewport {
|
|
|
23397
23444
|
}
|
|
23398
23445
|
}
|
|
23399
23446
|
|
|
23400
|
-
|
|
23447
|
+
/**
|
|
23448
|
+
* Repaint the canvas using the cached features
|
|
23449
|
+
*
|
|
23450
|
+
*/
|
|
23451
|
+
repaint() {
|
|
23401
23452
|
|
|
23402
|
-
if (undefined === this.
|
|
23453
|
+
if (undefined === this.featureCache) {
|
|
23403
23454
|
return
|
|
23404
23455
|
}
|
|
23405
23456
|
|
|
23406
|
-
let {features, roiFeatures
|
|
23457
|
+
let {features, roiFeatures} = this.featureCache;
|
|
23458
|
+
//this.tile.bpPerPixel = this.referenceFrame.bpPerPixel
|
|
23407
23459
|
|
|
23408
23460
|
// const isWGV = GenomeUtils.isWholeGenomeView(this.browser.referenceFrameList[0].chr)
|
|
23409
23461
|
const isWGV = GenomeUtils.isWholeGenomeView(this.referenceFrame.chr);
|
|
23410
|
-
let pixelWidth;
|
|
23411
|
-
|
|
23412
|
-
if (isWGV) {
|
|
23413
|
-
bpPerPixel = this.referenceFrame.end / this.$viewport.width();
|
|
23414
|
-
startBP = 0;
|
|
23415
|
-
endBP = this.referenceFrame.end;
|
|
23416
|
-
pixelWidth = this.$viewport.width();
|
|
23417
|
-
} else {
|
|
23418
|
-
pixelWidth = Math.ceil((endBP - startBP) / bpPerPixel);
|
|
23419
|
-
}
|
|
23420
23462
|
|
|
23463
|
+
// Canvas dimensions. There is no left-right panning for WGV so canvas width is viewport width.
|
|
23421
23464
|
// For deep tracks we paint a canvas == 3*viewportHeight centered on the current vertical scroll position
|
|
23465
|
+
const pixelWidth = isWGV ? this.$viewport.width() : 3 * this.$viewport.width();
|
|
23422
23466
|
const viewportHeight = this.$viewport.height();
|
|
23423
23467
|
const contentHeight = this.getContentHeight();
|
|
23424
23468
|
const minHeight = roiFeatures ? Math.max(contentHeight, viewportHeight) : contentHeight; // Need to fill viewport for ROIs.
|
|
23425
|
-
|
|
23469
|
+
const pixelHeight = Math.min(minHeight, 3 * viewportHeight);
|
|
23426
23470
|
if (0 === pixelWidth || 0 === pixelHeight) {
|
|
23427
23471
|
if (this.canvas) {
|
|
23428
23472
|
$$1(this.canvas).remove();
|
|
@@ -23431,30 +23475,25 @@ class TrackViewport extends Viewport {
|
|
|
23431
23475
|
}
|
|
23432
23476
|
const canvasTop = Math.max(0, -(this.$content.position().top) - viewportHeight);
|
|
23433
23477
|
|
|
23434
|
-
|
|
23435
|
-
|
|
23436
|
-
|
|
23437
|
-
|
|
23438
|
-
} else {
|
|
23439
|
-
devicePixelRatio = (this.trackView.track.supportHiDPI === false) ? 1 : window.devicePixelRatio;
|
|
23440
|
-
}
|
|
23441
|
-
|
|
23442
|
-
const pixelXOffset = Math.round((startBP - this.referenceFrame.start) / this.referenceFrame.bpPerPixel);
|
|
23478
|
+
const bpPerPixel = this.referenceFrame.bpPerPixel;
|
|
23479
|
+
const startBP = this.referenceFrame.start - (isWGV ? 0 : pixelWidth / 3 * bpPerPixel);
|
|
23480
|
+
const endBP = this.referenceFrame.end + (isWGV ? 0 : pixelWidth / 3 * bpPerPixel);
|
|
23481
|
+
const pixelXOffset = Math.round((startBP - this.referenceFrame.start) / bpPerPixel);
|
|
23443
23482
|
|
|
23444
23483
|
const newCanvas = $$1('<canvas class="igv-canvas">').get(0);
|
|
23445
|
-
const ctx = newCanvas.getContext("2d");
|
|
23446
|
-
|
|
23447
23484
|
newCanvas.style.width = pixelWidth + "px";
|
|
23448
23485
|
newCanvas.style.height = pixelHeight + "px";
|
|
23486
|
+
newCanvas.style.left = pixelXOffset + "px";
|
|
23487
|
+
newCanvas.style.top = canvasTop + "px";
|
|
23449
23488
|
|
|
23489
|
+
// Always use high DPI if in "FILL" display mode, otherwise use track setting;
|
|
23490
|
+
const devicePixelRatio = ("FILL" === this.trackView.track.displayMode || this.trackView.track.supportHiDPI !== false) ?
|
|
23491
|
+
window.devicePixelRatio : 1;
|
|
23450
23492
|
newCanvas.width = devicePixelRatio * pixelWidth;
|
|
23451
23493
|
newCanvas.height = devicePixelRatio * pixelHeight;
|
|
23452
23494
|
|
|
23495
|
+
const ctx = newCanvas.getContext("2d");
|
|
23453
23496
|
ctx.scale(devicePixelRatio, devicePixelRatio);
|
|
23454
|
-
|
|
23455
|
-
newCanvas.style.left = pixelXOffset + "px";
|
|
23456
|
-
newCanvas.style.top = canvasTop + "px";
|
|
23457
|
-
|
|
23458
23497
|
ctx.translate(0, -canvasTop);
|
|
23459
23498
|
|
|
23460
23499
|
const drawConfiguration =
|
|
@@ -23475,17 +23514,27 @@ class TrackViewport extends Viewport {
|
|
|
23475
23514
|
|
|
23476
23515
|
this.draw(drawConfiguration, features, roiFeatures);
|
|
23477
23516
|
|
|
23478
|
-
this.
|
|
23517
|
+
this.featureCache.canvasTop = canvasTop;
|
|
23518
|
+
this.featureCache.height = pixelHeight;
|
|
23479
23519
|
|
|
23480
|
-
if (this
|
|
23481
|
-
this
|
|
23520
|
+
if (this.canvas) {
|
|
23521
|
+
$$1(this.canvas).remove();
|
|
23482
23522
|
}
|
|
23483
|
-
|
|
23484
|
-
|
|
23523
|
+
newCanvas._data = {
|
|
23524
|
+
chr: this.featureCache.chr, bpPerPixel, startBP, endBP, pixelHeight, pixelTop: canvasTop
|
|
23525
|
+
};
|
|
23485
23526
|
this.canvas = newCanvas;
|
|
23486
|
-
this.
|
|
23527
|
+
this.$content.append($$1(newCanvas));
|
|
23528
|
+
|
|
23487
23529
|
}
|
|
23488
23530
|
|
|
23531
|
+
/**
|
|
23532
|
+
* Draw the associated track.
|
|
23533
|
+
*
|
|
23534
|
+
* @param drawConfiguration
|
|
23535
|
+
* @param features
|
|
23536
|
+
* @param roiFeatures
|
|
23537
|
+
*/
|
|
23489
23538
|
draw(drawConfiguration, features, roiFeatures) {
|
|
23490
23539
|
|
|
23491
23540
|
// console.log(`${ Date.now() } viewport draw(). track ${ this.trackView.track.type }. content-css-top ${ this.$content.css('top') }. canvas-top ${ drawConfiguration.pixelTop }.`)
|
|
@@ -23502,60 +23551,6 @@ class TrackViewport extends Viewport {
|
|
|
23502
23551
|
}
|
|
23503
23552
|
}
|
|
23504
23553
|
|
|
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
23554
|
containsPosition(chr, position) {
|
|
23560
23555
|
if (this.referenceFrame.chr === chr && position >= this.referenceFrame.start) {
|
|
23561
23556
|
return position <= this.referenceFrame.calculateEnd(this.getWidth())
|
|
@@ -23568,18 +23563,20 @@ class TrackViewport extends Viewport {
|
|
|
23568
23563
|
return this.loading
|
|
23569
23564
|
}
|
|
23570
23565
|
|
|
23571
|
-
|
|
23566
|
+
savePNG() {
|
|
23572
23567
|
|
|
23573
|
-
if (!this.
|
|
23568
|
+
if (!this.canvas) return
|
|
23574
23569
|
|
|
23575
|
-
const
|
|
23570
|
+
const canvasMetadata = this.featureCache;
|
|
23571
|
+
const canvasTop = canvasMetadata ? canvasMetadata.canvasTop : 0;
|
|
23576
23572
|
const devicePixelRatio = window.devicePixelRatio;
|
|
23577
23573
|
const w = this.$viewport.width() * devicePixelRatio;
|
|
23578
23574
|
const h = this.$viewport.height() * devicePixelRatio;
|
|
23579
23575
|
const x = -$$1(this.canvas).position().left * devicePixelRatio;
|
|
23580
23576
|
const y = (-this.$content.position().top - canvasTop) * devicePixelRatio;
|
|
23581
23577
|
|
|
23582
|
-
const
|
|
23578
|
+
const ctx = this.canvas.getContext("2d");
|
|
23579
|
+
const imageData = ctx.getImageData(x, y, w, h);
|
|
23583
23580
|
const exportCanvas = document.createElement('canvas');
|
|
23584
23581
|
const exportCtx = exportCanvas.getContext('2d');
|
|
23585
23582
|
exportCanvas.width = imageData.width;
|
|
@@ -23702,32 +23699,53 @@ class TrackViewport extends Viewport {
|
|
|
23702
23699
|
selection: this.selection
|
|
23703
23700
|
};
|
|
23704
23701
|
|
|
23705
|
-
const features = this.
|
|
23706
|
-
const roiFeatures = this.
|
|
23702
|
+
const features = this.featureCache ? this.featureCache.features : [];
|
|
23703
|
+
const roiFeatures = this.featureCache ? this.featureCache.roiFeatures : undefined;
|
|
23707
23704
|
this.draw(config, features, roiFeatures);
|
|
23708
23705
|
|
|
23709
23706
|
context.restore();
|
|
23710
23707
|
|
|
23711
23708
|
}
|
|
23712
23709
|
|
|
23713
|
-
|
|
23714
|
-
return this.
|
|
23710
|
+
get cachedFeatures() {
|
|
23711
|
+
return this.featureCache ? this.featureCache.features : []
|
|
23715
23712
|
}
|
|
23716
23713
|
|
|
23717
23714
|
async getFeatures(track, chr, start, end, bpPerPixel) {
|
|
23718
23715
|
|
|
23719
|
-
if (this.
|
|
23720
|
-
return this.
|
|
23716
|
+
if (this.featureCache && this.featureCache.containsRange(chr, start, end, bpPerPixel)) {
|
|
23717
|
+
return this.featureCache.features
|
|
23721
23718
|
} else if (typeof track.getFeatures === "function") {
|
|
23722
23719
|
const features = await track.getFeatures(chr, start, end, bpPerPixel, this);
|
|
23723
|
-
this.
|
|
23724
|
-
this.checkContentHeight();
|
|
23720
|
+
this.checkContentHeight(features);
|
|
23725
23721
|
return features
|
|
23726
23722
|
} else {
|
|
23727
23723
|
return undefined
|
|
23728
23724
|
}
|
|
23729
23725
|
}
|
|
23730
23726
|
|
|
23727
|
+
needsRepaint() {
|
|
23728
|
+
|
|
23729
|
+
if (!this.canvas) return true
|
|
23730
|
+
|
|
23731
|
+
const data = this.canvas._data;
|
|
23732
|
+
return !data ||
|
|
23733
|
+
this.referenceFrame.start < data.startBP ||
|
|
23734
|
+
this.referenceFrame.end > data.endBP ||
|
|
23735
|
+
this.referenceFrame.chr !== data.chr ||
|
|
23736
|
+
this.referenceFrame.bpPerPixel != data.bpPerPixel
|
|
23737
|
+
}
|
|
23738
|
+
|
|
23739
|
+
needsReload() {
|
|
23740
|
+
if (!this.featureCache) return true
|
|
23741
|
+
const referenceFrame = this.referenceFrame;
|
|
23742
|
+
const chr = this.referenceFrame.chr;
|
|
23743
|
+
const start = referenceFrame.start;
|
|
23744
|
+
const end = start + referenceFrame.toBP($$1(this.contentDiv).width());
|
|
23745
|
+
const bpPerPixel = referenceFrame.bpPerPixel;
|
|
23746
|
+
return (!this.featureCache.containsRange(chr, start, end, bpPerPixel))
|
|
23747
|
+
}
|
|
23748
|
+
|
|
23731
23749
|
createZoomInNotice($parent) {
|
|
23732
23750
|
|
|
23733
23751
|
const $container = $$1('<div>', {class: 'igv-zoom-in-notice-container'});
|
|
@@ -23749,15 +23767,33 @@ class TrackViewport extends Viewport {
|
|
|
23749
23767
|
|
|
23750
23768
|
addMouseHandlers() {
|
|
23751
23769
|
|
|
23752
|
-
this
|
|
23770
|
+
const viewport = this.$viewport.get(0);
|
|
23753
23771
|
|
|
23754
|
-
this.
|
|
23772
|
+
this.addViewportContextMenuHandler(viewport);
|
|
23755
23773
|
|
|
23756
|
-
|
|
23757
|
-
|
|
23758
|
-
|
|
23774
|
+
const md = (event) => {
|
|
23775
|
+
this.enableClick = true;
|
|
23776
|
+
this.browser.mouseDownOnViewport(event, this);
|
|
23777
|
+
pageCoordinates$1(event);
|
|
23778
|
+
};
|
|
23779
|
+
viewport.addEventListener('mousedown', md);
|
|
23780
|
+
viewport.addEventListener('touchstart', md);
|
|
23781
|
+
|
|
23782
|
+
const mu = (event) => {
|
|
23783
|
+
// Any mouse up cancels drag and scrolling
|
|
23784
|
+
if (this.browser.dragObject || this.browser.isScrolling) {
|
|
23785
|
+
this.browser.cancelTrackPan();
|
|
23786
|
+
// event.preventDefault();
|
|
23787
|
+
// event.stopPropagation();
|
|
23788
|
+
this.enableClick = false; // Until next mouse down
|
|
23789
|
+
} else {
|
|
23790
|
+
this.browser.cancelTrackPan();
|
|
23791
|
+
this.browser.endTrackDrag();
|
|
23792
|
+
}
|
|
23793
|
+
};
|
|
23759
23794
|
|
|
23760
|
-
|
|
23795
|
+
viewport.addEventListener('mouseup', mu);
|
|
23796
|
+
viewport.addEventListener('touchend', mu);
|
|
23761
23797
|
|
|
23762
23798
|
this.addViewportClickHandler(this.$viewport.get(0));
|
|
23763
23799
|
|
|
@@ -23767,32 +23803,9 @@ class TrackViewport extends Viewport {
|
|
|
23767
23803
|
|
|
23768
23804
|
}
|
|
23769
23805
|
|
|
23770
|
-
removeMouseHandlers() {
|
|
23771
|
-
|
|
23772
|
-
this.removeViewportContextMenuHandler(this.$viewport.get(0));
|
|
23773
|
-
|
|
23774
|
-
this.removeViewportMouseDownHandler(this.$viewport.get(0));
|
|
23775
|
-
|
|
23776
|
-
this.removeViewportTouchStartHandler(this.$viewport.get(0));
|
|
23777
|
-
|
|
23778
|
-
this.removeViewportMouseUpHandler(this.$viewport.get(0));
|
|
23779
|
-
|
|
23780
|
-
this.removeViewportTouchEndHandler(this.$viewport.get(0));
|
|
23781
|
-
|
|
23782
|
-
this.removeViewportClickHandler(this.$viewport.get(0));
|
|
23783
|
-
|
|
23784
|
-
if (this.trackView.track.name && "sequence" !== this.trackView.track.config.type) {
|
|
23785
|
-
this.removeTrackLabelClickHandler(this.$trackLabel.get(0));
|
|
23786
|
-
}
|
|
23787
|
-
|
|
23788
|
-
}
|
|
23789
|
-
|
|
23790
23806
|
addViewportContextMenuHandler(viewport) {
|
|
23791
23807
|
|
|
23792
|
-
|
|
23793
|
-
viewport.addEventListener('contextmenu', this.boundContextMenuHandler);
|
|
23794
|
-
|
|
23795
|
-
function contextMenuHandler(event) {
|
|
23808
|
+
viewport.addEventListener('contextmenu', (event) => {
|
|
23796
23809
|
|
|
23797
23810
|
// Ignore if we are doing a drag. This can happen with touch events.
|
|
23798
23811
|
if (this.browser.dragObject) {
|
|
@@ -23825,56 +23838,14 @@ class TrackViewport extends Viewport {
|
|
|
23825
23838
|
menuItems.push({label: 'Save Image (SVG)', click: () => this.saveSVG()});
|
|
23826
23839
|
|
|
23827
23840
|
this.browser.menuPopup.presentTrackContextMenu(event, menuItems);
|
|
23828
|
-
}
|
|
23829
|
-
|
|
23830
|
-
}
|
|
23831
|
-
|
|
23832
|
-
removeViewportContextMenuHandler(viewport) {
|
|
23833
|
-
viewport.removeEventListener('contextmenu', this.boundContextMenuHandler);
|
|
23834
|
-
}
|
|
23835
|
-
|
|
23836
|
-
addViewportMouseDownHandler(viewport) {
|
|
23837
|
-
this.boundMouseDownHandler = mouseDownHandler.bind(this);
|
|
23838
|
-
viewport.addEventListener('mousedown', this.boundMouseDownHandler);
|
|
23839
|
-
}
|
|
23840
|
-
|
|
23841
|
-
removeViewportMouseDownHandler(viewport) {
|
|
23842
|
-
viewport.removeEventListener('mousedown', this.boundMouseDownHandler);
|
|
23843
|
-
}
|
|
23844
|
-
|
|
23845
|
-
addViewportTouchStartHandler(viewport) {
|
|
23846
|
-
this.boundTouchStartHandler = mouseDownHandler.bind(this);
|
|
23847
|
-
viewport.addEventListener('touchstart', this.boundTouchStartHandler);
|
|
23848
|
-
}
|
|
23849
|
-
|
|
23850
|
-
removeViewportTouchStartHandler(viewport) {
|
|
23851
|
-
viewport.removeEventListener('touchstart', this.boundTouchStartHandler);
|
|
23852
|
-
}
|
|
23853
|
-
|
|
23854
|
-
addViewportMouseUpHandler(viewport) {
|
|
23855
|
-
this.boundMouseUpHandler = mouseUpHandler.bind(this);
|
|
23856
|
-
viewport.addEventListener('mouseup', this.boundMouseUpHandler);
|
|
23857
|
-
}
|
|
23841
|
+
});
|
|
23858
23842
|
|
|
23859
|
-
removeViewportMouseUpHandler(viewport) {
|
|
23860
|
-
viewport.removeEventListener('mouseup', this.boundMouseUpHandler);
|
|
23861
23843
|
}
|
|
23862
23844
|
|
|
23863
|
-
addViewportTouchEndHandler(viewport) {
|
|
23864
|
-
this.boundTouchEndHandler = mouseUpHandler.bind(this);
|
|
23865
|
-
viewport.addEventListener('touchend', this.boundTouchEndHandler);
|
|
23866
|
-
}
|
|
23867
|
-
|
|
23868
|
-
removeViewportTouchEndHandler(viewport) {
|
|
23869
|
-
viewport.removeEventListener('touchend', this.boundTouchEndHandler);
|
|
23870
|
-
}
|
|
23871
23845
|
|
|
23872
23846
|
addViewportClickHandler(viewport) {
|
|
23873
23847
|
|
|
23874
|
-
|
|
23875
|
-
viewport.addEventListener('click', this.boundClickHandler);
|
|
23876
|
-
|
|
23877
|
-
function clickHandler(event) {
|
|
23848
|
+
viewport.addEventListener('click', (event) => {
|
|
23878
23849
|
|
|
23879
23850
|
if (this.enableClick) {
|
|
23880
23851
|
if (3 === event.which || event.ctrlKey) {
|
|
@@ -23959,21 +23930,12 @@ class TrackViewport extends Viewport {
|
|
|
23959
23930
|
lastClickTime = time;
|
|
23960
23931
|
|
|
23961
23932
|
}
|
|
23962
|
-
|
|
23963
|
-
}
|
|
23964
|
-
|
|
23965
|
-
}
|
|
23966
|
-
|
|
23967
|
-
removeViewportClickHandler(viewport) {
|
|
23968
|
-
viewport.removeEventListener('click', this.boundClickHandler);
|
|
23933
|
+
});
|
|
23969
23934
|
}
|
|
23970
23935
|
|
|
23971
23936
|
addTrackLabelClickHandler(trackLabel) {
|
|
23972
23937
|
|
|
23973
|
-
|
|
23974
|
-
trackLabel.addEventListener('click', this.boundTrackLabelClickHandler);
|
|
23975
|
-
|
|
23976
|
-
function clickHandler(event) {
|
|
23938
|
+
trackLabel.addEventListener('click', (event) => {
|
|
23977
23939
|
|
|
23978
23940
|
event.stopPropagation();
|
|
23979
23941
|
|
|
@@ -23993,48 +23955,18 @@ class TrackViewport extends Viewport {
|
|
|
23993
23955
|
this.popover = new Popover(this.browser.columnContainer, (track.name || ''));
|
|
23994
23956
|
this.popover.presentContentWithEvent(event, str);
|
|
23995
23957
|
}
|
|
23996
|
-
}
|
|
23997
|
-
}
|
|
23998
|
-
|
|
23999
|
-
removeTrackLabelClickHandler(trackLabel) {
|
|
24000
|
-
trackLabel.removeEventListener('click', this.boundTrackLabelClickHandler);
|
|
23958
|
+
});
|
|
24001
23959
|
}
|
|
24002
23960
|
|
|
24003
23961
|
}
|
|
24004
23962
|
|
|
24005
|
-
function mouseDownHandler(event) {
|
|
24006
|
-
this.enableClick = true;
|
|
24007
|
-
this.browser.mouseDownOnViewport(event, this);
|
|
24008
|
-
pageCoordinates$1(event);
|
|
24009
|
-
}
|
|
24010
|
-
|
|
24011
|
-
function mouseUpHandler(event) {
|
|
24012
|
-
|
|
24013
|
-
// Any mouse up cancels drag and scrolling
|
|
24014
|
-
if (this.browser.dragObject || this.browser.isScrolling) {
|
|
24015
|
-
this.browser.cancelTrackPan();
|
|
24016
|
-
// event.preventDefault();
|
|
24017
|
-
// event.stopPropagation();
|
|
24018
|
-
this.enableClick = false; // Until next mouse down
|
|
24019
|
-
} else {
|
|
24020
|
-
this.browser.cancelTrackPan();
|
|
24021
|
-
this.browser.endTrackDrag();
|
|
24022
|
-
}
|
|
24023
|
-
}
|
|
24024
|
-
|
|
24025
23963
|
function createClickState(event, viewport) {
|
|
24026
23964
|
|
|
24027
23965
|
const referenceFrame = viewport.referenceFrame;
|
|
24028
|
-
|
|
24029
23966
|
const viewportCoords = translateMouseCoordinates$1(event, viewport.contentDiv);
|
|
24030
23967
|
const canvasCoords = translateMouseCoordinates$1(event, viewport.canvas);
|
|
24031
|
-
|
|
24032
23968
|
const genomicLocation = ((referenceFrame.start) + referenceFrame.toBP(viewportCoords.x));
|
|
24033
23969
|
|
|
24034
|
-
if (undefined === genomicLocation || null === viewport.tile) {
|
|
24035
|
-
return undefined
|
|
24036
|
-
}
|
|
24037
|
-
|
|
24038
23970
|
return {
|
|
24039
23971
|
event,
|
|
24040
23972
|
viewport,
|
|
@@ -24095,22 +24027,30 @@ function formatPopoverText(nameValues) {
|
|
|
24095
24027
|
return rows.join('')
|
|
24096
24028
|
}
|
|
24097
24029
|
|
|
24098
|
-
|
|
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
|
-
};
|
|
24030
|
+
class FeatureCache {
|
|
24106
24031
|
|
|
24107
|
-
|
|
24108
|
-
|
|
24109
|
-
|
|
24032
|
+
constructor(chr, tileStart, tileEnd, bpPerPixel, features, roiFeatures, multiresolution) {
|
|
24033
|
+
this.chr = chr;
|
|
24034
|
+
this.startBP = tileStart;
|
|
24035
|
+
this.endBP = tileEnd;
|
|
24036
|
+
this.bpPerPixel = bpPerPixel;
|
|
24037
|
+
this.features = features;
|
|
24038
|
+
this.roiFeatures = roiFeatures;
|
|
24039
|
+
this.multiresolution = multiresolution;
|
|
24040
|
+
}
|
|
24110
24041
|
|
|
24111
|
-
|
|
24112
|
-
|
|
24113
|
-
|
|
24042
|
+
containsRange(chr, start, end, bpPerPixel) {
|
|
24043
|
+
|
|
24044
|
+
// For multi-resolution tracks allow for a 2X change in bpPerPixel
|
|
24045
|
+
const r = this.multiresolution ? this.bpPerPixel / bpPerPixel : 1;
|
|
24046
|
+
|
|
24047
|
+
return start >= this.startBP && end <= this.endBP && chr === this.chr && r > 0.5 && r < 2
|
|
24048
|
+
}
|
|
24049
|
+
|
|
24050
|
+
overlapsRange(chr, start, end) {
|
|
24051
|
+
return this.chr === chr && end >= this.startBP && start <= this.endBP
|
|
24052
|
+
}
|
|
24053
|
+
}
|
|
24114
24054
|
|
|
24115
24055
|
|
|
24116
24056
|
/**
|
|
@@ -24275,11 +24215,13 @@ class RulerSweeper {
|
|
|
24275
24215
|
|
|
24276
24216
|
validateLocusExtent(this.rulerViewport.browser.genome.getChromosome(this.rulerViewport.referenceFrame.chr).bpLength, extent, this.rulerViewport.browser.minimumBases());
|
|
24277
24217
|
|
|
24278
|
-
|
|
24279
|
-
|
|
24280
|
-
this.rulerViewport.referenceFrame.
|
|
24218
|
+
const newStart = Math.round(extent.start);
|
|
24219
|
+
const newEnd = Math.round(extent.end);
|
|
24220
|
+
this.rulerViewport.referenceFrame.bpPerPixel = (newEnd - newStart) / this.rulerViewport.contentDiv.clientWidth;
|
|
24221
|
+
this.rulerViewport.referenceFrame.start = newStart;
|
|
24222
|
+
this.rulerViewport.referenceFrame.end = newEnd;
|
|
24281
24223
|
|
|
24282
|
-
this.rulerViewport.browser.updateViews(
|
|
24224
|
+
this.rulerViewport.browser.updateViews();
|
|
24283
24225
|
}
|
|
24284
24226
|
|
|
24285
24227
|
}
|
|
@@ -24417,6 +24359,18 @@ class PairedAlignment {
|
|
|
24417
24359
|
return true // By definition
|
|
24418
24360
|
}
|
|
24419
24361
|
|
|
24362
|
+
isMateMapped() {
|
|
24363
|
+
return true // By definition
|
|
24364
|
+
}
|
|
24365
|
+
|
|
24366
|
+
isProperPair() {
|
|
24367
|
+
return this.firstAlignment.isProperPair()
|
|
24368
|
+
}
|
|
24369
|
+
|
|
24370
|
+
get fragmentLength() {
|
|
24371
|
+
return Math.abs(this.firstAlignment.fragmentLength)
|
|
24372
|
+
}
|
|
24373
|
+
|
|
24420
24374
|
firstOfPairStrand() {
|
|
24421
24375
|
|
|
24422
24376
|
if (this.firstAlignment.isFirstOfPair()) {
|
|
@@ -24758,7 +24712,10 @@ function packAlignmentRows(alignments, start, end, showSoftClips) {
|
|
|
24758
24712
|
|
|
24759
24713
|
|
|
24760
24714
|
class AlignmentContainer {
|
|
24761
|
-
|
|
24715
|
+
|
|
24716
|
+
// this.config.samplingWindowSize, this.config.samplingDepth,
|
|
24717
|
+
// this.config.pairsSupported, this.config.alleleFreqThreshold)
|
|
24718
|
+
constructor(chr, start, end, {samplingWindowSize, samplingDepth, pairsSupported, alleleFreqThreshold}) {
|
|
24762
24719
|
|
|
24763
24720
|
this.chr = chr;
|
|
24764
24721
|
this.start = Math.floor(start);
|
|
@@ -24786,17 +24743,12 @@ class AlignmentContainer {
|
|
|
24786
24743
|
return alignment.isMapped() && !alignment.isFailsVendorQualityCheck()
|
|
24787
24744
|
};
|
|
24788
24745
|
|
|
24789
|
-
this.pairedEndStats = new PairedEndStats();
|
|
24790
24746
|
}
|
|
24791
24747
|
|
|
24792
24748
|
push(alignment) {
|
|
24793
24749
|
|
|
24794
24750
|
if (this.filter(alignment) === false) return
|
|
24795
24751
|
|
|
24796
|
-
if (alignment.isPaired()) {
|
|
24797
|
-
this.pairedEndStats.push(alignment);
|
|
24798
|
-
}
|
|
24799
|
-
|
|
24800
24752
|
this.coverageMap.incCounts(alignment); // Count coverage before any downsampling
|
|
24801
24753
|
|
|
24802
24754
|
if (this.pairsSupported && this.downsampledReads.has(alignment.readName)) {
|
|
@@ -24828,8 +24780,6 @@ class AlignmentContainer {
|
|
|
24828
24780
|
|
|
24829
24781
|
this.pairsCache = undefined;
|
|
24830
24782
|
this.downsampledReads = undefined;
|
|
24831
|
-
|
|
24832
|
-
this.pairedEndStats.compute();
|
|
24833
24783
|
}
|
|
24834
24784
|
|
|
24835
24785
|
contains(chr, start, end) {
|
|
@@ -25140,69 +25090,6 @@ class DownsampledInterval {
|
|
|
25140
25090
|
}
|
|
25141
25091
|
}
|
|
25142
25092
|
|
|
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
25093
|
class SupplementaryAlignment {
|
|
25207
25094
|
|
|
25208
25095
|
constructor(rec) {
|
|
@@ -25923,7 +25810,7 @@ const BamUtils = {
|
|
|
25923
25810
|
const lseq = readInt(ba, offset + 20);
|
|
25924
25811
|
const mateChrIdx = readInt(ba, offset + 24);
|
|
25925
25812
|
const matePos = readInt(ba, offset + 28);
|
|
25926
|
-
const
|
|
25813
|
+
const fragmentLength = readInt(ba, offset + 32);
|
|
25927
25814
|
|
|
25928
25815
|
let readName = [];
|
|
25929
25816
|
for (let j = 0; j < nl - 1; ++j) {
|
|
@@ -25964,7 +25851,7 @@ const BamUtils = {
|
|
|
25964
25851
|
alignment.readName = readName;
|
|
25965
25852
|
alignment.cigar = cigar;
|
|
25966
25853
|
alignment.lengthOnRef = lengthOnRef;
|
|
25967
|
-
alignment.fragmentLength =
|
|
25854
|
+
alignment.fragmentLength = fragmentLength;
|
|
25968
25855
|
alignment.mq = mq;
|
|
25969
25856
|
|
|
25970
25857
|
BamUtils.bam_tag2cigar(ba, blockEnd, p, lseq, alignment, cigarArray);
|
|
@@ -26392,7 +26279,7 @@ class BamReaderNonIndexed {
|
|
|
26392
26279
|
const header = this.header;
|
|
26393
26280
|
const queryChr = header.chrAliasTable.hasOwnProperty(chr) ? header.chrAliasTable[chr] : chr;
|
|
26394
26281
|
const qAlignments = this.alignmentCache.queryFeatures(queryChr, bpStart, bpEnd);
|
|
26395
|
-
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.
|
|
26282
|
+
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config);
|
|
26396
26283
|
for (let a of qAlignments) {
|
|
26397
26284
|
alignmentContainer.push(a);
|
|
26398
26285
|
}
|
|
@@ -26419,13 +26306,13 @@ class BamReaderNonIndexed {
|
|
|
26419
26306
|
const alignments = [];
|
|
26420
26307
|
this.header = BamUtils.decodeBamHeader(data);
|
|
26421
26308
|
BamUtils.decodeBamRecords(data, this.header.size, alignments, this.header.chrNames);
|
|
26422
|
-
this.alignmentCache = new FeatureCache(alignments, this.genome);
|
|
26309
|
+
this.alignmentCache = new FeatureCache$1(alignments, this.genome);
|
|
26423
26310
|
}
|
|
26424
26311
|
|
|
26425
26312
|
fetchAlignments(chr, bpStart, bpEnd) {
|
|
26426
26313
|
const queryChr = this.header.chrAliasTable.hasOwnProperty(chr) ? this.header.chrAliasTable[chr] : chr;
|
|
26427
26314
|
const features = this.alignmentCache.queryFeatures(queryChr, bpStart, bpEnd);
|
|
26428
|
-
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.
|
|
26315
|
+
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config);
|
|
26429
26316
|
for (let feature of features) {
|
|
26430
26317
|
alignmentContainer.push(feature);
|
|
26431
26318
|
}
|
|
@@ -27416,9 +27303,7 @@ class BamReader {
|
|
|
27416
27303
|
const chrToIndex = await this.getChrIndex();
|
|
27417
27304
|
const queryChr = this.chrAliasTable.hasOwnProperty(chr) ? this.chrAliasTable[chr] : chr;
|
|
27418
27305
|
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);
|
|
27306
|
+
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config);
|
|
27422
27307
|
|
|
27423
27308
|
if (chrId === undefined) {
|
|
27424
27309
|
return alignmentContainer
|
|
@@ -27550,7 +27435,7 @@ class ShardedBamReader {
|
|
|
27550
27435
|
async readAlignments(chr, start, end) {
|
|
27551
27436
|
|
|
27552
27437
|
if (!this.bamReaders.hasOwnProperty(chr)) {
|
|
27553
|
-
return new AlignmentContainer(chr, start, end)
|
|
27438
|
+
return new AlignmentContainer(chr, start, end, this.config)
|
|
27554
27439
|
} else {
|
|
27555
27440
|
|
|
27556
27441
|
let reader = this.bamReaders[chr];
|
|
@@ -27641,7 +27526,7 @@ BamWebserviceReader.prototype.readAlignments = function (chr, bpStart, bpEnd) {
|
|
|
27641
27526
|
|
|
27642
27527
|
header.chrToIndex[queryChr];
|
|
27643
27528
|
|
|
27644
|
-
alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, self.
|
|
27529
|
+
alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, self.config);
|
|
27645
27530
|
|
|
27646
27531
|
BamUtils.decodeSamRecords(sam, alignmentContainer, queryChr, bpStart, bpEnd, self.filter);
|
|
27647
27532
|
|
|
@@ -27895,7 +27780,7 @@ class HtsgetBamReader extends HtsgetReader {
|
|
|
27895
27780
|
const ba = unbgzf(compressedData.buffer);
|
|
27896
27781
|
|
|
27897
27782
|
const chrIdx = this.header.chrToIndex[chr];
|
|
27898
|
-
const alignmentContainer = new AlignmentContainer(chr, start, end, this.
|
|
27783
|
+
const alignmentContainer = new AlignmentContainer(chr, start, end, this.config);
|
|
27899
27784
|
BamUtils.decodeBamRecords(ba, this.header.size, alignmentContainer, this.header.chrNames, chrIdx, start, end);
|
|
27900
27785
|
alignmentContainer.finish();
|
|
27901
27786
|
|
|
@@ -28061,8 +27946,7 @@ class CramReader {
|
|
|
28061
27946
|
const header = await this.getHeader();
|
|
28062
27947
|
const queryChr = header.chrAliasTable.hasOwnProperty(chr) ? header.chrAliasTable[chr] : chr;
|
|
28063
27948
|
const chrIdx = header.chrToIndex[queryChr];
|
|
28064
|
-
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd,
|
|
28065
|
-
this.samplingWindowSize, this.samplingDepth, this.pairsSupported, this.alleleFreqThreshold);
|
|
27949
|
+
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config);
|
|
28066
27950
|
|
|
28067
27951
|
if (chrIdx === undefined) {
|
|
28068
27952
|
return alignmentContainer
|
|
@@ -28678,7 +28562,7 @@ Ga4ghAlignmentReader.prototype.readAlignments = function (chr, bpStart, bpEnd) {
|
|
|
28678
28562
|
"pageSize": "10000"
|
|
28679
28563
|
},
|
|
28680
28564
|
decode: decodeGa4ghReads,
|
|
28681
|
-
results: new AlignmentContainer(chr, bpStart, bpEnd, self.
|
|
28565
|
+
results: new AlignmentContainer(chr, bpStart, bpEnd, self.config)
|
|
28682
28566
|
})
|
|
28683
28567
|
})
|
|
28684
28568
|
|
|
@@ -29011,7 +28895,6 @@ class BamSource {
|
|
|
29011
28895
|
|
|
29012
28896
|
this.config = config;
|
|
29013
28897
|
this.genome = genome;
|
|
29014
|
-
this.alignmentContainer = undefined;
|
|
29015
28898
|
|
|
29016
28899
|
if (isDataURL(config.url)) {
|
|
29017
28900
|
if ("cram" === config.format) {
|
|
@@ -29059,19 +28942,11 @@ class BamSource {
|
|
|
29059
28942
|
}
|
|
29060
28943
|
|
|
29061
28944
|
setViewAsPairs(bool) {
|
|
29062
|
-
|
|
29063
|
-
if (this.viewAsPairs !== bool) {
|
|
29064
|
-
this.viewAsPairs = bool;
|
|
29065
|
-
// if (this.alignmentContainer) {
|
|
29066
|
-
// this.alignmentContainer.setViewAsPairs(bool);
|
|
29067
|
-
// }
|
|
29068
|
-
}
|
|
28945
|
+
this.viewAsPairs = bool;
|
|
29069
28946
|
}
|
|
29070
28947
|
|
|
29071
28948
|
setShowSoftClips(bool) {
|
|
29072
|
-
|
|
29073
|
-
this.showSoftClips = bool;
|
|
29074
|
-
}
|
|
28949
|
+
this.showSoftClips = bool;
|
|
29075
28950
|
}
|
|
29076
28951
|
|
|
29077
28952
|
async getAlignments(chr, bpStart, bpEnd) {
|
|
@@ -29079,33 +28954,28 @@ class BamSource {
|
|
|
29079
28954
|
const genome = this.genome;
|
|
29080
28955
|
const showSoftClips = this.showSoftClips;
|
|
29081
28956
|
|
|
29082
|
-
|
|
29083
|
-
|
|
28957
|
+
const alignmentContainer = await this.bamReader.readAlignments(chr, bpStart, bpEnd);
|
|
28958
|
+
let alignments = alignmentContainer.alignments;
|
|
28959
|
+
if (!this.viewAsPairs) {
|
|
28960
|
+
alignments = unpairAlignments([{alignments: alignments}]);
|
|
28961
|
+
}
|
|
28962
|
+
const hasAlignments = alignments.length > 0;
|
|
28963
|
+
alignmentContainer.packedAlignmentRows = packAlignmentRows(alignments, alignmentContainer.start, alignmentContainer.end, showSoftClips);
|
|
29084
28964
|
|
|
29085
|
-
|
|
29086
|
-
|
|
29087
|
-
|
|
29088
|
-
|
|
29089
|
-
|
|
29090
|
-
|
|
29091
|
-
|
|
29092
|
-
|
|
29093
|
-
|
|
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
|
-
}
|
|
28965
|
+
this.alignmentContainer = alignmentContainer;
|
|
28966
|
+
|
|
28967
|
+
if (hasAlignments) {
|
|
28968
|
+
const sequence = await genome.sequence.getSequence(chr, alignmentContainer.start, alignmentContainer.end);
|
|
28969
|
+
if (sequence) {
|
|
28970
|
+
alignmentContainer.coverageMap.refSeq = sequence; // TODO -- fix this
|
|
28971
|
+
alignmentContainer.sequence = sequence; // TODO -- fix this
|
|
28972
|
+
return alignmentContainer
|
|
28973
|
+
} else {
|
|
28974
|
+
console.error("No sequence for: " + chr + ":" + alignmentContainer.start + "-" + alignmentContainer.end);
|
|
29106
28975
|
}
|
|
29107
|
-
return alignmentContainer
|
|
29108
28976
|
}
|
|
28977
|
+
return alignmentContainer
|
|
28978
|
+
|
|
29109
28979
|
}
|
|
29110
28980
|
}
|
|
29111
28981
|
|
|
@@ -29285,7 +29155,7 @@ class TrackBase {
|
|
|
29285
29155
|
return state
|
|
29286
29156
|
}
|
|
29287
29157
|
|
|
29288
|
-
supportsWholeGenome() {
|
|
29158
|
+
get supportsWholeGenome() {
|
|
29289
29159
|
return false
|
|
29290
29160
|
}
|
|
29291
29161
|
|
|
@@ -29423,7 +29293,7 @@ class TrackBase {
|
|
|
29423
29293
|
|
|
29424
29294
|
// We use the cached features rather than method to avoid async load. If the
|
|
29425
29295
|
// 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.
|
|
29296
|
+
if (!features) features = clickState.viewport.cachedFeatures;
|
|
29427
29297
|
|
|
29428
29298
|
if (!features || features.length === 0) {
|
|
29429
29299
|
return []
|
|
@@ -29927,8 +29797,7 @@ class Locus {
|
|
|
29927
29797
|
}
|
|
29928
29798
|
|
|
29929
29799
|
overlaps(locus) {
|
|
29930
|
-
return locus.chr === this.chr
|
|
29931
|
-
&& !(locus.end < this.start || locus.start > this.end)
|
|
29800
|
+
return locus.chr === this.chr && !(locus.end < this.start || locus.start > this.end)
|
|
29932
29801
|
}
|
|
29933
29802
|
|
|
29934
29803
|
extend(l) {
|
|
@@ -31805,6 +31674,22 @@ function makeCircViewChromosomes(genome) {
|
|
|
31805
31674
|
return regions
|
|
31806
31675
|
}
|
|
31807
31676
|
|
|
31677
|
+
function sendChords(chords, track, refFrame, alpha) {
|
|
31678
|
+
|
|
31679
|
+
const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? track.color : getChrColor(refFrame.chr), alpha);
|
|
31680
|
+
const trackColor = IGVColor.addAlpha(track.color || 'rgb(0,0,255)', alpha);
|
|
31681
|
+
|
|
31682
|
+
// name the chord set to include locus and filtering information
|
|
31683
|
+
const encodedName = track.name.replaceAll(' ', '%20');
|
|
31684
|
+
const chordSetName = "all" === refFrame.chr ? encodedName :
|
|
31685
|
+
`${encodedName} ${refFrame.chr}:${refFrame.start}-${refFrame.end}`;
|
|
31686
|
+
track.browser.circularView.addChords(chords, {track: chordSetName, color: chordSetColor, trackColor: trackColor});
|
|
31687
|
+
|
|
31688
|
+
// show circular view if hidden
|
|
31689
|
+
if(!track.browser.circularViewVisible) track.browser.circularViewVisible = true;
|
|
31690
|
+
|
|
31691
|
+
}
|
|
31692
|
+
|
|
31808
31693
|
|
|
31809
31694
|
function createCircularView(el, browser) {
|
|
31810
31695
|
|
|
@@ -31814,42 +31699,105 @@ function createCircularView(el, browser) {
|
|
|
31814
31699
|
|
|
31815
31700
|
const f1 = feature.data;
|
|
31816
31701
|
const f2 = f1.mate;
|
|
31817
|
-
|
|
31702
|
+
addFrameForFeature(f1);
|
|
31703
|
+
addFrameForFeature(f2);
|
|
31818
31704
|
|
|
31819
|
-
|
|
31820
|
-
const l2 = new Locus({chr: browser.genome.getChromosomeName(f2.refName), start: f2.start, end: f2.end});
|
|
31705
|
+
function addFrameForFeature(feature) {
|
|
31821
31706
|
|
|
31822
|
-
|
|
31707
|
+
feature.chr = browser.genome.getChromosomeName(feature.refName);
|
|
31708
|
+
let frameFound = false;
|
|
31709
|
+
for (let referenceFrame of browser.referenceFrameList) {
|
|
31710
|
+
const l = Locus.fromLocusString(referenceFrame.getLocusString());
|
|
31711
|
+
if (l.contains(feature)) {
|
|
31712
|
+
frameFound = true;
|
|
31713
|
+
break
|
|
31714
|
+
} else if (l.overlaps(feature)) {
|
|
31715
|
+
referenceFrame.extend(feature);
|
|
31716
|
+
frameFound = true;
|
|
31717
|
+
break
|
|
31718
|
+
}
|
|
31719
|
+
}
|
|
31720
|
+
if (!frameFound) {
|
|
31721
|
+
const flanking = 2000;
|
|
31722
|
+
const center = (feature.start + feature.end) / 2;
|
|
31723
|
+
browser.addMultiLocusPanel(feature.chr, center - flanking, center + flanking);
|
|
31823
31724
|
|
|
31824
|
-
|
|
31725
|
+
}
|
|
31726
|
+
}
|
|
31727
|
+
}
|
|
31728
|
+
});
|
|
31825
31729
|
|
|
31826
|
-
|
|
31730
|
+
return circularView
|
|
31731
|
+
}
|
|
31827
31732
|
|
|
31828
|
-
|
|
31829
|
-
|
|
31830
|
-
|
|
31831
|
-
|
|
31832
|
-
|
|
31833
|
-
|
|
31834
|
-
|
|
31835
|
-
|
|
31836
|
-
|
|
31733
|
+
class PairedEndStats {
|
|
31734
|
+
|
|
31735
|
+
constructor(alignments, {minTLENPercentile, maxTLENPercentile}) {
|
|
31736
|
+
this.totalCount = 0;
|
|
31737
|
+
this.frCount = 0;
|
|
31738
|
+
this.rfCount = 0;
|
|
31739
|
+
this.ffCount = 0;
|
|
31740
|
+
this.sumF = 0;
|
|
31741
|
+
this.sumF2 = 0;
|
|
31742
|
+
this.lp = minTLENPercentile === undefined ? 0.1 : minTLENPercentile;
|
|
31743
|
+
this.up = maxTLENPercentile === undefined ? 99.5 : maxTLENPercentile;
|
|
31744
|
+
this.isizes = [];
|
|
31745
|
+
this.compute(alignments);
|
|
31746
|
+
}
|
|
31747
|
+
|
|
31748
|
+
compute(alignments) {
|
|
31749
|
+
|
|
31750
|
+
for (let alignment of alignments) {
|
|
31751
|
+
if (alignment.isProperPair()) {
|
|
31752
|
+
var tlen = Math.abs(alignment.fragmentLength);
|
|
31753
|
+
this.sumF += tlen;
|
|
31754
|
+
this.sumF2 += tlen * tlen;
|
|
31755
|
+
this.isizes.push(tlen);
|
|
31756
|
+
|
|
31757
|
+
var po = alignment.pairOrientation;
|
|
31758
|
+
|
|
31759
|
+
if (typeof po === "string" && po.length === 4) {
|
|
31760
|
+
var tmp = '' + po.charAt(0) + po.charAt(2);
|
|
31761
|
+
switch (tmp) {
|
|
31762
|
+
case 'FF':
|
|
31763
|
+
case 'RR':
|
|
31764
|
+
this.ffCount++;
|
|
31765
|
+
break
|
|
31766
|
+
case "FR":
|
|
31767
|
+
this.frCount++;
|
|
31768
|
+
break
|
|
31769
|
+
case"RF":
|
|
31770
|
+
this.rfCount++;
|
|
31837
31771
|
}
|
|
31838
31772
|
}
|
|
31839
|
-
|
|
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];
|
|
31773
|
+
this.totalCount++;
|
|
31845
31774
|
}
|
|
31846
|
-
|
|
31847
|
-
const searchString = loci.map(l => l.getLocusString()).join(" ");
|
|
31848
|
-
browser.search(searchString);
|
|
31849
31775
|
}
|
|
31776
|
+
|
|
31777
|
+
if (this.ffCount / this.totalCount > 0.9) this.orienation = "ff";
|
|
31778
|
+
else if (this.frCount / this.totalCount > 0.9) this.orienation = "fr";
|
|
31779
|
+
else if (this.rfCount / this.totalCount > 0.9) this.orienation = "rf";
|
|
31780
|
+
|
|
31781
|
+
this.minTLEN = this.lp === 0 ? 0 : percentile(this.isizes, this.lp);
|
|
31782
|
+
this.maxTLEN = percentile(this.isizes, this.up);
|
|
31783
|
+
|
|
31784
|
+
// var fMean = this.sumF / this.totalCount
|
|
31785
|
+
// var stdDev = Math.sqrt((this.totalCount * this.sumF2 - this.sumF * this.sumF) / (this.totalCount * this.totalCount))
|
|
31786
|
+
// this.minTLEN = fMean - 3 * stdDev
|
|
31787
|
+
// this.maxTLEN = fMean + 3 * stdDev
|
|
31788
|
+
|
|
31789
|
+
}
|
|
31790
|
+
}
|
|
31791
|
+
|
|
31792
|
+
function percentile(array, p) {
|
|
31793
|
+
|
|
31794
|
+
if (array.length === 0) return undefined
|
|
31795
|
+
var k = Math.floor(array.length * (p / 100));
|
|
31796
|
+
array.sort(function (a, b) {
|
|
31797
|
+
return a - b
|
|
31850
31798
|
});
|
|
31799
|
+
return array[k]
|
|
31851
31800
|
|
|
31852
|
-
return circularView
|
|
31853
31801
|
}
|
|
31854
31802
|
|
|
31855
31803
|
/*
|
|
@@ -31916,8 +31864,6 @@ class BAMTrack extends TrackBase {
|
|
|
31916
31864
|
this.showMismatches = false !== config.showMismatches;
|
|
31917
31865
|
this.color = config.color;
|
|
31918
31866
|
this.coverageColor = config.coverageColor;
|
|
31919
|
-
this.minFragmentLength = config.minFragmentLength; // Optional, might be undefined
|
|
31920
|
-
this.maxFragmentLength = config.maxFragmentLength || 1000;
|
|
31921
31867
|
|
|
31922
31868
|
// The sort object can be an array in the case of multi-locus view, however if multiple sort positions
|
|
31923
31869
|
// are present for a given reference frame the last one will take precedence
|
|
@@ -31945,12 +31891,24 @@ class BAMTrack extends TrackBase {
|
|
|
31945
31891
|
return this._height
|
|
31946
31892
|
}
|
|
31947
31893
|
|
|
31894
|
+
get minTemplateLength() {
|
|
31895
|
+
const configMinTLEN = this.config.minTLEN !== undefined ? this.config.minTLEN : this.config.minFragmentLength;
|
|
31896
|
+
return (configMinTLEN !== undefined) ? configMinTLEN :
|
|
31897
|
+
this._pairedEndStats ? this._pairedEndStats.minTLEN : 0
|
|
31898
|
+
}
|
|
31899
|
+
|
|
31900
|
+
get maxTemplateLength() {
|
|
31901
|
+
const configMaxTLEN = this.config.maxTLEN !== undefined ? this.config.maxTLEN : this.config.maxFragmentLength;
|
|
31902
|
+
return (configMaxTLEN !== undefined) ? configMaxTLEN :
|
|
31903
|
+
this._pairedEndStats ? this._pairedEndStats.maxTLEN : 1000
|
|
31904
|
+
}
|
|
31905
|
+
|
|
31948
31906
|
sort(options) {
|
|
31949
31907
|
options = this.assignSort(options);
|
|
31950
31908
|
|
|
31951
31909
|
for (let vp of this.trackView.viewports) {
|
|
31952
31910
|
if (vp.containsPosition(options.chr, options.position)) {
|
|
31953
|
-
const alignmentContainer = vp.
|
|
31911
|
+
const alignmentContainer = vp.cachedFeatures;
|
|
31954
31912
|
if (alignmentContainer) {
|
|
31955
31913
|
sortAlignmentRows(options, alignmentContainer);
|
|
31956
31914
|
vp.repaint();
|
|
@@ -31985,14 +31943,13 @@ class BAMTrack extends TrackBase {
|
|
|
31985
31943
|
|
|
31986
31944
|
const alignmentContainer = await this.featureSource.getAlignments(chr, bpStart, bpEnd);
|
|
31987
31945
|
|
|
31988
|
-
if (alignmentContainer.
|
|
31989
|
-
|
|
31990
|
-
|
|
31991
|
-
|
|
31992
|
-
if (undefined === this.maxFragmentLength) {
|
|
31993
|
-
this.maxFragmentLength = alignmentContainer.pairedEndStats.upperFragmentLength;
|
|
31946
|
+
if (alignmentContainer.paired && !this._pairedEndStats && !this.config.maxFragmentLength) {
|
|
31947
|
+
const pairedEndStats = new PairedEndStats(alignmentContainer.alignments, this.config);
|
|
31948
|
+
if (pairedEndStats.totalCount > 99) {
|
|
31949
|
+
this._pairedEndStats = pairedEndStats;
|
|
31994
31950
|
}
|
|
31995
31951
|
}
|
|
31952
|
+
alignmentContainer.alignments = undefined; // Don't need to hold onto these anymore
|
|
31996
31953
|
|
|
31997
31954
|
const sort = this.sortObject;
|
|
31998
31955
|
if (sort) {
|
|
@@ -32089,7 +32046,7 @@ class BAMTrack extends TrackBase {
|
|
|
32089
32046
|
if (this.alignmentTrack.hasPairs) {
|
|
32090
32047
|
colorByMenuItems.push({key: 'firstOfPairStrand', label: 'first-of-pair strand'});
|
|
32091
32048
|
colorByMenuItems.push({key: 'pairOrientation', label: 'pair orientation'});
|
|
32092
|
-
colorByMenuItems.push({key: '
|
|
32049
|
+
colorByMenuItems.push({key: 'tlen', label: 'insert size (TLEN)'});
|
|
32093
32050
|
colorByMenuItems.push({key: 'unexpectedPair', label: 'pair orientation & insert size (TLEN)'});
|
|
32094
32051
|
}
|
|
32095
32052
|
const tagLabel = 'tag' + (this.alignmentTrack.colorByTag ? ' (' + this.alignmentTrack.colorByTag + ')' : '');
|
|
@@ -32195,32 +32152,17 @@ class BAMTrack extends TrackBase {
|
|
|
32195
32152
|
});
|
|
32196
32153
|
}
|
|
32197
32154
|
|
|
32198
|
-
//
|
|
32199
|
-
if (this.browser.circularView &&
|
|
32155
|
+
// Add chords to JBrowse circular view, if present
|
|
32156
|
+
if (this.browser.circularView &&
|
|
32200
32157
|
(this.alignmentTrack.hasPairs || this.alignmentTrack.hasSupplemental)) {
|
|
32201
32158
|
menuItems.push('<hr/>');
|
|
32202
32159
|
if (this.alignmentTrack.hasPairs) {
|
|
32203
32160
|
menuItems.push({
|
|
32204
32161
|
label: 'Add discordant pairs to circular view',
|
|
32205
32162
|
click: () => {
|
|
32206
|
-
const maxFragmentLength = this.maxFragmentLength;
|
|
32207
|
-
const inView = [];
|
|
32208
32163
|
for (let viewport of this.trackView.viewports) {
|
|
32209
|
-
|
|
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
|
-
}
|
|
32164
|
+
this.addPairedChordsForViewport(viewport);
|
|
32219
32165
|
}
|
|
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
32166
|
}
|
|
32225
32167
|
});
|
|
32226
32168
|
}
|
|
@@ -32228,19 +32170,9 @@ class BAMTrack extends TrackBase {
|
|
|
32228
32170
|
menuItems.push({
|
|
32229
32171
|
label: 'Add split reads to circular view',
|
|
32230
32172
|
click: () => {
|
|
32231
|
-
const inView = [];
|
|
32232
32173
|
for (let viewport of this.trackView.viewports) {
|
|
32233
|
-
|
|
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
|
-
}
|
|
32174
|
+
this.addSplitChordsForViewport(viewport);
|
|
32240
32175
|
}
|
|
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
32176
|
}
|
|
32245
32177
|
});
|
|
32246
32178
|
}
|
|
@@ -32352,7 +32284,7 @@ class BAMTrack extends TrackBase {
|
|
|
32352
32284
|
}
|
|
32353
32285
|
|
|
32354
32286
|
getCachedAlignmentContainers() {
|
|
32355
|
-
return this.trackView.viewports.map(vp => vp.
|
|
32287
|
+
return this.trackView.viewports.map(vp => vp.cachedFeatures)
|
|
32356
32288
|
}
|
|
32357
32289
|
|
|
32358
32290
|
get dataRange() {
|
|
@@ -32378,6 +32310,62 @@ class BAMTrack extends TrackBase {
|
|
|
32378
32310
|
set autoscale(autoscale) {
|
|
32379
32311
|
this.coverageTrack.autoscale = autoscale;
|
|
32380
32312
|
}
|
|
32313
|
+
|
|
32314
|
+
/**
|
|
32315
|
+
* Add chords to the circular view for the given viewport, represented by its reference frame
|
|
32316
|
+
* @param refFrame
|
|
32317
|
+
*/
|
|
32318
|
+
addPairedChordsForViewport(viewport) {
|
|
32319
|
+
|
|
32320
|
+
const maxTemplateLength = this.maxTemplateLength;
|
|
32321
|
+
const inView = [];
|
|
32322
|
+
const refFrame = viewport.referenceFrame;
|
|
32323
|
+
for (let a of viewport.cachedFeatures.allAlignments()) {
|
|
32324
|
+
if (a.end >= refFrame.start
|
|
32325
|
+
&& a.start <= refFrame.end
|
|
32326
|
+
&& a.mate
|
|
32327
|
+
&& a.mate.chr
|
|
32328
|
+
&& (a.mate.chr !== a.chr || Math.max(a.fragmentLength) > maxTemplateLength)) {
|
|
32329
|
+
inView.push(a);
|
|
32330
|
+
}
|
|
32331
|
+
}
|
|
32332
|
+
const chords = makePairedAlignmentChords(inView);
|
|
32333
|
+
sendChords(chords, this, refFrame, 0.02);
|
|
32334
|
+
|
|
32335
|
+
// const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? this.color : getChrColor(refFrame.chr), 0.02)
|
|
32336
|
+
// const trackColor = IGVColor.addAlpha(this.color || 'rgb(0,0,255)', 0.02)
|
|
32337
|
+
//
|
|
32338
|
+
// // name the chord set to include track name and locus
|
|
32339
|
+
// const encodedName = this.name.replaceAll(' ', '%20')
|
|
32340
|
+
// const chordSetName = "all" === refFrame.chr ? encodedName :
|
|
32341
|
+
// `${encodedName} (${refFrame.chr}:${refFrame.start}-${refFrame.end}`
|
|
32342
|
+
// this.browser.circularView.addChords(chords, {name: chordSetName, color: chordSetColor, trackColor: trackColor})
|
|
32343
|
+
}
|
|
32344
|
+
|
|
32345
|
+
addSplitChordsForViewport(viewport) {
|
|
32346
|
+
|
|
32347
|
+
const inView = [];
|
|
32348
|
+
const refFrame = viewport.referenceFrame;
|
|
32349
|
+
for (let a of viewport.cachedFeatures.allAlignments()) {
|
|
32350
|
+
|
|
32351
|
+
const sa = a.hasTag('SA');
|
|
32352
|
+
if (a.end >= refFrame.start && a.start <= refFrame.end && sa) {
|
|
32353
|
+
inView.push(a);
|
|
32354
|
+
}
|
|
32355
|
+
}
|
|
32356
|
+
|
|
32357
|
+
const chords = makeSupplementalAlignmentChords(inView);
|
|
32358
|
+
sendChords(chords, this, refFrame, 0.02);
|
|
32359
|
+
|
|
32360
|
+
// const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? this.color : getChrColor(refFrame.chr), 0.02)
|
|
32361
|
+
// const trackColor = IGVColor.addAlpha(this.color || 'rgb(0,0,255)', 0.02)
|
|
32362
|
+
//
|
|
32363
|
+
// // name the chord set to include track name and locus
|
|
32364
|
+
// const encodedName = this.name.replaceAll(' ', '%20')
|
|
32365
|
+
// const chordSetName = "all" === refFrame.chr ? encodedName :
|
|
32366
|
+
// `${encodedName} (${refFrame.chr}:${refFrame.start}-${refFrame.end}`
|
|
32367
|
+
// this.browser.circularView.addChords(chords, {name: chordSetName, color: chordSetColor, trackColor: trackColor})
|
|
32368
|
+
}
|
|
32381
32369
|
}
|
|
32382
32370
|
|
|
32383
32371
|
|
|
@@ -32498,7 +32486,7 @@ class CoverageTrack {
|
|
|
32498
32486
|
|
|
32499
32487
|
getClickedObject(clickState) {
|
|
32500
32488
|
|
|
32501
|
-
let features = clickState.viewport.
|
|
32489
|
+
let features = clickState.viewport.cachedFeatures;
|
|
32502
32490
|
if (!features || features.length === 0) return
|
|
32503
32491
|
|
|
32504
32492
|
const genomicLocation = Math.floor(clickState.genomicLocation);
|
|
@@ -32574,8 +32562,8 @@ class AlignmentTrack {
|
|
|
32574
32562
|
this.skippedColor = config.skippedColor || "rgb(150, 170, 170)";
|
|
32575
32563
|
this.pairConnectorColor = config.pairConnectorColor;
|
|
32576
32564
|
|
|
32577
|
-
this.
|
|
32578
|
-
this.
|
|
32565
|
+
this.smallTLENColor = config.smallTLENColor || config.smallFragmentLengthColor || "rgb(0, 0, 150)";
|
|
32566
|
+
this.largeTLENColor = config.largeTLENColor || config.largeFragmentLengthColor || "rgb(200, 0, 0)";
|
|
32579
32567
|
|
|
32580
32568
|
this.pairOrientation = config.pairOrienation || 'fr';
|
|
32581
32569
|
this.pairColors = {};
|
|
@@ -32583,7 +32571,7 @@ class AlignmentTrack {
|
|
|
32583
32571
|
this.pairColors["RR"] = config.rrColor || "rgb(20, 50, 200)";
|
|
32584
32572
|
this.pairColors["LL"] = config.llColor || "rgb(0, 150, 150)";
|
|
32585
32573
|
|
|
32586
|
-
this.colorBy = config.colorBy || "
|
|
32574
|
+
this.colorBy = config.colorBy || "unexpectedPair";
|
|
32587
32575
|
this.colorByTag = config.colorByTag ? config.colorByTag.toUpperCase() : undefined;
|
|
32588
32576
|
this.bamColorTag = config.bamColorTag === undefined ? "YC" : config.bamColorTag;
|
|
32589
32577
|
|
|
@@ -32690,7 +32678,7 @@ class AlignmentTrack {
|
|
|
32690
32678
|
for (let alignment of alignmentRow.alignments) {
|
|
32691
32679
|
|
|
32692
32680
|
this.hasPairs = this.hasPairs || alignment.isPaired();
|
|
32693
|
-
if (this.browser.circularView
|
|
32681
|
+
if (this.browser.circularView) {
|
|
32694
32682
|
// This is an expensive check, only do it if needed
|
|
32695
32683
|
this.hasSupplemental = this.hasSupplemental || alignment.hasTag('SA');
|
|
32696
32684
|
}
|
|
@@ -32975,7 +32963,7 @@ class AlignmentTrack {
|
|
|
32975
32963
|
direction: direction
|
|
32976
32964
|
};
|
|
32977
32965
|
this.parent.sortObject = newSortObject;
|
|
32978
|
-
sortAlignmentRows(newSortObject, viewport.
|
|
32966
|
+
sortAlignmentRows(newSortObject, viewport.cachedFeatures);
|
|
32979
32967
|
viewport.repaint();
|
|
32980
32968
|
};
|
|
32981
32969
|
list.push('<b>Sort by...</b>');
|
|
@@ -33005,7 +32993,7 @@ class AlignmentTrack {
|
|
|
33005
32993
|
};
|
|
33006
32994
|
this.sortByTag = tag;
|
|
33007
32995
|
this.parent.sortObject = newSortObject;
|
|
33008
|
-
sortAlignmentRows(newSortObject, viewport.
|
|
32996
|
+
sortAlignmentRows(newSortObject, viewport.cachedFeatures);
|
|
33009
32997
|
viewport.repaint();
|
|
33010
32998
|
}
|
|
33011
32999
|
}
|
|
@@ -33032,7 +33020,11 @@ class AlignmentTrack {
|
|
|
33032
33020
|
const referenceFrame = clickState.viewport.referenceFrame;
|
|
33033
33021
|
if (this.browser.genome.getChromosome(clickedAlignment.mate.chr)) {
|
|
33034
33022
|
this.highlightedAlignmentReadNamed = clickedAlignment.readName;
|
|
33035
|
-
this.browser.presentMultiLocusPanel(clickedAlignment, referenceFrame)
|
|
33023
|
+
//this.browser.presentMultiLocusPanel(clickedAlignment, referenceFrame)
|
|
33024
|
+
const bpWidth = referenceFrame.end - referenceFrame.start;
|
|
33025
|
+
const frameStart = clickedAlignment.mate.position - bpWidth / 2;
|
|
33026
|
+
const frameEnd = clickedAlignment.mate.position + bpWidth / 2;
|
|
33027
|
+
this.browser.addMultiLocusPanel(clickedAlignment.mate.chr, frameStart, frameEnd, referenceFrame);
|
|
33036
33028
|
} else {
|
|
33037
33029
|
Alert.presentAlert(`Reference does not contain chromosome: ${clickedAlignment.mate.chr}`);
|
|
33038
33030
|
}
|
|
@@ -33045,10 +33037,7 @@ class AlignmentTrack {
|
|
|
33045
33037
|
list.push({
|
|
33046
33038
|
label: 'View read sequence',
|
|
33047
33039
|
click: () => {
|
|
33048
|
-
const
|
|
33049
|
-
if (!alignment) return
|
|
33050
|
-
|
|
33051
|
-
const seqstring = alignment.seq; //.map(b => String.fromCharCode(b)).join("");
|
|
33040
|
+
const seqstring = clickedAlignment.seq; //.map(b => String.fromCharCode(b)).join("");
|
|
33052
33041
|
if (!seqstring || "*" === seqstring) {
|
|
33053
33042
|
Alert.presentAlert("Read sequence: *");
|
|
33054
33043
|
} else {
|
|
@@ -33060,11 +33049,16 @@ class AlignmentTrack {
|
|
|
33060
33049
|
if (isSecureContext()) {
|
|
33061
33050
|
list.push({
|
|
33062
33051
|
label: 'Copy read sequence',
|
|
33063
|
-
click: () => {
|
|
33064
|
-
const
|
|
33065
|
-
|
|
33066
|
-
|
|
33067
|
-
|
|
33052
|
+
click: async () => {
|
|
33053
|
+
const seq = clickedAlignment.seq; //.map(b => String.fromCharCode(b)).join("");
|
|
33054
|
+
try {
|
|
33055
|
+
//console.log(`seq: ${seq}`)
|
|
33056
|
+
await navigator.clipboard.writeText(seq);
|
|
33057
|
+
} catch (e) {
|
|
33058
|
+
console.error(e);
|
|
33059
|
+
Alert.presentAlert(`error copying sequence to clipboard ${e}`);
|
|
33060
|
+
}
|
|
33061
|
+
|
|
33068
33062
|
}
|
|
33069
33063
|
});
|
|
33070
33064
|
}
|
|
@@ -33074,25 +33068,12 @@ class AlignmentTrack {
|
|
|
33074
33068
|
}
|
|
33075
33069
|
|
|
33076
33070
|
// Experimental JBrowse feature
|
|
33077
|
-
if (this.browser.circularView &&
|
|
33078
|
-
&& (this.hasPairs || this.hasSupplemental)) {
|
|
33071
|
+
if (this.browser.circularView && (this.hasPairs || this.hasSupplemental)) {
|
|
33079
33072
|
if (this.hasPairs) {
|
|
33080
33073
|
list.push({
|
|
33081
33074
|
label: 'Add discordant pairs to circular view',
|
|
33082
33075
|
click: () => {
|
|
33083
|
-
|
|
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});
|
|
33076
|
+
this.parent.addPairedChordsForViewport(viewport);
|
|
33096
33077
|
}
|
|
33097
33078
|
});
|
|
33098
33079
|
}
|
|
@@ -33100,17 +33081,7 @@ class AlignmentTrack {
|
|
|
33100
33081
|
list.push({
|
|
33101
33082
|
label: 'Add split reads to circular view',
|
|
33102
33083
|
click: () => {
|
|
33103
|
-
|
|
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});
|
|
33084
|
+
this.parent.addSplitChordsForViewport(viewport);
|
|
33114
33085
|
}
|
|
33115
33086
|
});
|
|
33116
33087
|
}
|
|
@@ -33129,7 +33100,7 @@ class AlignmentTrack {
|
|
|
33129
33100
|
|
|
33130
33101
|
const showSoftClips = this.parent.showSoftClips;
|
|
33131
33102
|
|
|
33132
|
-
let features = viewport.
|
|
33103
|
+
let features = viewport.cachedFeatures;
|
|
33133
33104
|
if (!features || features.length === 0) return
|
|
33134
33105
|
|
|
33135
33106
|
let packedAlignmentRows = features.packedAlignmentRows;
|
|
@@ -33220,14 +33191,17 @@ class AlignmentTrack {
|
|
|
33220
33191
|
break
|
|
33221
33192
|
}
|
|
33222
33193
|
|
|
33194
|
+
case "tlen":
|
|
33223
33195
|
case "fragmentLength":
|
|
33224
33196
|
|
|
33225
|
-
if (alignment.mate && alignment.isMateMapped()
|
|
33226
|
-
|
|
33227
|
-
|
|
33228
|
-
|
|
33229
|
-
|
|
33230
|
-
|
|
33197
|
+
if (alignment.mate && alignment.isMateMapped()) {
|
|
33198
|
+
if (alignment.mate.chr !== alignment.chr) {
|
|
33199
|
+
color = getChrColor(alignment.mate.chr);
|
|
33200
|
+
} else if (this.parent.minTemplateLength && Math.abs(alignment.fragmentLength) < this.parent.minTemplateLength) {
|
|
33201
|
+
color = this.smallTLENColor;
|
|
33202
|
+
} else if (this.parent.maxTemplateLength && Math.abs(alignment.fragmentLength) > this.parent.maxTemplateLength) {
|
|
33203
|
+
color = this.largeTLENColor;
|
|
33204
|
+
}
|
|
33231
33205
|
}
|
|
33232
33206
|
break
|
|
33233
33207
|
|
|
@@ -33269,11 +33243,6 @@ function sortAlignmentRows(options, alignmentContainer) {
|
|
|
33269
33243
|
return true === direction ? i : -i
|
|
33270
33244
|
});
|
|
33271
33245
|
|
|
33272
|
-
// For debugging
|
|
33273
|
-
// for(let r of alignmentContainer.packedAlignmentRows) {
|
|
33274
|
-
// console.log(r.score);
|
|
33275
|
-
// }
|
|
33276
|
-
|
|
33277
33246
|
}
|
|
33278
33247
|
|
|
33279
33248
|
function shadedBaseColor(qual, baseColor) {
|
|
@@ -33433,7 +33402,7 @@ class RulerViewport extends TrackViewport {
|
|
|
33433
33402
|
|
|
33434
33403
|
this.$rulerLabel.click(async () => {
|
|
33435
33404
|
|
|
33436
|
-
await this.browser.
|
|
33405
|
+
await this.browser.gotoMultilocusPanel(this.referenceFrame);
|
|
33437
33406
|
|
|
33438
33407
|
// const removals = this.browser.referenceFrameList.filter(r => this.referenceFrame !== r)
|
|
33439
33408
|
// for (let referenceFrame of removals) {
|
|
@@ -33556,7 +33525,9 @@ class RulerViewport extends TrackViewport {
|
|
|
33556
33525
|
currentViewport = this;
|
|
33557
33526
|
this.$tooltip.show();
|
|
33558
33527
|
} else if (currentViewport.guid !== this.guid) {
|
|
33559
|
-
currentViewport.$tooltip
|
|
33528
|
+
if (currentViewport.$tooltip) {
|
|
33529
|
+
currentViewport.$tooltip.hide();
|
|
33530
|
+
}
|
|
33560
33531
|
this.$tooltip.show();
|
|
33561
33532
|
currentViewport = this;
|
|
33562
33533
|
} else {
|
|
@@ -33583,7 +33554,9 @@ class RulerViewport extends TrackViewport {
|
|
|
33583
33554
|
|
|
33584
33555
|
// hide tooltip when movement stops
|
|
33585
33556
|
clearTimeout(timer);
|
|
33586
|
-
timer = setTimeout(() =>
|
|
33557
|
+
timer = setTimeout(() => {
|
|
33558
|
+
if (this.$tooltip) this.$tooltip.hide();
|
|
33559
|
+
}, toolTipTimeout);
|
|
33587
33560
|
|
|
33588
33561
|
}
|
|
33589
33562
|
|
|
@@ -33602,69 +33575,6 @@ class RulerViewport extends TrackViewport {
|
|
|
33602
33575
|
|
|
33603
33576
|
}
|
|
33604
33577
|
|
|
33605
|
-
const viewportColumnManager =
|
|
33606
|
-
{
|
|
33607
|
-
createColumns: (columnContainer, count) => {
|
|
33608
|
-
|
|
33609
|
-
for (let i = 0; i < count; i++) {
|
|
33610
|
-
if (0 === i) {
|
|
33611
|
-
createColumn(columnContainer, 'igv-column');
|
|
33612
|
-
} else {
|
|
33613
|
-
columnContainer.appendChild(div$1({class: 'igv-column-shim'}));
|
|
33614
|
-
createColumn(columnContainer, 'igv-column');
|
|
33615
|
-
}
|
|
33616
|
-
}
|
|
33617
|
-
|
|
33618
|
-
},
|
|
33619
|
-
|
|
33620
|
-
removeColumnAtIndex: (i, column) => {
|
|
33621
|
-
const shim = 0 === i ? column.nextElementSibling : column.previousElementSibling;
|
|
33622
|
-
column.remove();
|
|
33623
|
-
shim.remove();
|
|
33624
|
-
},
|
|
33625
|
-
|
|
33626
|
-
insertAfter: referenceElement => {
|
|
33627
|
-
|
|
33628
|
-
const shim = div$1({class: 'igv-column-shim'});
|
|
33629
|
-
insertElementAfter(shim, referenceElement);
|
|
33630
|
-
|
|
33631
|
-
const column = div$1({class: 'igv-column'});
|
|
33632
|
-
insertElementAfter(column, shim);
|
|
33633
|
-
|
|
33634
|
-
return column
|
|
33635
|
-
},
|
|
33636
|
-
|
|
33637
|
-
insertBefore: (referenceElement, count) => {
|
|
33638
|
-
|
|
33639
|
-
for (let i = 0; i < count; i++) {
|
|
33640
|
-
|
|
33641
|
-
const column = div$1({class: 'igv-column'});
|
|
33642
|
-
insertElementBefore(column, referenceElement);
|
|
33643
|
-
|
|
33644
|
-
if (count > 1 && i > 0) {
|
|
33645
|
-
const columnShim = div$1({class: 'igv-column-shim'});
|
|
33646
|
-
insertElementBefore(columnShim, column);
|
|
33647
|
-
}
|
|
33648
|
-
|
|
33649
|
-
}
|
|
33650
|
-
|
|
33651
|
-
},
|
|
33652
|
-
|
|
33653
|
-
indexOfColumn: (columnContainer, column) => {
|
|
33654
|
-
|
|
33655
|
-
const allColumns = columnContainer.querySelectorAll('.igv-column');
|
|
33656
|
-
|
|
33657
|
-
for (let i = 0; i < allColumns.length; i++) {
|
|
33658
|
-
const c = allColumns[ i ];
|
|
33659
|
-
if (c === column) {
|
|
33660
|
-
return i
|
|
33661
|
-
}
|
|
33662
|
-
}
|
|
33663
|
-
|
|
33664
|
-
return undefined
|
|
33665
|
-
},
|
|
33666
|
-
};
|
|
33667
|
-
|
|
33668
33578
|
/*
|
|
33669
33579
|
* The MIT License (MIT)
|
|
33670
33580
|
*
|
|
@@ -33698,61 +33608,28 @@ class IdeogramViewport extends TrackViewport {
|
|
|
33698
33608
|
|
|
33699
33609
|
initializationHelper() {
|
|
33700
33610
|
|
|
33701
|
-
this
|
|
33702
|
-
this
|
|
33703
|
-
|
|
33704
|
-
|
|
33705
|
-
this.ideogram_ctx = canvas.getContext('2d');
|
|
33706
|
-
|
|
33707
|
-
this.$canvas.remove();
|
|
33708
|
-
this.canvas = undefined;
|
|
33709
|
-
this.ctx = undefined;
|
|
33611
|
+
this.canvas = document.createElement('canvas');
|
|
33612
|
+
this.canvas.className = 'igv-ideogram-canvas';
|
|
33613
|
+
this.$content.append($$1(this.canvas));
|
|
33614
|
+
this.ideogram_ctx = this.canvas.getContext('2d');
|
|
33710
33615
|
|
|
33711
33616
|
this.addMouseHandlers();
|
|
33712
|
-
|
|
33713
33617
|
}
|
|
33714
33618
|
|
|
33715
33619
|
addMouseHandlers() {
|
|
33716
|
-
this.addBrowserObserver();
|
|
33717
33620
|
this.addViewportClickHandler(this.$viewport.get(0));
|
|
33718
33621
|
}
|
|
33719
33622
|
|
|
33720
|
-
removeMouseHandlers() {
|
|
33721
|
-
this.removeBrowserObserver();
|
|
33722
|
-
this.removeViewportClickHandler(this.$viewport.get(0));
|
|
33723
|
-
|
|
33724
|
-
}
|
|
33725
|
-
|
|
33726
|
-
addBrowserObserver() {
|
|
33727
|
-
|
|
33728
|
-
function observerHandler(referenceFrameList) {
|
|
33729
|
-
const column = this.$viewport.get(0).parentElement;
|
|
33730
|
-
if (null !== column) {
|
|
33731
|
-
const index = viewportColumnManager.indexOfColumn(this.browser.columnContainer, column);
|
|
33732
|
-
// console.log(`ideogram-viewport - locus-change-handler index(${ index }) ${ referenceFrameList[ index ].getLocusString() } ${ Date.now() } `)
|
|
33733
|
-
this.update(this.ideogram_ctx, this.$viewport.width(), this.$viewport.height(), referenceFrameList[ index ]);
|
|
33734
|
-
}
|
|
33735
|
-
}
|
|
33736
|
-
|
|
33737
|
-
this.boundObserverHandler = observerHandler.bind(this);
|
|
33738
|
-
this.browser.on('locuschange', this.boundObserverHandler);
|
|
33739
|
-
}
|
|
33740
|
-
|
|
33741
|
-
removeBrowserObserver() {
|
|
33742
|
-
this.browser.off('locuschange', this.boundObserverHandler);
|
|
33743
|
-
}
|
|
33744
|
-
|
|
33745
33623
|
addViewportClickHandler(viewport) {
|
|
33746
33624
|
|
|
33747
|
-
|
|
33625
|
+
this.boundClickHandler = clickHandler.bind(this);
|
|
33626
|
+
viewport.addEventListener('click', this.boundClickHandler);
|
|
33748
33627
|
|
|
33749
|
-
|
|
33750
|
-
const index = viewportColumnManager.indexOfColumn(this.browser.columnContainer, column);
|
|
33751
|
-
const referenceFrame = this.browser.referenceFrameList[ index ];
|
|
33628
|
+
function clickHandler(event) {
|
|
33752
33629
|
|
|
33753
33630
|
const {xNormalized, width} = translateMouseCoordinates$1(event, this.ideogram_ctx.canvas);
|
|
33754
|
-
const {bpLength} = this.browser.genome.getChromosome(referenceFrame.chr);
|
|
33755
|
-
const locusLength = referenceFrame.bpPerPixel * width;
|
|
33631
|
+
const {bpLength} = this.browser.genome.getChromosome(this.referenceFrame.chr);
|
|
33632
|
+
const locusLength = this.referenceFrame.bpPerPixel * width;
|
|
33756
33633
|
const chrCoveragePercentage = locusLength / bpLength;
|
|
33757
33634
|
|
|
33758
33635
|
let xPercentage = xNormalized;
|
|
@@ -33767,21 +33644,14 @@ class IdeogramViewport extends TrackViewport {
|
|
|
33767
33644
|
const ss = Math.round((xPercentage - (chrCoveragePercentage / 2.0)) * bpLength);
|
|
33768
33645
|
const ee = Math.round((xPercentage + (chrCoveragePercentage / 2.0)) * bpLength);
|
|
33769
33646
|
|
|
33770
|
-
referenceFrame.start = ss;
|
|
33771
|
-
referenceFrame.end = ee;
|
|
33772
|
-
referenceFrame.bpPerPixel = (ee - ss) / width;
|
|
33647
|
+
this.referenceFrame.start = ss;
|
|
33648
|
+
this.referenceFrame.end = ee;
|
|
33649
|
+
this.referenceFrame.bpPerPixel = (ee - ss) / width;
|
|
33773
33650
|
|
|
33774
|
-
this.browser.updateViews(referenceFrame, this.browser.trackViews, true);
|
|
33651
|
+
this.browser.updateViews(this.referenceFrame, this.browser.trackViews, true);
|
|
33775
33652
|
|
|
33776
33653
|
}
|
|
33777
33654
|
|
|
33778
|
-
this.boundClickHandler = clickHandler.bind(this);
|
|
33779
|
-
viewport.addEventListener('click', this.boundClickHandler);
|
|
33780
|
-
|
|
33781
|
-
}
|
|
33782
|
-
|
|
33783
|
-
removeViewportClickHandler(viewport) {
|
|
33784
|
-
viewport.removeEventListener('click', this.boundClickHandler);
|
|
33785
33655
|
}
|
|
33786
33656
|
|
|
33787
33657
|
setWidth(width) {
|
|
@@ -33802,10 +33672,20 @@ class IdeogramViewport extends TrackViewport {
|
|
|
33802
33672
|
context.restore();
|
|
33803
33673
|
}
|
|
33804
33674
|
|
|
33805
|
-
|
|
33806
|
-
this
|
|
33807
|
-
|
|
33808
|
-
|
|
33675
|
+
repaint() {
|
|
33676
|
+
this.draw({referenceFrame: this.referenceFrame});
|
|
33677
|
+
}
|
|
33678
|
+
|
|
33679
|
+
draw({referenceFrame}) {
|
|
33680
|
+
|
|
33681
|
+
IGVGraphics.configureHighDPICanvas(this.ideogram_ctx, this.$viewport.width(), this.$viewport.height());
|
|
33682
|
+
|
|
33683
|
+
this.trackView.track.draw({
|
|
33684
|
+
context: this.ideogram_ctx,
|
|
33685
|
+
referenceFrame,
|
|
33686
|
+
pixelWidth: this.$viewport.width(),
|
|
33687
|
+
pixelHeight: this.$viewport.height()
|
|
33688
|
+
});
|
|
33809
33689
|
}
|
|
33810
33690
|
|
|
33811
33691
|
startSpinner() {
|
|
@@ -33846,7 +33726,7 @@ function createViewport(trackView, column, referenceFrame, width) {
|
|
|
33846
33726
|
|
|
33847
33727
|
if ('ruler' === trackView.track.type) {
|
|
33848
33728
|
return new RulerViewport(trackView, column, referenceFrame, width)
|
|
33849
|
-
} else if ('ideogram' === trackView.track.
|
|
33729
|
+
} else if ('ideogram' === trackView.track.id) {
|
|
33850
33730
|
return new IdeogramViewport(trackView, column, referenceFrame, width)
|
|
33851
33731
|
} else {
|
|
33852
33732
|
return new TrackViewport(trackView, column, referenceFrame, width)
|
|
@@ -34369,15 +34249,10 @@ const colorPickerExclusionTypes = new Set(['ruler', 'sequence', 'ideogram']);
|
|
|
34369
34249
|
class TrackView {
|
|
34370
34250
|
|
|
34371
34251
|
constructor(browser, columnContainer, track) {
|
|
34372
|
-
|
|
34373
|
-
this.namespace = `trackview-${guid$2()}`;
|
|
34374
|
-
|
|
34375
34252
|
this.browser = browser;
|
|
34376
34253
|
this.track = track;
|
|
34377
34254
|
track.trackView = this;
|
|
34378
|
-
|
|
34379
34255
|
this.addDOMToColumnContainer(browser, columnContainer, browser.referenceFrameList);
|
|
34380
|
-
|
|
34381
34256
|
}
|
|
34382
34257
|
|
|
34383
34258
|
/**
|
|
@@ -34401,7 +34276,7 @@ class TrackView {
|
|
|
34401
34276
|
// Axis
|
|
34402
34277
|
this.axis = this.createAxis(browser, this.track);
|
|
34403
34278
|
|
|
34404
|
-
//
|
|
34279
|
+
// Create a viewport for each reference frame
|
|
34405
34280
|
this.viewports = [];
|
|
34406
34281
|
const viewportWidth = browser.calculateViewportWidth(referenceFrameList.length);
|
|
34407
34282
|
const viewportColumns = columnContainer.querySelectorAll('.igv-column');
|
|
@@ -34474,7 +34349,6 @@ class TrackView {
|
|
|
34474
34349
|
|
|
34475
34350
|
// Track Viewports
|
|
34476
34351
|
for (let viewport of this.viewports) {
|
|
34477
|
-
viewport.removeMouseHandlers();
|
|
34478
34352
|
viewport.$viewport.remove();
|
|
34479
34353
|
}
|
|
34480
34354
|
|
|
@@ -34540,19 +34414,14 @@ class TrackView {
|
|
|
34540
34414
|
if (false === colorPickerExclusionTypes.has(this.track.type)) {
|
|
34541
34415
|
|
|
34542
34416
|
const trackColors = [];
|
|
34543
|
-
|
|
34544
34417
|
const color = this.track.color || this.track.defaultColor;
|
|
34545
|
-
|
|
34546
34418
|
if (isString$3(color)) {
|
|
34547
34419
|
trackColors.push(color);
|
|
34548
34420
|
}
|
|
34549
|
-
|
|
34550
34421
|
if (this.track.altColor && isString$3(this.track.altColor)) {
|
|
34551
34422
|
trackColors.push(this.track.altColor);
|
|
34552
34423
|
}
|
|
34553
|
-
|
|
34554
34424
|
const defaultColors = trackColors.map(c => c.startsWith("#") ? c : c.startsWith("rgb(") ? IGVColor.rgbToHex(c) : IGVColor.colorNameToHex(c));
|
|
34555
|
-
|
|
34556
34425
|
const colorHandlers =
|
|
34557
34426
|
{
|
|
34558
34427
|
color: color => {
|
|
@@ -34565,7 +34434,6 @@ class TrackView {
|
|
|
34565
34434
|
}
|
|
34566
34435
|
|
|
34567
34436
|
};
|
|
34568
|
-
|
|
34569
34437
|
this.browser.genericColorPicker.configure(defaultColors, colorHandlers);
|
|
34570
34438
|
this.browser.genericColorPicker.setActiveColorHandler(key);
|
|
34571
34439
|
this.browser.genericColorPicker.show();
|
|
@@ -34579,7 +34447,6 @@ class TrackView {
|
|
|
34579
34447
|
if (this.track.minHeight) {
|
|
34580
34448
|
newHeight = Math.max(this.track.minHeight, newHeight);
|
|
34581
34449
|
}
|
|
34582
|
-
|
|
34583
34450
|
if (this.track.maxHeight) {
|
|
34584
34451
|
newHeight = Math.min(this.track.maxHeight, newHeight);
|
|
34585
34452
|
}
|
|
@@ -34599,9 +34466,8 @@ class TrackView {
|
|
|
34599
34466
|
|
|
34600
34467
|
this.sampleNameViewport.viewport.style.height = `${newHeight}px`;
|
|
34601
34468
|
|
|
34602
|
-
// If the track does not manage its own content height set it here
|
|
34469
|
+
// If the track does not manage its own content height set it equal to the viewport height here
|
|
34603
34470
|
if (typeof this.track.computePixelHeight !== "function") {
|
|
34604
|
-
|
|
34605
34471
|
for (let vp of this.viewports) {
|
|
34606
34472
|
vp.setContentHeight(newHeight);
|
|
34607
34473
|
}
|
|
@@ -34658,15 +34524,6 @@ class TrackView {
|
|
|
34658
34524
|
}
|
|
34659
34525
|
}
|
|
34660
34526
|
|
|
34661
|
-
resize(viewportWidth) {
|
|
34662
|
-
|
|
34663
|
-
for (let viewport of this.viewports) {
|
|
34664
|
-
viewport.setWidth(viewportWidth);
|
|
34665
|
-
}
|
|
34666
|
-
|
|
34667
|
-
this.updateViews(true);
|
|
34668
|
-
}
|
|
34669
|
-
|
|
34670
34527
|
/**
|
|
34671
34528
|
* Repaint all viewports without loading any new data. Use this for events that change visual aspect of data,
|
|
34672
34529
|
* e.g. color, sort order, etc, but do not change the genomic state.
|
|
@@ -34674,7 +34531,9 @@ class TrackView {
|
|
|
34674
34531
|
repaintViews() {
|
|
34675
34532
|
|
|
34676
34533
|
for (let viewport of this.viewports) {
|
|
34677
|
-
viewport.
|
|
34534
|
+
if (viewport.isVisible()) {
|
|
34535
|
+
viewport.repaint();
|
|
34536
|
+
}
|
|
34678
34537
|
}
|
|
34679
34538
|
|
|
34680
34539
|
if (typeof this.track.paintAxis === 'function') {
|
|
@@ -34683,7 +34542,6 @@ class TrackView {
|
|
|
34683
34542
|
|
|
34684
34543
|
// Repaint sample names last
|
|
34685
34544
|
this.repaintSamples();
|
|
34686
|
-
|
|
34687
34545
|
}
|
|
34688
34546
|
|
|
34689
34547
|
repaintSamples() {
|
|
@@ -34699,10 +34557,25 @@ class TrackView {
|
|
|
34699
34557
|
this.viewports.forEach(viewport => viewport.setTrackLabel(name));
|
|
34700
34558
|
}
|
|
34701
34559
|
|
|
34560
|
+
/**
|
|
34561
|
+
* Called in response to a window resize event, change in # of multilocus panels, or other event that changes
|
|
34562
|
+
* the width of the track view.
|
|
34563
|
+
*
|
|
34564
|
+
* @param viewportWidth The width of each viewport in this track view.
|
|
34565
|
+
*/
|
|
34566
|
+
resize(viewportWidth) {
|
|
34567
|
+
for (let viewport of this.viewports) {
|
|
34568
|
+
viewport.setWidth(viewportWidth);
|
|
34569
|
+
}
|
|
34570
|
+
}
|
|
34571
|
+
|
|
34702
34572
|
/**
|
|
34703
34573
|
* Update viewports to reflect current genomic state, possibly loading additional data.
|
|
34574
|
+
*
|
|
34575
|
+
* @param force - if true, force a repaint even if no new data is loaded
|
|
34576
|
+
* @returns {Promise<void>}
|
|
34704
34577
|
*/
|
|
34705
|
-
async updateViews(
|
|
34578
|
+
async updateViews() {
|
|
34706
34579
|
|
|
34707
34580
|
if (!(this.browser && this.browser.referenceFrameList)) return
|
|
34708
34581
|
|
|
@@ -34711,31 +34584,38 @@ class TrackView {
|
|
|
34711
34584
|
// Shift viewports left/right to current genomic state (pans canvas)
|
|
34712
34585
|
visibleViewports.forEach(viewport => viewport.shift());
|
|
34713
34586
|
|
|
34714
|
-
|
|
34715
|
-
|
|
34716
|
-
if (isDragging) {
|
|
34587
|
+
// If dragging (panning) return
|
|
34588
|
+
if (this.browser.dragObject) {
|
|
34717
34589
|
return
|
|
34718
34590
|
}
|
|
34719
34591
|
|
|
34720
|
-
//
|
|
34721
|
-
|
|
34592
|
+
// Get viewports to repaint
|
|
34593
|
+
let viewportsToRepaint = (this.track.autoscale || this.track.autoscaleGroup || this.track.type === 'ruler') ?
|
|
34594
|
+
visibleViewports :
|
|
34595
|
+
visibleViewports.filter(vp => vp.needsRepaint());
|
|
34596
|
+
|
|
34597
|
+
// Filter zoomed out views. This has the side effect or turning off or no the zoomed out notice
|
|
34598
|
+
viewportsToRepaint = viewportsToRepaint.filter(viewport => viewport.checkZoomIn());
|
|
34599
|
+
|
|
34600
|
+
// Get viewports that require a data load
|
|
34601
|
+
const viewportsToReload = viewportsToRepaint.filter(viewport => viewport.needsReload());
|
|
34722
34602
|
|
|
34723
34603
|
// Trigger viewport to load features needed to cover current genomic range
|
|
34724
34604
|
// NOTE: these must be loaded synchronously, do not user Promise.all, not all file readers are thread safe
|
|
34725
|
-
for (let viewport of
|
|
34605
|
+
for (let viewport of viewportsToReload) {
|
|
34726
34606
|
await viewport.loadFeatures();
|
|
34727
34607
|
}
|
|
34728
|
-
|
|
34608
|
+
|
|
34729
34609
|
if (this.disposed) return // Track was removed during load
|
|
34730
34610
|
|
|
34731
|
-
//
|
|
34611
|
+
// Special case for variant tracks in multilocus view. The # of rows to allocate to the variant (site)
|
|
34732
34612
|
// section depends on data from all the views. We only need to adjust this however if any data was loaded
|
|
34733
34613
|
// (i.e. reloadableViewports.length > 0)
|
|
34734
|
-
if (this.track && typeof this.track.variantRowCount === 'function' &&
|
|
34614
|
+
if (this.track && typeof this.track.variantRowCount === 'function' && viewportsToReload.length > 0) {
|
|
34735
34615
|
let maxRow = 0;
|
|
34736
34616
|
for (let viewport of this.viewports) {
|
|
34737
|
-
if (viewport.
|
|
34738
|
-
maxRow = Math.max(maxRow, viewport.
|
|
34617
|
+
if (viewport.featureCache && viewport.featureCache.features) {
|
|
34618
|
+
maxRow = Math.max(maxRow, viewport.featureCache.features.reduce((a, f) => Math.max(a, f.row || 0), 0));
|
|
34739
34619
|
}
|
|
34740
34620
|
}
|
|
34741
34621
|
const current = this.track.nVariantRows;
|
|
@@ -34747,19 +34627,18 @@ class TrackView {
|
|
|
34747
34627
|
}
|
|
34748
34628
|
}
|
|
34749
34629
|
|
|
34750
|
-
|
|
34751
34630
|
if (this.track.autoscale) {
|
|
34752
34631
|
let allFeatures = [];
|
|
34753
34632
|
for (let visibleViewport of visibleViewports) {
|
|
34754
34633
|
const referenceFrame = visibleViewport.referenceFrame;
|
|
34755
34634
|
const start = referenceFrame.start;
|
|
34756
34635
|
const end = start + referenceFrame.toBP($$1(visibleViewport.contentDiv).width());
|
|
34757
|
-
if (visibleViewport.
|
|
34758
|
-
if (typeof visibleViewport.
|
|
34759
|
-
const max = visibleViewport.
|
|
34636
|
+
if (visibleViewport.featureCache && visibleViewport.featureCache.features) {
|
|
34637
|
+
if (typeof visibleViewport.featureCache.features.getMax === 'function') {
|
|
34638
|
+
const max = visibleViewport.featureCache.features.getMax(start, end);
|
|
34760
34639
|
allFeatures.push({value: max});
|
|
34761
34640
|
} else {
|
|
34762
|
-
allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(visibleViewport.
|
|
34641
|
+
allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(visibleViewport.featureCache.features, start, end));
|
|
34763
34642
|
}
|
|
34764
34643
|
}
|
|
34765
34644
|
}
|
|
@@ -34770,15 +34649,8 @@ class TrackView {
|
|
|
34770
34649
|
}
|
|
34771
34650
|
}
|
|
34772
34651
|
|
|
34773
|
-
|
|
34774
|
-
|
|
34775
|
-
for (let visibleViewport of visibleViewports) {
|
|
34776
|
-
visibleViewport.repaint();
|
|
34777
|
-
}
|
|
34778
|
-
} else {
|
|
34779
|
-
for (let vp of reloadableViewports) {
|
|
34780
|
-
vp.repaint();
|
|
34781
|
-
}
|
|
34652
|
+
for (let vp of viewportsToRepaint) {
|
|
34653
|
+
vp.repaint();
|
|
34782
34654
|
}
|
|
34783
34655
|
|
|
34784
34656
|
this.adjustTrackHeight();
|
|
@@ -34806,34 +34678,29 @@ class TrackView {
|
|
|
34806
34678
|
}
|
|
34807
34679
|
|
|
34808
34680
|
/**
|
|
34809
|
-
* Return a promise to get all in-view features. Used for group autoscaling.
|
|
34681
|
+
* Return a promise to get all in-view features across all viewports. Used for group autoscaling.
|
|
34810
34682
|
*/
|
|
34811
|
-
async getInViewFeatures(
|
|
34683
|
+
async getInViewFeatures() {
|
|
34812
34684
|
|
|
34813
34685
|
if (!(this.browser && this.browser.referenceFrameList)) {
|
|
34814
34686
|
return []
|
|
34815
34687
|
}
|
|
34816
34688
|
|
|
34817
|
-
// List of viewports that need reloading
|
|
34818
|
-
const rpV = this.viewportsToReload(force);
|
|
34819
|
-
const promises = rpV.map(function (vp) {
|
|
34820
|
-
return vp.loadFeatures()
|
|
34821
|
-
});
|
|
34822
|
-
|
|
34823
|
-
await Promise.all(promises);
|
|
34824
|
-
|
|
34825
34689
|
let allFeatures = [];
|
|
34826
34690
|
for (let vp of this.viewports) {
|
|
34827
|
-
if (vp.
|
|
34691
|
+
if (vp.needsReload()) {
|
|
34692
|
+
await vp.loadFeatures();
|
|
34693
|
+
}
|
|
34694
|
+
if (vp.featureCache && vp.featureCache.features) {
|
|
34828
34695
|
const referenceFrame = vp.referenceFrame;
|
|
34829
34696
|
const start = referenceFrame.start;
|
|
34830
34697
|
const end = start + referenceFrame.toBP($$1(vp.contentDiv).width());
|
|
34831
34698
|
|
|
34832
|
-
if (typeof vp.
|
|
34833
|
-
const max = vp.
|
|
34699
|
+
if (typeof vp.featureCache.features.getMax === 'function') {
|
|
34700
|
+
const max = vp.featureCache.features.getMax(start, end);
|
|
34834
34701
|
allFeatures.push({value: max});
|
|
34835
34702
|
} else {
|
|
34836
|
-
allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(vp.
|
|
34703
|
+
allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(vp.featureCache.features, start, end));
|
|
34837
34704
|
}
|
|
34838
34705
|
}
|
|
34839
34706
|
}
|
|
@@ -34874,27 +34741,6 @@ class TrackView {
|
|
|
34874
34741
|
}
|
|
34875
34742
|
}
|
|
34876
34743
|
|
|
34877
|
-
viewportsToReload(force) {
|
|
34878
|
-
|
|
34879
|
-
// List of viewports that need reloading
|
|
34880
|
-
const viewports = this.viewports.filter(viewport => {
|
|
34881
|
-
if (!viewport.isVisible()) {
|
|
34882
|
-
return false
|
|
34883
|
-
}
|
|
34884
|
-
if (!viewport.checkZoomIn()) {
|
|
34885
|
-
return false
|
|
34886
|
-
} else {
|
|
34887
|
-
const referenceFrame = viewport.referenceFrame;
|
|
34888
|
-
const chr = viewport.referenceFrame.chr;
|
|
34889
|
-
const start = referenceFrame.start;
|
|
34890
|
-
const end = start + referenceFrame.toBP($$1(viewport.contentDiv).width());
|
|
34891
|
-
const bpPerPixel = referenceFrame.bpPerPixel;
|
|
34892
|
-
return force || (!viewport.tile || viewport.tile.invalidate || !viewport.tile.containsRange(chr, start, end, bpPerPixel))
|
|
34893
|
-
}
|
|
34894
|
-
});
|
|
34895
|
-
return viewports
|
|
34896
|
-
}
|
|
34897
|
-
|
|
34898
34744
|
createTrackScrollbar(browser) {
|
|
34899
34745
|
|
|
34900
34746
|
const outerScroll = div$1();
|
|
@@ -35007,7 +34853,7 @@ class TrackView {
|
|
|
35007
34853
|
|
|
35008
34854
|
addTrackDragMouseHandlers(browser) {
|
|
35009
34855
|
|
|
35010
|
-
if ('ideogram' === this.track.
|
|
34856
|
+
if ('ideogram' === this.track.id || 'ruler' === this.track.id) ; else {
|
|
35011
34857
|
|
|
35012
34858
|
let currentDragHandle = undefined;
|
|
35013
34859
|
|
|
@@ -35084,7 +34930,7 @@ class TrackView {
|
|
|
35084
34930
|
|
|
35085
34931
|
removeTrackDragMouseHandlers() {
|
|
35086
34932
|
|
|
35087
|
-
if ('ideogram' === this.track.
|
|
34933
|
+
if ('ideogram' === this.track.id || 'ruler' === this.track.id) ; else {
|
|
35088
34934
|
this.dragHandle.removeEventListener('mousedown', this.boundTrackDragMouseDownHandler);
|
|
35089
34935
|
document.removeEventListener('mouseup', this.boundDocumentTrackDragMouseUpHandler);
|
|
35090
34936
|
this.dragHandle.removeEventListener('mouseup', this.boundTrackDragMouseEnterHandler);
|
|
@@ -39662,7 +39508,7 @@ class TextFeatureSource {
|
|
|
39662
39508
|
this.sourceType = (config.sourceType === undefined ? "file" : config.sourceType);
|
|
39663
39509
|
this.maxWGCount = config.maxWGCount || DEFAULT_MAX_WG_COUNT;
|
|
39664
39510
|
|
|
39665
|
-
const queryableFormats = new Set(["bigwig", "bw", "bigbed", "bb", "tdf"]);
|
|
39511
|
+
const queryableFormats = new Set(["bigwig", "bw", "bigbed", "bb", "biginteract", "tdf"]);
|
|
39666
39512
|
|
|
39667
39513
|
if (config.features && Array.isArray(config.features)) {
|
|
39668
39514
|
// Explicit array of features
|
|
@@ -39672,7 +39518,7 @@ class TextFeatureSource {
|
|
|
39672
39518
|
mapProperties(features, config.mappings);
|
|
39673
39519
|
}
|
|
39674
39520
|
this.queryable = false;
|
|
39675
|
-
this.featureCache = new FeatureCache(features, genome);
|
|
39521
|
+
this.featureCache = new FeatureCache$1(features, genome);
|
|
39676
39522
|
} else if (config.reader) {
|
|
39677
39523
|
// Explicit reader implementation
|
|
39678
39524
|
this.reader = config.reader;
|
|
@@ -39839,14 +39685,14 @@ class TextFeatureSource {
|
|
|
39839
39685
|
}
|
|
39840
39686
|
|
|
39841
39687
|
// 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);
|
|
39688
|
+
this.featureCache = new FeatureCache$1(features, this.genome, genomicInterval);
|
|
39843
39689
|
|
|
39844
39690
|
// If track is marked "searchable"< cache features by name -- use this with caution, memory intensive
|
|
39845
39691
|
if (this.config.searchable || this.config.searchableFields) {
|
|
39846
39692
|
this.addFeaturesToDB(features);
|
|
39847
39693
|
}
|
|
39848
39694
|
} else {
|
|
39849
|
-
this.featureCache = new FeatureCache([], genomicInterval); // Empty cache
|
|
39695
|
+
this.featureCache = new FeatureCache$1([], genomicInterval); // Empty cache
|
|
39850
39696
|
}
|
|
39851
39697
|
}
|
|
39852
39698
|
|
|
@@ -40084,9 +39930,9 @@ class BufferedReader {
|
|
|
40084
39930
|
|
|
40085
39931
|
//table chromatinInteract
|
|
40086
39932
|
|
|
40087
|
-
function getDecoder(definedFieldCount, fieldCount, autoSql) {
|
|
39933
|
+
function getDecoder(definedFieldCount, fieldCount, autoSql, format) {
|
|
40088
39934
|
|
|
40089
|
-
if (autoSql && 'chromatinInteract' === autoSql.table) {
|
|
39935
|
+
if (autoSql && 'chromatinInteract' === autoSql.table || "biginteract" === format) {
|
|
40090
39936
|
return decodeInteract
|
|
40091
39937
|
} else {
|
|
40092
39938
|
const standardFieldCount = definedFieldCount - 3;
|
|
@@ -40233,6 +40079,7 @@ class BWReader {
|
|
|
40233
40079
|
|
|
40234
40080
|
constructor(config, genome) {
|
|
40235
40081
|
this.path = config.url;
|
|
40082
|
+
this.format = config.format || "bigwig";
|
|
40236
40083
|
this.genome = genome;
|
|
40237
40084
|
this.rpTreeCache = {};
|
|
40238
40085
|
this.config = config;
|
|
@@ -40833,7 +40680,7 @@ function decodeWigData(data, chrIdx1, bpStart, chrIdx2, bpEnd, featureArray, chr
|
|
|
40833
40680
|
function getBedDataDecoder() {
|
|
40834
40681
|
|
|
40835
40682
|
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);
|
|
40683
|
+
const decoder = getDecoder(this.header.definedFieldCount, this.header.fieldCount, this.autoSql, this.format);
|
|
40837
40684
|
return function (data, chrIdx1, bpStart, chrIdx2, bpEnd, featureArray, chrDict) {
|
|
40838
40685
|
const binaryParser = new BinaryParser(data);
|
|
40839
40686
|
while (binaryParser.remLength() >= minSize) {
|
|
@@ -41589,7 +41436,7 @@ class TDFSource {
|
|
|
41589
41436
|
return features
|
|
41590
41437
|
}
|
|
41591
41438
|
|
|
41592
|
-
supportsWholeGenome() {
|
|
41439
|
+
get supportsWholeGenome() {
|
|
41593
41440
|
return true
|
|
41594
41441
|
}
|
|
41595
41442
|
}
|
|
@@ -41701,7 +41548,7 @@ function zoomLevelForScale(chr, bpPerPixel, genome) {
|
|
|
41701
41548
|
function FeatureSource(config, genome) {
|
|
41702
41549
|
|
|
41703
41550
|
const format = config.format ? config.format.toLowerCase() : undefined;
|
|
41704
|
-
if ('bigwig' === format || 'bigbed' === format || 'bb' === format) {
|
|
41551
|
+
if ('bigwig' === format || 'bigbed' === format || 'bb' === format || "biginteract" === format) {
|
|
41705
41552
|
return new BWSource(config, genome)
|
|
41706
41553
|
} else if ("tdf" === format) {
|
|
41707
41554
|
return new TDFSource(config, genome)
|
|
@@ -41710,38 +41557,6 @@ function FeatureSource(config, genome) {
|
|
|
41710
41557
|
}
|
|
41711
41558
|
}
|
|
41712
41559
|
|
|
41713
|
-
const pairs =
|
|
41714
|
-
[
|
|
41715
|
-
['A', 'T'],
|
|
41716
|
-
['G', 'C'],
|
|
41717
|
-
['Y', 'R'],
|
|
41718
|
-
['W', 'S'],
|
|
41719
|
-
['K', 'M'],
|
|
41720
|
-
['D', 'H'],
|
|
41721
|
-
['B', 'V']
|
|
41722
|
-
];
|
|
41723
|
-
|
|
41724
|
-
const complements = new Map();
|
|
41725
|
-
for (let p of pairs) {
|
|
41726
|
-
const p1 = p[0];
|
|
41727
|
-
const p2 = p[1];
|
|
41728
|
-
complements.set(p1, p2);
|
|
41729
|
-
complements.set(p2, p1);
|
|
41730
|
-
complements.set(p1.toLowerCase(), p2.toLowerCase());
|
|
41731
|
-
complements.set(p2.toLowerCase(), p1.toLowerCase());
|
|
41732
|
-
}
|
|
41733
|
-
|
|
41734
|
-
function reverseComplementSequence(sequence) {
|
|
41735
|
-
|
|
41736
|
-
let comp = '';
|
|
41737
|
-
let idx = sequence.length;
|
|
41738
|
-
while (idx-- > 0) {
|
|
41739
|
-
const base = sequence[idx];
|
|
41740
|
-
comp += complements.has(base) ? complements.get(base) : base;
|
|
41741
|
-
}
|
|
41742
|
-
return comp
|
|
41743
|
-
}
|
|
41744
|
-
|
|
41745
41560
|
const GtexUtils = {
|
|
41746
41561
|
|
|
41747
41562
|
getTissueInfo: function (datasetId, baseURL) {
|
|
@@ -41842,7 +41657,7 @@ function renderFeature(feature, bpStart, xScale, pixelHeight, ctx, options) {
|
|
|
41842
41657
|
// single-exon transcript
|
|
41843
41658
|
const xLeft = Math.max(0, coord.px);
|
|
41844
41659
|
const xRight = Math.min(pixelWidth, coord.px1);
|
|
41845
|
-
const width =
|
|
41660
|
+
const width = xRight - xLeft;
|
|
41846
41661
|
ctx.fillRect(xLeft, py, width, h);
|
|
41847
41662
|
|
|
41848
41663
|
// Arrows
|
|
@@ -42285,7 +42100,7 @@ class FeatureTrack extends TrackBase {
|
|
|
42285
42100
|
|
|
42286
42101
|
}
|
|
42287
42102
|
|
|
42288
|
-
supportsWholeGenome() {
|
|
42103
|
+
get supportsWholeGenome() {
|
|
42289
42104
|
return (this.config.indexed === false || !this.config.indexURL) && this.config.supportsWholeGenome !== false
|
|
42290
42105
|
}
|
|
42291
42106
|
|
|
@@ -42434,21 +42249,18 @@ class FeatureTrack extends TrackBase {
|
|
|
42434
42249
|
const infoURL = this.infoURL || this.config.infoURL;
|
|
42435
42250
|
for (let fd of featureData) {
|
|
42436
42251
|
data.push(fd);
|
|
42437
|
-
if (infoURL
|
|
42438
|
-
|
|
42439
|
-
|
|
42440
|
-
|
|
42441
|
-
|
|
42442
|
-
|
|
42443
|
-
|
|
42444
|
-
|
|
42445
|
-
const url = this.infoURL || this.config.infoURL;
|
|
42446
|
-
const href = url.replace("$$", feature.name);
|
|
42447
|
-
data.push({name: "Info", value: `<a target="_blank" href=${href}>${fd.value}</a>`});
|
|
42448
|
-
}
|
|
42252
|
+
if (infoURL &&
|
|
42253
|
+
fd.name &&
|
|
42254
|
+
fd.name.toLowerCase() === "name" &&
|
|
42255
|
+
fd.value &&
|
|
42256
|
+
isString$3(fd.value) &&
|
|
42257
|
+
!fd.value.startsWith("<")) {
|
|
42258
|
+
const href = infoURL.replace("$$", feature.name);
|
|
42259
|
+
fd.value = `<a target=_blank href=${href}>${fd.value}</a>`;
|
|
42449
42260
|
}
|
|
42450
42261
|
}
|
|
42451
42262
|
|
|
42263
|
+
|
|
42452
42264
|
//Array.prototype.push.apply(data, featureData);
|
|
42453
42265
|
|
|
42454
42266
|
// If we have clicked over an exon number it.
|
|
@@ -42477,7 +42289,7 @@ class FeatureTrack extends TrackBase {
|
|
|
42477
42289
|
}
|
|
42478
42290
|
|
|
42479
42291
|
menuItemList() {
|
|
42480
|
-
|
|
42292
|
+
|
|
42481
42293
|
const menuItems = [];
|
|
42482
42294
|
|
|
42483
42295
|
if (this.render === renderSnp) {
|
|
@@ -42505,7 +42317,7 @@ class FeatureTrack extends TrackBase {
|
|
|
42505
42317
|
menuItems.push(
|
|
42506
42318
|
{
|
|
42507
42319
|
object: $$1(createCheckbox$1(lut[displayMode], displayMode === this.displayMode)),
|
|
42508
|
-
click:
|
|
42320
|
+
click: () => {
|
|
42509
42321
|
this.displayMode = displayMode;
|
|
42510
42322
|
this.config.displayMode = displayMode;
|
|
42511
42323
|
this.trackView.checkContentHeight();
|
|
@@ -42521,14 +42333,28 @@ class FeatureTrack extends TrackBase {
|
|
|
42521
42333
|
|
|
42522
42334
|
contextMenuItemList(clickState) {
|
|
42523
42335
|
|
|
42524
|
-
|
|
42525
|
-
|
|
42526
|
-
|
|
42527
|
-
|
|
42528
|
-
|
|
42529
|
-
|
|
42530
|
-
|
|
42531
|
-
|
|
42336
|
+
const features = this.clickedFeatures(clickState);
|
|
42337
|
+
if (features.length > 1) {
|
|
42338
|
+
features.sort((a, b) => (b.end - b.start) - (a.end - a.start));
|
|
42339
|
+
}
|
|
42340
|
+
const f = features[0]; // The shortest clicked feature
|
|
42341
|
+
|
|
42342
|
+
if ((f.end - f.start) <= 1000000) {
|
|
42343
|
+
const list = [{
|
|
42344
|
+
label: 'View feature sequence',
|
|
42345
|
+
click: async () => {
|
|
42346
|
+
let seq = await this.browser.genome.getSequence(f.chr, f.start, f.end);
|
|
42347
|
+
if (f.strand === '-') {
|
|
42348
|
+
seq = reverseComplementSequence(seq);
|
|
42349
|
+
}
|
|
42350
|
+
if (!seq) seq = "Unknown sequence";
|
|
42351
|
+
Alert.presentAlert(seq);
|
|
42352
|
+
|
|
42353
|
+
}
|
|
42354
|
+
}];
|
|
42355
|
+
|
|
42356
|
+
if (isSecureContext() && navigator.clipboard !== undefined) {
|
|
42357
|
+
list.push(
|
|
42532
42358
|
{
|
|
42533
42359
|
label: 'Copy feature sequence',
|
|
42534
42360
|
click: async () => {
|
|
@@ -42536,17 +42362,23 @@ class FeatureTrack extends TrackBase {
|
|
|
42536
42362
|
if (f.strand === '-') {
|
|
42537
42363
|
seq = reverseComplementSequence(seq);
|
|
42538
42364
|
}
|
|
42539
|
-
|
|
42365
|
+
try {
|
|
42366
|
+
await navigator.clipboard.writeText(seq);
|
|
42367
|
+
} catch (e) {
|
|
42368
|
+
console.error(e);
|
|
42369
|
+
Alert.presentAlert(`error copying sequence to clipboard ${e}`);
|
|
42370
|
+
}
|
|
42540
42371
|
}
|
|
42541
|
-
}
|
|
42542
|
-
|
|
42543
|
-
]
|
|
42372
|
+
}
|
|
42373
|
+
);
|
|
42544
42374
|
}
|
|
42545
|
-
|
|
42375
|
+
list.push('<hr/>');
|
|
42376
|
+
return list
|
|
42377
|
+
} else {
|
|
42546
42378
|
|
|
42547
|
-
|
|
42548
|
-
return undefined
|
|
42379
|
+
return undefined
|
|
42549
42380
|
|
|
42381
|
+
}
|
|
42550
42382
|
}
|
|
42551
42383
|
|
|
42552
42384
|
description() {
|
|
@@ -42567,7 +42399,7 @@ class FeatureTrack extends TrackBase {
|
|
|
42567
42399
|
desc += "</html>";
|
|
42568
42400
|
return desc
|
|
42569
42401
|
} else {
|
|
42570
|
-
return super.description()
|
|
42402
|
+
return super.description()
|
|
42571
42403
|
}
|
|
42572
42404
|
|
|
42573
42405
|
};
|
|
@@ -42648,13 +42480,11 @@ class WigTrack extends TrackBase {
|
|
|
42648
42480
|
this.paintAxis = paintAxis;
|
|
42649
42481
|
|
|
42650
42482
|
const format = config.format ? config.format.toLowerCase() : config.format;
|
|
42483
|
+
this.flipAxis = config.flipAxis ? config.flipAxis : false;
|
|
42484
|
+
this.logScale = config.logScale ? config.logScale : false;
|
|
42651
42485
|
if ("bigwig" === format) {
|
|
42652
|
-
this.flipAxis = config.flipAxis ? config.flipAxis : false;
|
|
42653
|
-
this.logScale = config.logScale ? config.logScale : false;
|
|
42654
42486
|
this.featureSource = new BWSource(config, this.browser.genome);
|
|
42655
42487
|
} else if ("tdf" === format) {
|
|
42656
|
-
this.flipAxis = config.flipAxis ? config.flipAxis : false;
|
|
42657
|
-
this.logScale = config.logScale ? config.logScale : false;
|
|
42658
42488
|
this.featureSource = new TDFSource(config, this.browser.genome);
|
|
42659
42489
|
} else {
|
|
42660
42490
|
this.featureSource = FeatureSource(config, this.browser.genome);
|
|
@@ -42706,7 +42536,7 @@ class WigTrack extends TrackBase {
|
|
|
42706
42536
|
let items = [];
|
|
42707
42537
|
if (this.flipAxis !== undefined) {
|
|
42708
42538
|
items.push({
|
|
42709
|
-
label:"Flip y-axis",
|
|
42539
|
+
label: "Flip y-axis",
|
|
42710
42540
|
click: () => {
|
|
42711
42541
|
this.flipAxis = !this.flipAxis;
|
|
42712
42542
|
this.trackView.repaintViews();
|
|
@@ -42890,7 +42720,7 @@ class WigTrack extends TrackBase {
|
|
|
42890
42720
|
}
|
|
42891
42721
|
}
|
|
42892
42722
|
|
|
42893
|
-
supportsWholeGenome() {
|
|
42723
|
+
get supportsWholeGenome() {
|
|
42894
42724
|
return !this.config.indexURL && this.config.supportsWholeGenome !== false
|
|
42895
42725
|
}
|
|
42896
42726
|
|
|
@@ -43434,7 +43264,7 @@ class SegTrack extends TrackBase {
|
|
|
43434
43264
|
|
|
43435
43265
|
const sortHandler = (sort) => {
|
|
43436
43266
|
const viewport = clickState.viewport;
|
|
43437
|
-
const features = viewport.
|
|
43267
|
+
const features = viewport.cachedFeatures;
|
|
43438
43268
|
this.sortSamples(sort.chr, sort.start, sort.end, sort.direction, features);
|
|
43439
43269
|
};
|
|
43440
43270
|
|
|
@@ -43462,7 +43292,7 @@ class SegTrack extends TrackBase {
|
|
|
43462
43292
|
|
|
43463
43293
|
}
|
|
43464
43294
|
|
|
43465
|
-
supportsWholeGenome() {
|
|
43295
|
+
get supportsWholeGenome() {
|
|
43466
43296
|
return (this.config.indexed === false || !this.config.indexURL) && this.config.supportsWholeGenome !== false
|
|
43467
43297
|
}
|
|
43468
43298
|
|
|
@@ -43552,10 +43382,16 @@ const MUT_COLORS = {
|
|
|
43552
43382
|
* THE SOFTWARE.
|
|
43553
43383
|
*/
|
|
43554
43384
|
|
|
43385
|
+
/**
|
|
43386
|
+
* Represents 2 or more wig tracks overlaid on a common viewport.
|
|
43387
|
+
*/
|
|
43555
43388
|
class MergedTrack extends TrackBase {
|
|
43556
43389
|
|
|
43557
43390
|
constructor(config, browser) {
|
|
43558
43391
|
super(config, browser);
|
|
43392
|
+
this.type = "merged";
|
|
43393
|
+
this.featureType = 'numeric';
|
|
43394
|
+
this.paintAxis = paintAxis;
|
|
43559
43395
|
}
|
|
43560
43396
|
|
|
43561
43397
|
init(config) {
|
|
@@ -43566,21 +43402,6 @@ class MergedTrack extends TrackBase {
|
|
|
43566
43402
|
super.init(config);
|
|
43567
43403
|
}
|
|
43568
43404
|
|
|
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
43405
|
async postInit() {
|
|
43585
43406
|
|
|
43586
43407
|
this.tracks = [];
|
|
@@ -43600,11 +43421,55 @@ class MergedTrack extends TrackBase {
|
|
|
43600
43421
|
}
|
|
43601
43422
|
}
|
|
43602
43423
|
|
|
43603
|
-
this.
|
|
43424
|
+
this.flipAxis = this.config.flipAxis ? this.config.flipAxis : false;
|
|
43425
|
+
this.logScale = this.config.logScale ? this.config.logScale : false;
|
|
43426
|
+
this.autoscale = this.config.autoscale || this.config.max === undefined;
|
|
43427
|
+
if (!this.autoscale) {
|
|
43428
|
+
this.dataRange = {
|
|
43429
|
+
min: this.config.min || 0,
|
|
43430
|
+
max: this.config.max
|
|
43431
|
+
};
|
|
43432
|
+
}
|
|
43433
|
+
for (let t of this.tracks) {
|
|
43434
|
+
t.autoscale = false;
|
|
43435
|
+
t.dataRange = this.dataRange;
|
|
43436
|
+
}
|
|
43437
|
+
|
|
43438
|
+
this.height = this.config.height || 50;
|
|
43604
43439
|
|
|
43605
43440
|
return Promise.all(p)
|
|
43606
43441
|
}
|
|
43607
43442
|
|
|
43443
|
+
get height() {
|
|
43444
|
+
return this._height
|
|
43445
|
+
}
|
|
43446
|
+
|
|
43447
|
+
set height(h) {
|
|
43448
|
+
this._height = h;
|
|
43449
|
+
if (this.tracks) {
|
|
43450
|
+
for (let t of this.tracks) {
|
|
43451
|
+
t.height = h;
|
|
43452
|
+
t.config.height = h;
|
|
43453
|
+
}
|
|
43454
|
+
}
|
|
43455
|
+
}
|
|
43456
|
+
|
|
43457
|
+
menuItemList() {
|
|
43458
|
+
let items = [];
|
|
43459
|
+
if (this.flipAxis !== undefined) {
|
|
43460
|
+
items.push({
|
|
43461
|
+
label: "Flip y-axis",
|
|
43462
|
+
click: () => {
|
|
43463
|
+
this.flipAxis = !this.flipAxis;
|
|
43464
|
+
this.trackView.repaintViews();
|
|
43465
|
+
}
|
|
43466
|
+
});
|
|
43467
|
+
}
|
|
43468
|
+
|
|
43469
|
+
items = items.concat(MenuUtils.numericDataMenuItems(this.trackView));
|
|
43470
|
+
|
|
43471
|
+
return items
|
|
43472
|
+
}
|
|
43608
43473
|
|
|
43609
43474
|
async getFeatures(chr, bpStart, bpEnd, bpPerPixel) {
|
|
43610
43475
|
|
|
@@ -43614,44 +43479,26 @@ class MergedTrack extends TrackBase {
|
|
|
43614
43479
|
|
|
43615
43480
|
draw(options) {
|
|
43616
43481
|
|
|
43617
|
-
|
|
43618
|
-
|
|
43619
|
-
mergedFeatures = options.features; // Array of feature arrays, 1 for each track
|
|
43620
|
-
|
|
43621
|
-
dataRange = autoscale(options.referenceFrame.chr, mergedFeatures);
|
|
43482
|
+
const mergedFeatures = options.features; // Array of feature arrays, 1 for each track
|
|
43622
43483
|
|
|
43623
|
-
|
|
43624
|
-
|
|
43625
|
-
|
|
43484
|
+
if (this.autoscale) {
|
|
43485
|
+
this.dataRange = autoscale(options.referenceFrame.chr, mergedFeatures);
|
|
43486
|
+
}
|
|
43626
43487
|
|
|
43627
|
-
|
|
43488
|
+
for (let i = 0, len = this.tracks.length; i < len; i++) {
|
|
43489
|
+
const trackOptions = Object.assign({}, options);
|
|
43628
43490
|
trackOptions.features = mergedFeatures[i];
|
|
43629
|
-
this.tracks[i].dataRange = dataRange;
|
|
43491
|
+
this.tracks[i].dataRange = this.dataRange;
|
|
43492
|
+
this.tracks[i].flipAxis = this.flipAxis;
|
|
43493
|
+
this.tracks[i].logScale = this.logScale;
|
|
43494
|
+
this.tracks[i].graphType = this.graphType;
|
|
43630
43495
|
this.tracks[i].draw(trackOptions);
|
|
43631
43496
|
}
|
|
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
43497
|
}
|
|
43651
43498
|
|
|
43652
43499
|
popupData(clickState, features) {
|
|
43653
43500
|
|
|
43654
|
-
const featuresArray = features || clickState.viewport.
|
|
43501
|
+
const featuresArray = features || clickState.viewport.cachedFeatures;
|
|
43655
43502
|
|
|
43656
43503
|
if (featuresArray && featuresArray.length === this.tracks.length) {
|
|
43657
43504
|
// Array of feature arrays, 1 for each track
|
|
@@ -43668,42 +43515,23 @@ class MergedTrack extends TrackBase {
|
|
|
43668
43515
|
}
|
|
43669
43516
|
|
|
43670
43517
|
|
|
43671
|
-
supportsWholeGenome() {
|
|
43672
|
-
|
|
43673
|
-
return b
|
|
43518
|
+
get supportsWholeGenome() {
|
|
43519
|
+
return this.tracks.every(track => track.supportsWholeGenome())
|
|
43674
43520
|
}
|
|
43675
43521
|
}
|
|
43676
43522
|
|
|
43677
43523
|
function autoscale(chr, featureArrays) {
|
|
43678
43524
|
|
|
43679
|
-
|
|
43680
|
-
|
|
43681
|
-
|
|
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) {
|
|
43525
|
+
let min = 0;
|
|
43526
|
+
let max = -Number.MAX_VALUE;
|
|
43527
|
+
for(let features of featureArrays) {
|
|
43528
|
+
for(let f of features) {
|
|
43700
43529
|
if (typeof f.value !== 'undefined' && !Number.isNaN(f.value)) {
|
|
43701
43530
|
min = Math.min(min, f.value);
|
|
43702
43531
|
max = Math.max(max, f.value);
|
|
43703
43532
|
}
|
|
43704
|
-
}
|
|
43705
|
-
}
|
|
43706
|
-
// }
|
|
43533
|
+
}
|
|
43534
|
+
}
|
|
43707
43535
|
return {min: min, max: max}
|
|
43708
43536
|
}
|
|
43709
43537
|
|
|
@@ -43812,7 +43640,7 @@ class InteractionTrack extends TrackBase {
|
|
|
43812
43640
|
return this
|
|
43813
43641
|
}
|
|
43814
43642
|
|
|
43815
|
-
supportsWholeGenome() {
|
|
43643
|
+
get supportsWholeGenome() {
|
|
43816
43644
|
return true
|
|
43817
43645
|
}
|
|
43818
43646
|
|
|
@@ -44198,7 +44026,7 @@ class InteractionTrack extends TrackBase {
|
|
|
44198
44026
|
items = items.concat(MenuUtils.numericDataMenuItems(this.trackView));
|
|
44199
44027
|
}
|
|
44200
44028
|
|
|
44201
|
-
if (this.browser.circularView
|
|
44029
|
+
if (this.browser.circularView) {
|
|
44202
44030
|
items.push('<hr/>');
|
|
44203
44031
|
items.push({
|
|
44204
44032
|
label: 'Add interactions to circular view',
|
|
@@ -44216,7 +44044,7 @@ class InteractionTrack extends TrackBase {
|
|
|
44216
44044
|
contextMenuItemList(clickState) {
|
|
44217
44045
|
|
|
44218
44046
|
// Experimental JBrowse feature
|
|
44219
|
-
if (this.browser.circularView
|
|
44047
|
+
if (this.browser.circularView ) {
|
|
44220
44048
|
const viewport = clickState.viewport;
|
|
44221
44049
|
const list = [];
|
|
44222
44050
|
|
|
@@ -44245,21 +44073,22 @@ class InteractionTrack extends TrackBase {
|
|
|
44245
44073
|
|
|
44246
44074
|
// inView features are simply features that have been drawn, i.e. have a drawState
|
|
44247
44075
|
const inView = cachedFeatures.filter(f => f.drawState);
|
|
44248
|
-
if(inView.length === 0)
|
|
44076
|
+
if(inView.length === 0) return;
|
|
44249
44077
|
|
|
44250
|
-
this.browser.circularViewVisible = true;
|
|
44251
44078
|
const chords = makeBedPEChords(inView);
|
|
44252
|
-
|
|
44253
|
-
//
|
|
44254
|
-
|
|
44255
|
-
|
|
44256
|
-
|
|
44257
|
-
//
|
|
44258
|
-
|
|
44259
|
-
|
|
44260
|
-
|
|
44261
|
-
|
|
44262
|
-
|
|
44079
|
+
sendChords(chords, this, refFrame, 0.5);
|
|
44080
|
+
//
|
|
44081
|
+
//
|
|
44082
|
+
// // for filtered set, distinguishing the chromosomes is more critical than tracks
|
|
44083
|
+
// const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? this.color : getChrColor(refFrame.chr), 0.5)
|
|
44084
|
+
// const trackColor = IGVColor.addAlpha(this.color, 0.5)
|
|
44085
|
+
//
|
|
44086
|
+
// // name the chord set to include locus and filtering information
|
|
44087
|
+
// const encodedName = this.name.replaceAll(' ', '%20')
|
|
44088
|
+
// const chordSetName = "all" === refFrame.chr ?
|
|
44089
|
+
// encodedName :
|
|
44090
|
+
// `${encodedName} (${refFrame.chr}:${refFrame.start}-${refFrame.end} ; range:${this.dataRange.min}-${this.dataRange.max})`
|
|
44091
|
+
// this.browser.circularView.addChords(chords, {track: chordSetName, color: chordSetColor, trackColor: trackColor})
|
|
44263
44092
|
}
|
|
44264
44093
|
|
|
44265
44094
|
doAutoscale(features) {
|
|
@@ -44324,7 +44153,7 @@ class InteractionTrack extends TrackBase {
|
|
|
44324
44153
|
|
|
44325
44154
|
// We use the cached features rather than method to avoid async load. If the
|
|
44326
44155
|
// 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.
|
|
44156
|
+
const featureList = features || clickState.viewport.cachedFeatures;
|
|
44328
44157
|
const candidates = [];
|
|
44329
44158
|
if (featureList) {
|
|
44330
44159
|
const proportional = (this.arcType === "proportional" || this.arcType === "inView" || this.arcType === "partialInView");
|
|
@@ -44658,7 +44487,7 @@ class VariantTrack extends TrackBase {
|
|
|
44658
44487
|
|
|
44659
44488
|
}
|
|
44660
44489
|
|
|
44661
|
-
supportsWholeGenome() {
|
|
44490
|
+
get supportsWholeGenome() {
|
|
44662
44491
|
return this.config.indexed === false || this.config.supportsWholeGenome === true
|
|
44663
44492
|
}
|
|
44664
44493
|
|
|
@@ -44850,7 +44679,7 @@ class VariantTrack extends TrackBase {
|
|
|
44850
44679
|
}
|
|
44851
44680
|
|
|
44852
44681
|
} else if (this._color) {
|
|
44853
|
-
variantColor =
|
|
44682
|
+
variantColor = this.color;
|
|
44854
44683
|
} else if ("NONVARIANT" === v.type) {
|
|
44855
44684
|
variantColor = this.nonRefColor;
|
|
44856
44685
|
} else if ("MIXED" === v.type) {
|
|
@@ -44861,6 +44690,10 @@ class VariantTrack extends TrackBase {
|
|
|
44861
44690
|
return variantColor
|
|
44862
44691
|
}
|
|
44863
44692
|
|
|
44693
|
+
get color() {
|
|
44694
|
+
return this._color ? ((typeof this._color === "function") ? this._color(v) : this._color) : this.defaultColor
|
|
44695
|
+
}
|
|
44696
|
+
|
|
44864
44697
|
clickedFeatures(clickState, features) {
|
|
44865
44698
|
|
|
44866
44699
|
let featureList = super.clickedFeatures(clickState, features);
|
|
@@ -45087,25 +44920,15 @@ class VariantTrack extends TrackBase {
|
|
|
45087
44920
|
}
|
|
45088
44921
|
|
|
45089
44922
|
// Experimental JBrowse circular view integration
|
|
45090
|
-
if (this.browser.circularView
|
|
44923
|
+
if (this.browser.circularView) {
|
|
45091
44924
|
|
|
45092
44925
|
menuItems.push('<hr>');
|
|
45093
44926
|
menuItems.push({
|
|
45094
44927
|
label: 'Add SVs to circular view',
|
|
45095
44928
|
click: () => {
|
|
45096
|
-
const inView = [];
|
|
45097
44929
|
for (let viewport of this.trackView.viewports) {
|
|
45098
|
-
|
|
45099
|
-
for (let f of viewport.getCachedFeatures()) {
|
|
45100
|
-
if (f.end >= refFrame.start && f.start <= refFrame.end) {
|
|
45101
|
-
inView.push(f);
|
|
45102
|
-
}
|
|
45103
|
-
}
|
|
44930
|
+
this.sendChordsForViewport(viewport);
|
|
45104
44931
|
}
|
|
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
44932
|
}
|
|
45110
44933
|
});
|
|
45111
44934
|
}
|
|
@@ -45117,20 +44940,14 @@ class VariantTrack extends TrackBase {
|
|
|
45117
44940
|
contextMenuItemList(clickState) {
|
|
45118
44941
|
|
|
45119
44942
|
// Experimental JBrowse circular view integration
|
|
45120
|
-
if (this.browser.circularView
|
|
44943
|
+
if (this.browser.circularView) {
|
|
45121
44944
|
const viewport = clickState.viewport;
|
|
45122
44945
|
const list = [];
|
|
45123
44946
|
|
|
45124
44947
|
list.push({
|
|
45125
44948
|
label: 'Add SVs to Circular View',
|
|
45126
44949
|
click: () => {
|
|
45127
|
-
|
|
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});
|
|
44950
|
+
this.sendChordsForViewport(viewport);
|
|
45134
44951
|
}
|
|
45135
44952
|
});
|
|
45136
44953
|
|
|
@@ -45140,6 +44957,15 @@ class VariantTrack extends TrackBase {
|
|
|
45140
44957
|
}
|
|
45141
44958
|
|
|
45142
44959
|
|
|
44960
|
+
sendChordsForViewport(viewport) {
|
|
44961
|
+
const refFrame = viewport.referenceFrame;
|
|
44962
|
+
const inView = "all" === refFrame.chr ?
|
|
44963
|
+
this.featureSource.getAllFeatures() :
|
|
44964
|
+
this.featureSource.featureCache.queryFeatures(refFrame.chr, refFrame.start, refFrame.end);
|
|
44965
|
+
const chords = makeVCFChords(inView);
|
|
44966
|
+
sendChords(chords, this, refFrame, 0.5);
|
|
44967
|
+
}
|
|
44968
|
+
|
|
45143
44969
|
/**
|
|
45144
44970
|
* Create a "color by" checkbox menu item, optionally initially checked
|
|
45145
44971
|
* @param menuItem
|
|
@@ -45436,7 +45262,7 @@ class EqtlTrack extends TrackBase {
|
|
|
45436
45262
|
*/
|
|
45437
45263
|
popupData(clickState) {
|
|
45438
45264
|
|
|
45439
|
-
let features = clickState.viewport.
|
|
45265
|
+
let features = clickState.viewport.cachedFeatures;
|
|
45440
45266
|
if (!features || features.length === 0) return []
|
|
45441
45267
|
|
|
45442
45268
|
const tolerance = 3;
|
|
@@ -45655,7 +45481,7 @@ class GWASTrack extends TrackBase {
|
|
|
45655
45481
|
}
|
|
45656
45482
|
|
|
45657
45483
|
|
|
45658
|
-
supportsWholeGenome() {
|
|
45484
|
+
get supportsWholeGenome() {
|
|
45659
45485
|
return true
|
|
45660
45486
|
}
|
|
45661
45487
|
|
|
@@ -45766,7 +45592,7 @@ class GWASTrack extends TrackBase {
|
|
|
45766
45592
|
|
|
45767
45593
|
let data = [];
|
|
45768
45594
|
const track = clickState.viewport.trackView.track;
|
|
45769
|
-
const features = clickState.viewport.
|
|
45595
|
+
const features = clickState.viewport.cachedFeatures;
|
|
45770
45596
|
|
|
45771
45597
|
if (features) {
|
|
45772
45598
|
let count = 0;
|
|
@@ -46169,7 +45995,7 @@ class GCNVTrack extends TrackBase {
|
|
|
46169
45995
|
return items
|
|
46170
45996
|
}
|
|
46171
45997
|
|
|
46172
|
-
supportsWholeGenome() {
|
|
45998
|
+
get supportsWholeGenome() {
|
|
46173
45999
|
return false
|
|
46174
46000
|
}
|
|
46175
46001
|
}
|
|
@@ -46449,7 +46275,7 @@ class RNAFeatureSource {
|
|
|
46449
46275
|
|
|
46450
46276
|
const data = await igvxhr.loadString(this.config.url, options);
|
|
46451
46277
|
|
|
46452
|
-
this.featureCache = new FeatureCache(parseBP(data), genome);
|
|
46278
|
+
this.featureCache = new FeatureCache$1(parseBP(data), genome);
|
|
46453
46279
|
|
|
46454
46280
|
return this.featureCache.queryFeatures(chr, start, end)
|
|
46455
46281
|
|
|
@@ -46556,21 +46382,18 @@ class RNAFeatureSource {
|
|
|
46556
46382
|
* THE SOFTWARE.
|
|
46557
46383
|
*/
|
|
46558
46384
|
|
|
46385
|
+
/**
|
|
46386
|
+
* Class represents an ideogram of a chromsome cytobands. It is used for the header of a track panel.
|
|
46387
|
+
*
|
|
46388
|
+
*/
|
|
46559
46389
|
class IdeogramTrack {
|
|
46560
46390
|
constructor(browser) {
|
|
46561
|
-
|
|
46562
46391
|
this.browser = browser;
|
|
46563
|
-
|
|
46564
46392
|
this.type = 'ideogram';
|
|
46565
|
-
this.id = this.type;
|
|
46566
|
-
|
|
46567
46393
|
this.height = 16;
|
|
46568
|
-
|
|
46569
46394
|
this.order = Number.MIN_SAFE_INTEGER;
|
|
46570
|
-
|
|
46571
46395
|
this.disableButtons = true;
|
|
46572
46396
|
this.ignoreTrackMenu = true;
|
|
46573
|
-
|
|
46574
46397
|
}
|
|
46575
46398
|
|
|
46576
46399
|
async getFeatures(chr, start, end) {
|
|
@@ -46837,7 +46660,7 @@ class SpliceJunctionTrack extends TrackBase {
|
|
|
46837
46660
|
|
|
46838
46661
|
}
|
|
46839
46662
|
|
|
46840
|
-
supportsWholeGenome() {
|
|
46663
|
+
get supportsWholeGenome() {
|
|
46841
46664
|
return false
|
|
46842
46665
|
}
|
|
46843
46666
|
|
|
@@ -47731,6 +47554,15 @@ class ReferenceFrame {
|
|
|
47731
47554
|
this.id = guid$2();
|
|
47732
47555
|
}
|
|
47733
47556
|
|
|
47557
|
+
extend(locus) {
|
|
47558
|
+
const newStart = Math.min(locus.start, this.start);
|
|
47559
|
+
const newEnd = Math.max(locus.end, this.end);
|
|
47560
|
+
const ratio = (newEnd - newStart) / (this.end - this.start);
|
|
47561
|
+
this.start = newStart;
|
|
47562
|
+
this.end = newEnd;
|
|
47563
|
+
this.bpPerPixel *= ratio;
|
|
47564
|
+
}
|
|
47565
|
+
|
|
47734
47566
|
calculateEnd(pixels) {
|
|
47735
47567
|
return this.start + this.bpPerPixel * pixels
|
|
47736
47568
|
}
|
|
@@ -47817,7 +47649,7 @@ class ReferenceFrame {
|
|
|
47817
47649
|
|
|
47818
47650
|
const viewChanged = start !== this.start || bpPerPixel !== this.bpPerPixel;
|
|
47819
47651
|
if (viewChanged) {
|
|
47820
|
-
await browser.updateViews(
|
|
47652
|
+
await browser.updateViews(true);
|
|
47821
47653
|
}
|
|
47822
47654
|
|
|
47823
47655
|
}
|
|
@@ -47893,30 +47725,6 @@ function createReferenceFrameList(loci, genome, browserFlanking, minimumBases, v
|
|
|
47893
47725
|
})
|
|
47894
47726
|
}
|
|
47895
47727
|
|
|
47896
|
-
function adjustReferenceFrame(scaleFactor, referenceFrame, viewportWidth, alignmentStart, alignmentLength) {
|
|
47897
|
-
|
|
47898
|
-
referenceFrame.bpPerPixel *= scaleFactor;
|
|
47899
|
-
|
|
47900
|
-
const alignmentEE = alignmentStart + alignmentLength;
|
|
47901
|
-
const alignmentCC = (alignmentStart + alignmentEE) / 2;
|
|
47902
|
-
|
|
47903
|
-
referenceFrame.start = alignmentCC - (referenceFrame.bpPerPixel * (viewportWidth / 2));
|
|
47904
|
-
referenceFrame.end = referenceFrame.start + (referenceFrame.bpPerPixel * viewportWidth);
|
|
47905
|
-
referenceFrame.locusSearchString = referenceFrame.getLocusString();
|
|
47906
|
-
}
|
|
47907
|
-
|
|
47908
|
-
function createReferenceFrameWithAlignment(genome, chromosomeName, bpp, viewportWidth, alignmentStart, alignmentLength) {
|
|
47909
|
-
|
|
47910
|
-
const alignmentEE = alignmentStart + alignmentLength;
|
|
47911
|
-
const alignmentCC = (alignmentStart + alignmentEE) / 2;
|
|
47912
|
-
|
|
47913
|
-
const ss = alignmentCC - (bpp * (viewportWidth / 2));
|
|
47914
|
-
const ee = ss + (bpp * viewportWidth);
|
|
47915
|
-
|
|
47916
|
-
return new ReferenceFrame(genome, chromosomeName, ss, ee, bpp)
|
|
47917
|
-
|
|
47918
|
-
}
|
|
47919
|
-
|
|
47920
47728
|
const defaultNucleotideColors = {
|
|
47921
47729
|
"A": "rgb( 0, 200, 0)",
|
|
47922
47730
|
"C": "rgb( 0,0,200)",
|
|
@@ -49016,6 +48824,69 @@ const SVGSaveControl = function (parent, browser) {
|
|
|
49016
48824
|
button.addEventListener('click', () => browser.saveSVGtoFile({}));
|
|
49017
48825
|
};
|
|
49018
48826
|
|
|
48827
|
+
const viewportColumnManager =
|
|
48828
|
+
{
|
|
48829
|
+
createColumns: (columnContainer, count) => {
|
|
48830
|
+
|
|
48831
|
+
for (let i = 0; i < count; i++) {
|
|
48832
|
+
if (0 === i) {
|
|
48833
|
+
createColumn(columnContainer, 'igv-column');
|
|
48834
|
+
} else {
|
|
48835
|
+
columnContainer.appendChild(div$1({class: 'igv-column-shim'}));
|
|
48836
|
+
createColumn(columnContainer, 'igv-column');
|
|
48837
|
+
}
|
|
48838
|
+
}
|
|
48839
|
+
|
|
48840
|
+
},
|
|
48841
|
+
|
|
48842
|
+
removeColumnAtIndex: (i, column) => {
|
|
48843
|
+
const shim = 0 === i ? column.nextElementSibling : column.previousElementSibling;
|
|
48844
|
+
column.remove();
|
|
48845
|
+
shim.remove();
|
|
48846
|
+
},
|
|
48847
|
+
|
|
48848
|
+
insertAfter: referenceElement => {
|
|
48849
|
+
|
|
48850
|
+
const shim = div$1({class: 'igv-column-shim'});
|
|
48851
|
+
insertElementAfter(shim, referenceElement);
|
|
48852
|
+
|
|
48853
|
+
const column = div$1({class: 'igv-column'});
|
|
48854
|
+
insertElementAfter(column, shim);
|
|
48855
|
+
|
|
48856
|
+
return column
|
|
48857
|
+
},
|
|
48858
|
+
|
|
48859
|
+
insertBefore: (referenceElement, count) => {
|
|
48860
|
+
|
|
48861
|
+
for (let i = 0; i < count; i++) {
|
|
48862
|
+
|
|
48863
|
+
const column = div$1({class: 'igv-column'});
|
|
48864
|
+
insertElementBefore(column, referenceElement);
|
|
48865
|
+
|
|
48866
|
+
if (count > 1 && i > 0) {
|
|
48867
|
+
const columnShim = div$1({class: 'igv-column-shim'});
|
|
48868
|
+
insertElementBefore(columnShim, column);
|
|
48869
|
+
}
|
|
48870
|
+
|
|
48871
|
+
}
|
|
48872
|
+
|
|
48873
|
+
},
|
|
48874
|
+
|
|
48875
|
+
indexOfColumn: (columnContainer, column) => {
|
|
48876
|
+
|
|
48877
|
+
const allColumns = columnContainer.querySelectorAll('.igv-column');
|
|
48878
|
+
|
|
48879
|
+
for (let i = 0; i < allColumns.length; i++) {
|
|
48880
|
+
const c = allColumns[ i ];
|
|
48881
|
+
if (c === column) {
|
|
48882
|
+
return i
|
|
48883
|
+
}
|
|
48884
|
+
}
|
|
48885
|
+
|
|
48886
|
+
return undefined
|
|
48887
|
+
},
|
|
48888
|
+
};
|
|
48889
|
+
|
|
49019
48890
|
/*
|
|
49020
48891
|
* The MIT License (MIT)
|
|
49021
48892
|
*
|
|
@@ -49248,7 +49119,7 @@ class RulerTrack {
|
|
|
49248
49119
|
}
|
|
49249
49120
|
}
|
|
49250
49121
|
|
|
49251
|
-
supportsWholeGenome() {
|
|
49122
|
+
get supportsWholeGenome() {
|
|
49252
49123
|
return true
|
|
49253
49124
|
};
|
|
49254
49125
|
|
|
@@ -49619,8 +49490,8 @@ class Browser {
|
|
|
49619
49490
|
this.svgSaveControl = new SVGSaveControl($toggle_button_container.get(0), this);
|
|
49620
49491
|
}
|
|
49621
49492
|
|
|
49622
|
-
if(config.customButtons) {
|
|
49623
|
-
for(let b of config.customButtons) {
|
|
49493
|
+
if (config.customButtons) {
|
|
49494
|
+
for (let b of config.customButtons) {
|
|
49624
49495
|
new CustomButton($toggle_button_container.get(0), this, b);
|
|
49625
49496
|
}
|
|
49626
49497
|
}
|
|
@@ -49781,7 +49652,6 @@ class Browser {
|
|
|
49781
49652
|
return undefined
|
|
49782
49653
|
}
|
|
49783
49654
|
}
|
|
49784
|
-
|
|
49785
49655
|
}
|
|
49786
49656
|
}
|
|
49787
49657
|
|
|
@@ -49826,7 +49696,9 @@ class Browser {
|
|
|
49826
49696
|
// Create ideogram and ruler track. Really this belongs in browser initialization, but creation is
|
|
49827
49697
|
// deferred because ideogram and ruler are treated as "tracks", and tracks require a reference frame
|
|
49828
49698
|
if (false !== session.showIdeogram) {
|
|
49829
|
-
|
|
49699
|
+
const ideogramTrack = new IdeogramTrack(this);
|
|
49700
|
+
ideogramTrack.id = 'ideogram';
|
|
49701
|
+
this.trackViews.push(new TrackView(this, this.columnContainer, ideogramTrack));
|
|
49830
49702
|
}
|
|
49831
49703
|
|
|
49832
49704
|
if (false !== session.showRuler) {
|
|
@@ -49871,6 +49743,11 @@ class Browser {
|
|
|
49871
49743
|
|
|
49872
49744
|
await this.loadTrackList(trackConfigurations);
|
|
49873
49745
|
|
|
49746
|
+
// The ruler and ideogram tracks are not explicitly loaded, but needs updated nonetheless.
|
|
49747
|
+
for (let rtv of this.trackViews.filter((tv) => tv.track.type === 'ruler' || tv.track.type === 'ideogram')) {
|
|
49748
|
+
rtv.updateViews();
|
|
49749
|
+
}
|
|
49750
|
+
|
|
49874
49751
|
this.updateUIWithReferenceFrameList();
|
|
49875
49752
|
|
|
49876
49753
|
}
|
|
@@ -50049,23 +49926,19 @@ class Browser {
|
|
|
50049
49926
|
|
|
50050
49927
|
async loadTrackList(configList) {
|
|
50051
49928
|
|
|
50052
|
-
|
|
50053
|
-
|
|
50054
|
-
|
|
50055
|
-
|
|
50056
|
-
}
|
|
49929
|
+
const promises = [];
|
|
49930
|
+
for (let config of configList) {
|
|
49931
|
+
promises.push(this.loadTrack(config));
|
|
49932
|
+
}
|
|
50057
49933
|
|
|
50058
|
-
|
|
50059
|
-
|
|
50060
|
-
|
|
50061
|
-
|
|
50062
|
-
|
|
50063
|
-
|
|
50064
|
-
}
|
|
50065
|
-
return loadedTracks
|
|
50066
|
-
} finally {
|
|
50067
|
-
await this.resize();
|
|
49934
|
+
const loadedTracks = await Promise.all(promises);
|
|
49935
|
+
const groupAutoscaleViews = this.trackViews.filter(function (trackView) {
|
|
49936
|
+
return trackView.track.autoscaleGroup
|
|
49937
|
+
});
|
|
49938
|
+
if (groupAutoscaleViews.length > 0) {
|
|
49939
|
+
this.updateViews();
|
|
50068
49940
|
}
|
|
49941
|
+
return loadedTracks
|
|
50069
49942
|
}
|
|
50070
49943
|
|
|
50071
49944
|
async loadROI(config) {
|
|
@@ -50079,6 +49952,8 @@ class Browser {
|
|
|
50079
49952
|
} else {
|
|
50080
49953
|
this.roi.push(new ROI(config, this.genome));
|
|
50081
49954
|
}
|
|
49955
|
+
// Force reload all views (force = true) to insure ROI features are loaded. Wasteful but this function is
|
|
49956
|
+
// rarely called.
|
|
50082
49957
|
await this.updateViews(true);
|
|
50083
49958
|
}
|
|
50084
49959
|
|
|
@@ -50090,14 +49965,14 @@ class Browser {
|
|
|
50090
49965
|
}
|
|
50091
49966
|
}
|
|
50092
49967
|
for (let tv of this.trackViews) {
|
|
50093
|
-
tv.
|
|
49968
|
+
tv.repaintViews();
|
|
50094
49969
|
}
|
|
50095
49970
|
}
|
|
50096
49971
|
|
|
50097
49972
|
clearROIs() {
|
|
50098
49973
|
this.roi = [];
|
|
50099
49974
|
for (let tv of this.trackViews) {
|
|
50100
|
-
tv.
|
|
49975
|
+
tv.repaintViews();
|
|
50101
49976
|
}
|
|
50102
49977
|
}
|
|
50103
49978
|
|
|
@@ -50109,24 +49984,12 @@ class Browser {
|
|
|
50109
49984
|
/**
|
|
50110
49985
|
* Return a promise to load a track.
|
|
50111
49986
|
*
|
|
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
49987
|
* @param config
|
|
50125
49988
|
* @param doResize - undefined by default
|
|
50126
49989
|
* @returns {*}
|
|
50127
49990
|
*/
|
|
50128
49991
|
|
|
50129
|
-
async loadTrack(config
|
|
49992
|
+
async loadTrack(config) {
|
|
50130
49993
|
|
|
50131
49994
|
|
|
50132
49995
|
// config might be json
|
|
@@ -50194,11 +50057,6 @@ class Browser {
|
|
|
50194
50057
|
}
|
|
50195
50058
|
msg += (": " + config.url);
|
|
50196
50059
|
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
50060
|
}
|
|
50203
50061
|
}
|
|
50204
50062
|
|
|
@@ -50422,44 +50280,11 @@ class Browser {
|
|
|
50422
50280
|
this.navbarManager.navbarDidResize(this.$navigation.width(), isWGV);
|
|
50423
50281
|
}
|
|
50424
50282
|
|
|
50425
|
-
|
|
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();
|
|
50283
|
+
resize.call(this);
|
|
50284
|
+
await this.updateViews();
|
|
50460
50285
|
}
|
|
50461
50286
|
|
|
50462
|
-
async updateViews(
|
|
50287
|
+
async updateViews() {
|
|
50463
50288
|
|
|
50464
50289
|
const trackViews = this.trackViews;
|
|
50465
50290
|
|
|
@@ -50472,7 +50297,7 @@ class Browser {
|
|
|
50472
50297
|
// Don't autoscale while dragging.
|
|
50473
50298
|
if (this.dragObject) {
|
|
50474
50299
|
for (let trackView of trackViews) {
|
|
50475
|
-
await trackView.updateViews(
|
|
50300
|
+
await trackView.updateViews();
|
|
50476
50301
|
}
|
|
50477
50302
|
} else {
|
|
50478
50303
|
// Group autoscale
|
|
@@ -50517,14 +50342,14 @@ class Browser {
|
|
|
50517
50342
|
for (let trackView of groupTrackViews) {
|
|
50518
50343
|
trackView.track.dataRange = dataRange;
|
|
50519
50344
|
trackView.track.autoscale = false;
|
|
50520
|
-
p.push(trackView.updateViews(
|
|
50345
|
+
p.push(trackView.updateViews());
|
|
50521
50346
|
}
|
|
50522
50347
|
await Promise.all(p);
|
|
50523
50348
|
}
|
|
50524
50349
|
|
|
50525
50350
|
}
|
|
50526
50351
|
|
|
50527
|
-
await Promise.all(otherTracks.map(tv => tv.updateViews(
|
|
50352
|
+
await Promise.all(otherTracks.map(tv => tv.updateViews()));
|
|
50528
50353
|
// for (let trackView of otherTracks) {
|
|
50529
50354
|
// await trackView.updateViews(force);
|
|
50530
50355
|
// }
|
|
@@ -50532,6 +50357,12 @@ class Browser {
|
|
|
50532
50357
|
|
|
50533
50358
|
}
|
|
50534
50359
|
|
|
50360
|
+
repaintViews() {
|
|
50361
|
+
for (let trackView of this.trackViews) {
|
|
50362
|
+
trackView.repaintViews();
|
|
50363
|
+
}
|
|
50364
|
+
}
|
|
50365
|
+
|
|
50535
50366
|
updateLocusSearchWidget() {
|
|
50536
50367
|
|
|
50537
50368
|
const referenceFrameList = this.referenceFrameList;
|
|
@@ -50596,49 +50427,50 @@ class Browser {
|
|
|
50596
50427
|
}
|
|
50597
50428
|
}
|
|
50598
50429
|
|
|
50599
|
-
|
|
50430
|
+
/**
|
|
50431
|
+
* Add a new multi-locus panel for the specified region
|
|
50432
|
+
* @param chr
|
|
50433
|
+
* @param start
|
|
50434
|
+
* @param end
|
|
50435
|
+
* @param referenceFrameLeft - optional, if supplied new panel should be placed to the immediate right
|
|
50436
|
+
*/
|
|
50437
|
+
async addMultiLocusPanel(chr, start, end, referenceFrameLeft) {
|
|
50600
50438
|
|
|
50601
50439
|
// account for reduced viewport width as a result of adding right mate pair panel
|
|
50602
50440
|
const viewportWidth = this.calculateViewportWidth(1 + this.referenceFrameList.length);
|
|
50603
|
-
|
|
50604
50441
|
const scaleFactor = this.calculateViewportWidth(this.referenceFrameList.length) / this.calculateViewportWidth(1 + this.referenceFrameList.length);
|
|
50605
|
-
|
|
50606
|
-
|
|
50607
|
-
|
|
50608
|
-
const mateChrName = this.genome.getChromosomeName(alignment.mate.chr);
|
|
50609
|
-
|
|
50610
|
-
const referenceFrameRight = createReferenceFrameWithAlignment(this.genome, mateChrName, referenceFrameLeft.bpPerPixel, viewportWidth, alignment.mate.position, alignment.lengthOnRef);
|
|
50442
|
+
for (let refFrame of this.referenceFrameList) {
|
|
50443
|
+
refFrame.bpPerPixel *= scaleFactor;
|
|
50444
|
+
}
|
|
50611
50445
|
|
|
50612
|
-
|
|
50613
|
-
const
|
|
50614
|
-
const
|
|
50446
|
+
const bpp = (end - start) / viewportWidth;
|
|
50447
|
+
const newReferenceFrame = new ReferenceFrame(this.genome, chr, start, end, bpp);
|
|
50448
|
+
const indexLeft = referenceFrameLeft ? this.referenceFrameList.indexOf(referenceFrameLeft) : this.referenceFrameList.length - 1;
|
|
50449
|
+
const indexRight = 1 + indexLeft;
|
|
50615
50450
|
|
|
50451
|
+
// TODO -- this is really ugly
|
|
50616
50452
|
const {$viewport} = this.trackViews[0].viewports[indexLeft];
|
|
50617
50453
|
const viewportColumn = viewportColumnManager.insertAfter($viewport.get(0).parentElement);
|
|
50618
50454
|
|
|
50619
50455
|
if (indexRight === this.referenceFrameList.length) {
|
|
50620
|
-
|
|
50621
|
-
this.referenceFrameList.push(referenceFrameRight);
|
|
50622
|
-
|
|
50456
|
+
this.referenceFrameList.push(newReferenceFrame);
|
|
50623
50457
|
for (let trackView of this.trackViews) {
|
|
50624
|
-
const viewport = createViewport(trackView, viewportColumn,
|
|
50458
|
+
const viewport = createViewport(trackView, viewportColumn, newReferenceFrame);
|
|
50625
50459
|
trackView.viewports.push(viewport);
|
|
50626
50460
|
}
|
|
50627
|
-
|
|
50628
50461
|
} else {
|
|
50629
|
-
|
|
50630
|
-
this.referenceFrameList.splice(indexRight, 0, referenceFrameRight);
|
|
50631
|
-
|
|
50462
|
+
this.referenceFrameList.splice(indexRight, 0, newReferenceFrame);
|
|
50632
50463
|
for (let trackView of this.trackViews) {
|
|
50633
|
-
const viewport = createViewport(trackView, viewportColumn,
|
|
50464
|
+
const viewport = createViewport(trackView, viewportColumn, newReferenceFrame);
|
|
50634
50465
|
trackView.viewports.splice(indexRight, 0, viewport);
|
|
50635
50466
|
}
|
|
50636
|
-
|
|
50637
50467
|
}
|
|
50638
50468
|
|
|
50469
|
+
|
|
50639
50470
|
this.centerLineList = this.createCenterLineList(this.columnContainer);
|
|
50640
50471
|
|
|
50641
|
-
|
|
50472
|
+
resize.call(this);
|
|
50473
|
+
await this.updateViews(true);
|
|
50642
50474
|
}
|
|
50643
50475
|
|
|
50644
50476
|
async removeMultiLocusPanel(referenceFrame) {
|
|
@@ -50667,7 +50499,13 @@ class Browser {
|
|
|
50667
50499
|
|
|
50668
50500
|
}
|
|
50669
50501
|
|
|
50670
|
-
|
|
50502
|
+
/**
|
|
50503
|
+
* Goto the locus represented by the selected referenceFrame, discarding all other panels
|
|
50504
|
+
*
|
|
50505
|
+
* @param referenceFrame
|
|
50506
|
+
* @returns {Promise<void>}
|
|
50507
|
+
*/
|
|
50508
|
+
async gotoMultilocusPanel(referenceFrame) {
|
|
50671
50509
|
|
|
50672
50510
|
const referenceFrameIndex = this.referenceFrameList.indexOf(referenceFrame);
|
|
50673
50511
|
|
|
@@ -50721,7 +50559,7 @@ class Browser {
|
|
|
50721
50559
|
|
|
50722
50560
|
this.updateUIWithReferenceFrameList();
|
|
50723
50561
|
|
|
50724
|
-
await this.updateViews(
|
|
50562
|
+
await this.updateViews();
|
|
50725
50563
|
|
|
50726
50564
|
}
|
|
50727
50565
|
|
|
@@ -50944,7 +50782,6 @@ class Browser {
|
|
|
50944
50782
|
}
|
|
50945
50783
|
|
|
50946
50784
|
|
|
50947
|
-
|
|
50948
50785
|
json["tracks"] = trackJson;
|
|
50949
50786
|
|
|
50950
50787
|
return json // This is an object, not a json string
|
|
@@ -50963,16 +50800,6 @@ class Browser {
|
|
|
50963
50800
|
return surl
|
|
50964
50801
|
}
|
|
50965
50802
|
|
|
50966
|
-
currentLoci() {
|
|
50967
|
-
const loci = [];
|
|
50968
|
-
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
|
|
50974
|
-
}
|
|
50975
|
-
|
|
50976
50803
|
/**
|
|
50977
50804
|
* Record a mouse click on a specific viewport. This might be the start of a drag operation. Dragging
|
|
50978
50805
|
* (panning) is handled here so that the mouse can move out of a specific viewport (e.g. stray into another
|
|
@@ -51010,12 +50837,22 @@ class Browser {
|
|
|
51010
50837
|
|
|
51011
50838
|
}
|
|
51012
50839
|
|
|
50840
|
+
/**
|
|
50841
|
+
* Track drag here refers to vertical dragging to reorder tracks, not horizontal panning.
|
|
50842
|
+
*
|
|
50843
|
+
* @param trackView
|
|
50844
|
+
*/
|
|
51013
50845
|
startTrackDrag(trackView) {
|
|
51014
50846
|
|
|
51015
50847
|
this.dragTrack = trackView;
|
|
51016
50848
|
|
|
51017
50849
|
}
|
|
51018
50850
|
|
|
50851
|
+
/**
|
|
50852
|
+
* Track drag here refers to vertical dragging to reorder tracks, not horizontal panning.
|
|
50853
|
+
*
|
|
50854
|
+
* @param dragDestination
|
|
50855
|
+
*/
|
|
51019
50856
|
updateTrackDrag(dragDestination) {
|
|
51020
50857
|
|
|
51021
50858
|
if (dragDestination && this.dragTrack) {
|
|
@@ -51090,12 +50927,9 @@ class Browser {
|
|
|
51090
50927
|
}
|
|
51091
50928
|
|
|
51092
50929
|
addWindowResizeHandler() {
|
|
51093
|
-
this.
|
|
50930
|
+
// Create a copy of the prototype "resize" function bound to this instance. Neccessary to support removing.
|
|
50931
|
+
this.boundWindowResizeHandler = resize.bind(this);
|
|
51094
50932
|
window.addEventListener('resize', this.boundWindowResizeHandler);
|
|
51095
|
-
|
|
51096
|
-
function windowResizeHandler() {
|
|
51097
|
-
this.resize();
|
|
51098
|
-
}
|
|
51099
50933
|
}
|
|
51100
50934
|
|
|
51101
50935
|
removeWindowResizeHandler() {
|
|
@@ -51178,7 +51012,7 @@ class Browser {
|
|
|
51178
51012
|
id: this.genome.id,
|
|
51179
51013
|
chromosomes: makeCircViewChromosomes(this.genome)
|
|
51180
51014
|
});
|
|
51181
|
-
this.circularViewVisible = show
|
|
51015
|
+
this.circularViewVisible = show;
|
|
51182
51016
|
|
|
51183
51017
|
}
|
|
51184
51018
|
|
|
@@ -51194,6 +51028,50 @@ class Browser {
|
|
|
51194
51028
|
}
|
|
51195
51029
|
}
|
|
51196
51030
|
|
|
51031
|
+
/**
|
|
51032
|
+
* Function called win window is resized, or visibility changed (e.g. "show" from a tab). This is a function rather
|
|
51033
|
+
* than class method because it needs to be copied and bound to specific instances of browser to support listener
|
|
51034
|
+
* removal
|
|
51035
|
+
*
|
|
51036
|
+
* @returns {Promise<void>}
|
|
51037
|
+
*/
|
|
51038
|
+
async function resize() {
|
|
51039
|
+
|
|
51040
|
+
const viewportWidth = this.calculateViewportWidth(this.referenceFrameList.length);
|
|
51041
|
+
|
|
51042
|
+
for (let referenceFrame of this.referenceFrameList) {
|
|
51043
|
+
|
|
51044
|
+
const index = this.referenceFrameList.indexOf(referenceFrame);
|
|
51045
|
+
|
|
51046
|
+
const {chr, genome} = referenceFrame;
|
|
51047
|
+
|
|
51048
|
+
const {bpLength} = genome.getChromosome(referenceFrame.chr);
|
|
51049
|
+
|
|
51050
|
+
const viewportWidthBP = referenceFrame.toBP(viewportWidth);
|
|
51051
|
+
|
|
51052
|
+
// viewportWidthBP > bpLength occurs when locus is full chromosome and user widens browser
|
|
51053
|
+
if (GenomeUtils.isWholeGenomeView(chr) || viewportWidthBP > bpLength) {
|
|
51054
|
+
// console.log(`${ Date.now() } Recalc referenceFrame(${ index }) bpp. viewport ${ StringUtils.numberFormatter(viewportWidthBP) } > ${ StringUtils.numberFormatter(bpLength) }.`)
|
|
51055
|
+
referenceFrame.bpPerPixel = bpLength / viewportWidth;
|
|
51056
|
+
} else {
|
|
51057
|
+
// console.log(`${ Date.now() } Recalc referenceFrame(${ index }) end.`)
|
|
51058
|
+
referenceFrame.end = referenceFrame.start + referenceFrame.toBP(viewportWidth);
|
|
51059
|
+
}
|
|
51060
|
+
|
|
51061
|
+
for (let {viewports} of this.trackViews) {
|
|
51062
|
+
viewports[index].setWidth(viewportWidth);
|
|
51063
|
+
}
|
|
51064
|
+
|
|
51065
|
+
}
|
|
51066
|
+
|
|
51067
|
+
this.updateUIWithReferenceFrameList();
|
|
51068
|
+
|
|
51069
|
+
//TODO -- update view only if needed. Reducing size never needed. Increasing size maybe
|
|
51070
|
+
|
|
51071
|
+
await this.updateViews(true);
|
|
51072
|
+
}
|
|
51073
|
+
|
|
51074
|
+
|
|
51197
51075
|
function handleMouseMove(e) {
|
|
51198
51076
|
|
|
51199
51077
|
e.preventDefault();
|