b9c90b025f5cc6cefe1fa0347293ce5000446988 jrobinso Sun Sep 21 23:38:22 2025 -0700 Delay inserting IGV row until dom content is loaded. Inserting the row before the image table is ready sends the browser into a race condition that requires completely closing and restarting. It's not 100% sure that waiting for a dom content load event will fix this. diff --git src/hg/js/igvFileHelper.js src/hg/js/igvFileHelper.js index 16ed87d132b..455ad8bcd94 100644 --- src/hg/js/igvFileHelper.js +++ src/hg/js/igvFileHelper.js @@ -511,34 +511,40 @@ trackLabelDiv.style.height = `{track.trackView.viewports[0].viewportElement.clientHeight}px`; trackLabelDiv.style.textAlign = 'left'; trackLabelDiv.style.overflow = 'hidden'; nameDiv.appendChild(trackLabelDiv); } top += track.trackView.viewports[0].viewportElement.clientHeight; // Adjust top for the next element } } /** * Insert a new row into the image table for the IGV browser. The igv browser object is a super-track of * sorts, containing all igv.js tracks in the session. In the future we might want to allocate a row * for each igv.js track, but this will require some refactoring of igv.js. * + * The function returns a Promise that resolves with the new row element after the DOM content has fully loaded. + * This (hopefully) ensures that the table is completely rendered before the new row is added, which can help + * with the initialization of plugins like tableDnD. + * * @returns {HTMLTableRowElement} */ function insertIGVRow() { + return new Promise(resolve => { + const doInsert = () => { const imgTbl = document.getElementById('imgTbl'); const tbody = imgTbl.querySelector('tbody'); const igvRow = document.createElement('tr'); igvRow.id = "tr_igv"; igvRow.classList.add('imgOrd', 'trDraggable'); igvRow.style.position = 'relative'; // For positioning the overlay // First cell for drag handle and left sidebar const td1 = document.createElement('td'); td1.className = 'dragHandle'; td1.style.position = 'relative'; igvRow.appendChild(td1); const leftSidebarDiv = document.createElement('div'); leftSidebarDiv.id = 'igv_leftsidebar'; @@ -562,84 +568,65 @@ nameDiv.style.top = '0'; nameDiv.style.bottom = '0'; nameDiv.style.left = '0'; nameDiv.style.right = '0'; td2.appendChild(nameDiv); // Third cell for the IGV browser div const td3 = document.createElement('td'); igvRow.appendChild(td3); const igvDiv = document.createElement('div'); igvDiv.id = 'igv_div'; igvDiv.style.width = 'auto'; td3.appendChild(igvDiv); - // Create a transparent overlay for the entire row --currently disabled - // const overlay = document.createElement('div'); - // overlay.style.position = 'absolute'; - // overlay.style.top = '0'; - // overlay.style.left = '0'; - // overlay.style.width = '100%'; - // overlay.style.height = '100%'; - // overlay.style.backgroundColor = 'rgba(75, 255, 75, 0.2)'; - // overlay.style.display = 'none'; // Initially hidden - // overlay.style.pointerEvents = 'none'; // Allow clicks to pass through - // igvRow.appendChild(overlay); - // - // // Show/hide overlay on mouseover/mouseout of the left sidebar - // leftSidebarDiv.addEventListener('mouseenter', () => { - // overlay.style.display = 'block'; - // }); - // leftSidebarDiv.addEventListener('mouseleave', () => { - // overlay.style.display = 'none'; - // }); - // nameDiv.addEventListener('mouseenter', () => { - // overlay.style.display = 'block'; - // }); - // nameDiv.addEventListener('mouseleave', () => { - // overlay.style.display = 'none'; - // }); - // - tbody.appendChild(igvRow); - return igvRow; + resolve(igvRow); + }; + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', doInsert); + } else { + doInsert(); + } + }); } /** * Create an IGV browser instance and insert it into the image table in a new row. The IGV browser essentially becomes * a track in the UCSC browser context. This function is called when the user adds the first IGV track, or * the igv session is restored on page reload. * * @param config -- The IGV browser configuration object. Must include a reference genome, but might also include * an initial locus or tracks. * @returns {Promise} */ async function createIGVBrowser(config) { // Override locus in the IGV session with the UCSC locus // TODO -- should we use genomePos here? const ucscImageWidth = document.getElementById("td_data_ruler").clientWidth; const resolution = (hgTracks.winEnd - hgTracks.winStart) / ucscImageWidth; config.locus = {chr: hgTracks.chromName, start: hgTracks.winStart, bpPerPixel: resolution}; //console.log("Creating IGV browser with config: ", config); let igvRow = document.getElementById("tr_igv"); if (!igvRow) { // No existing igv row, insert one - igvRow = insertIGVRow(); + igvRow = await insertIGVRow(); } // Ammend the igv config to remove most of the IGV widgets. We only want the track display area. Object.assign(config, { showNavigation: false, showIdeogram: false, showRuler: false, //showSequence: false, // Uncomment this to hide igv sequence track showAxis: false, showTrackDragHandles: false, showAxisColumn: false, gearColumnPosition: 'left', showGearColumn: false, showTrackLabels: false, // Uncomment this to hide the "floating div" igv track labels