1bafe5a5569d58aa2446c60ef2a42f531d47698f
jrobinso
  Sun Sep 21 21:56:30 2025 -0700
igv dialog position fixes.  Misc minor cleanup

diff --git src/hg/js/igvFileHelper.js src/hg/js/igvFileHelper.js
index a2421d09671..658d8b1c95d 100644
--- src/hg/js/igvFileHelper.js
+++ src/hg/js/igvFileHelper.js
@@ -1,30 +1,30 @@
 // Helper functions for using igv.js with local files in the UCSC genome browser
 //
 // The UCSC browser does not use modules, so wrap code in a self-executing function to limit
 // scope of variables to this file.
 
 (function () {
     const indexExtensions = new Set(['bai', 'csi', 'tbi', 'idx', 'crai']);
     const requireIndex = new Set(['bam', 'cram']);
 
     // File scope variables
     const IGV_STORAGE_KEY = "igvSession";
     let filePicker = null;
     let isDragging = false;
     let sessionAutoSaveTimer = null;
-    window.igvBrowser = undefined;  // The gloal igv.js browser object, TODO -- perhaps this should be attached to "igv"
+    let igvBrowser = null;
 
     // Create a BroadcastChannel for communication between the UCSC browser page and the file picker page.
     const channel = new BroadcastChannel('igv_file_channel');
 
     // Message types for communication between browser page and file picker page
     const MSG = {
         SELECTED_FILES: 'selectedFiles',
         RESTORE_FILES: 'restoreFiles',
         REMOVED_TRACK: 'removedTrack',
         LOAD_URL: 'loadURL',
         FILE_PICKER_READY: 'filePickerReady',
         PING: 'ping',
         PONG: 'pong'
     };
 
@@ -62,31 +62,32 @@
                 throw new Error(`Connection to file ${this.name} is not available.  Please re-select the file` +
                     ' in the IGV File Manager window.');
             }
         }
     }
 
 
     /**
      * Initialize igv.js, creating an igvBrowser if a session is found in local storage.   The existence of a
      * global "igvBrowser" variable indicates igv.js has already been initialized.
      *
      * @returns {Promise<void>}
      */
     async function initIgvUcsc() {
 
-        if (window.igvBrowser) {
+        if (igvBrowser) {
+            // This can happend because initVars(), which calls initIgvUcsc(), is called from multiple places
             //console.log("igvBrowser already exists");
             return;
         }
 
         // Retrieve the igv session for this genome, if any,  from local storage.
         // TODO -- in the future this might come from the UCSC session (cart)
         const db = getDb();
         let sessionString = getSessionStorage()[db];
 
         if (sessionString) {
 
             const igvSession = JSON.parse(igv.uncompressSession(`blob:${sessionString}`));
 
             // Reconnect any file-based tracks to the actual File objects.
             if (igvSession.tracks) {
@@ -343,35 +344,38 @@
         if (sessionAutoSaveTimer !== null) {
             clearInterval(sessionAutoSaveTimer);
             sessionAutoSaveTimer = null;
         }
     }
 
 
     window.addEventListener("DOMContentLoaded", async () => {
 
         // The "Add IGV track" button handler.  The button opens the file picker window, unless
         // it is already open in which case it brings that window to the front.  Tracks are added
         // from the filePicker page by selecting track files.
         document.getElementById('hgtIgv').addEventListener('click', async function (e) {
             e.preventDefault(); // our
             if (filePicker && !filePicker.closed) {
+                showDialog("To add tracks please use the IGV File Manager window.");
                 filePicker.focus();
                 return;
-            } else {
+            }
+
+            else {
                 // A file picker may be open from a previous session. First ping it to see if it is still there,
-                // if it responds the user should be alerted from that window, no need to open a new window.
+                // if it responds the user should be alerted, if needed,  from a failed track load.
                 const responded = await pingFilePicker();
                 if (!responded) {
                     filePicker = openFilePicker();
                 }
             }
         });
 
         initializeDialog();
 
     });
 
     // Initialize a jQuery UI dialog used for user messages.
     function initializeDialog() {
 
         // Inject a hidden dive for an alert dialog.  We use this to report errors.
@@ -606,33 +610,33 @@
      * 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);
+        //console.log("Creating IGV browser with config: ", config);
 
-        let igvRow = document.getElementById("tr_igv")
+        let igvRow = document.getElementById("tr_igv");
         if (!igvRow) {
             // No existing igv row, insert one
             igvRow = 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,
@@ -668,50 +672,57 @@
         // as if the user had dragged a track image
         igvBrowser.on('trackdrag', e => {
                 isDragging = true;
                 const newStart = igvBrowser.referenceFrameList[0].start;
                 igv.ucscTrackpan(newStart);
             }
         );
 
         // Notify UCSC browser that igv.js track panning has ended.
         igvBrowser.on('trackdragend', () => {
                 isDragging = false;
                 igv.ucscTrackpanEnd();
             }
         );
 
-        window.igvBrowser = igvBrowser;
+        // Repaint name panels as needed
+        igvBrowser.on('trackheightchange', () => {
+            updateTrackNames();
+        })
+        igvBrowser.on('tracknamechange', () => {
+            updateTrackNames();
+        })
 
         // Start the session auto-save timer.  This will periodically save the igv session to local storage.
         // When / if we can reliably capture IGV state changes we can eliminate this and just save on state change.
         startSessionAutoSave();
 
+        window.igvBrowser = igvBrowser;  // Make available to hgTracks.js code
+
         return igvBrowser;
     }
 
-
     // Respond to messages from the filePicker window.
     channel.onmessage = async function (event) {
         const msg = event.data;
         if (!msg || !msg.type) return;
 
         switch (msg.type) {
 
             case MSG.SELECTED_FILES:
 
-                console.log("Received selected files: ", event.data.files);
+                //console.log("Received selected files: ", event.data.files);
                 const configs = getTrackConfigurations(event.data.files);
                 loadIGVTracks(configs);
 
                 break;
             case MSG.LOAD_URL:
                 loadIGVTracks([event.data.config]);
                 break;
 
             // NOTE: The file picker ready message does not appear to be used.
             case MSG.FILE_PICKER_READY:
                 const filesToRestore = JSON.parse(sessionStorage.getItem('filesToRestore'));
                 if (filesToRestore) {
                     channel.postMessage({type: MSG.RESTORE_FILES, files: filesToRestore});
                     sessionStorage.removeItem('filesToRestore');
                 }
@@ -719,31 +730,31 @@
         }
     };
 
     /**
      * Load one or more tracks into the igvBrowser.  If the igvBrowser does not exist it is created.
      *
      * @param configs
      * @returns {Promise<void>}
      */
     async function loadIGVTracks(configs) {
 
         if (configs.length > 0) {
 
             // Create igvBrowser if needed -- i.e. this is the first track being added.  State needs to be obtained
             // from the UCSC browser for genome and locus.
-            if (typeof (window.igvBrowser) === 'undefined' || window.igvBrowser === null) {
+            if (typeof (igvBrowser) === 'undefined' || igvBrowser === null) {
                 const defaultConfig = {
                     reference: await getMinimalReference(getDb()),
                     // locus: genomePos.get()
                 };
                 igvBrowser = await createIGVBrowser(defaultConfig);
             }
 
             // First search for existing tracks referencing the same files.  This is to handle the situation
             // of a user closing the file picker window, thus loosing file references, then reopening the file picker
             // to restore them.
             const newConfigs = [];
 
             for (let config of configs) {
 
                 const matchingTracks = igvBrowser.findTracks(t => config.id === t.id);