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<Browser>}
      */
     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