c8eef4a1e85bcfd8b41e87e287cbf15f09fc357a
jcasper
  Sun Aug 16 22:19:22 2020 -0700
Replacing beforeUnload synchronous updates (blocked by Chrome) with async updates.
Also fixing the suggestion box, so following a suggestion turns on the track and highlights the item.
refs #25017, #25703

diff --git src/hg/js/hgTracks.js src/hg/js/hgTracks.js
index 2f5dc77..33f1cbe 100644
--- src/hg/js/hgTracks.js
+++ src/hg/js/hgTracks.js
@@ -798,87 +798,73 @@
             var updates = cart.varsToUrlData(); // clears the queue
             if (!url || url.length === 0)
                 return updates;
 
             if (updates.length > 0) {
                 var dataOnly = (url.indexOf("cgi-bin") === -1); // all urls should be to our cgis
                 if (!dataOnly && url.lastIndexOf("?") === -1)
                     url += "?" + updates;
                 else
                     url += '&' + updates;
             }
         }
         return url;
     },
     
-    beforeUnload: function ()
-    {   // named function that can be bound and unbound to beforeunload event
-        // Makes sure any outstanding queued updates are sent before leaving the page.
-        //console.log('cart.beforeUnload: '+objKeyCount(cart.updateQueue)+' vars');
-        cart.setVarsObj( {}, null, false ); // synchronous
-    },
-    
     varsToUrlData: function (varsObj)
     {   // creates a url data (var1=val1&var2=val2...) string from vars object and queue
         // The queue will be emptied by this call.
         cart.queueVarsObj(varsObj); // lay ontop of queue, to give new values precedence
         
         // Now convert to url data and clear queue
         var urlData = '';
         if (cart.updatesWaiting()) {
             //console.log('cart.varsToUrlData: '+objKeyCount(cart.updateQueue)+' vars');
             urlData = varHashToQueryString(cart.updateQueue);
             cart.updateQueue = {};
         }
         return urlData;
     },
     
     setVarsObj: function (varsObj, errFunc, async)
     {   // Set all vars in a var hash, appending any queued updates
+        // The default behavior is async = true
         //console.log('cart.setVarsObj: were:'+objKeyCount(cart.updateQueue) + 
         //            ' new:'+objKeyCount(varsObj);
         cart.queueVarsObj(varsObj); // lay ontop of queue, to give new values precedence
         
         // Now ajax update all in queue and clear queue
         if (cart.updatesWaiting()) {
             setVarsFromHash(cart.updateQueue, errFunc, async);
             cart.updateQueue = {};
         }
     },
     
     setVars: function (names, values, errFunc, async)
     {   // ajax updates the cart, and includes any queued updates.
         cart.updateSessionPanel();      // handles hide from left minibutton
         cart.setVarsObj(arysToObj(names, values), errFunc, async);
     },
 
     queueVarsObj: function (varsObj)
     {   // Add object worth of cart updates to the 'to be updated' queue, so they can be sent to
         // the server later. Note: hash allows overwriting previous updates to the same variable.
         if (typeof varsObj !== 'undefined' && objNotEmpty(varsObj)) {
             //console.log('cart.queueVarsObj: were:'+objKeyCount(cart.updateQueue) + 
             //            ' new:'+objKeyCount(varsObj));
             for (var name in varsObj) {
                 cart.updateQueue[name] = varsObj[name];
-                
-                // Could update in background, however, failing to hit "refresh" is user choice
-                // first in queue, schedule background update
-                if (objKeyCount(cart.updateQueue) === 1) {
-                    // By unbind/bind, we assure that there is only one instance bound
-                    $(window).unbind('beforeunload', cart.beforeUnload); 
-                    $(window).bind(  'beforeunload', cart.beforeUnload); 
-                }
             }
         }
     },
 
     addVarsToQueue: function (names,values)
     {   // creates a string of updates to save for ajax batch or a submit
         cart.queueVarsObj(arysToObj(names,values));
     },
 
     updateSessionPanel: function()
     {
     if (typeof recTrackSetsDetectChanges === 'undefined' || recTrackSetsDetectChanges === null)
         return;
 
     // change color of text
@@ -924,30 +910,31 @@
     {   // return current visibility for given track
         var rec = hgTracks.trackDb[track];
         if (rec) {
             if (rec.localVisibility) {
                 return rec.localVisibility;
             } else {
                 return vis.enumOrder[rec.visibility];
             }
         } else {
             return null;
         }
     },
 
     makeTrackVisible: function (track)
     {   // Sets the vis box to visible, and ques a cart update, but does not update the image
+        // Trusts that the cart update will be submitted later.
         if (track && vis.get(track) !== "full") {
             vis.update(track, 'pack');
             cart.addVarsToQueue([track], ['pack']);
         }
     },
 
     toggleForGroup: function (button, prefix)
     {   // toggle visibility of a track group; prefix is the prefix of all the id's of tr's in the
         // relevant group. This code also modifies the corresponding hidden fields and the gif
         // of the +/- img tag.
         imageV2.markAsDirtyPage();
         if (arguments.length > 2)
             return setTableRowVisibility(button, prefix, "hgtgroup", "group",false,arguments[2]);
         else
             return setTableRowVisibility(button, prefix, "hgtgroup", "group",false);
@@ -970,31 +957,31 @@
         var sels = $('select.normalText,select.hiddenText');
         $(sels).change(function() {
             var track = $(this).attr('name');
             if ($(this).val() === 'hide') {
                 var rec = hgTracks.trackDb[track];
                 if (rec)
                     rec.visibility = 0;
                 // else Would be nice to hide subtracks as well but that may be overkill
                 $(document.getElementById('tr_' + track)).remove();
                 cart.updateSessionPanel();
                 imageV2.highlightRegion();
                 $(this).attr('class', 'hiddenText');
             } else
                 $(this).attr('class', 'normalText');
             
-            cart.addVarsToQueue([track], [$(this).val()]);
+            cart.setVars([track], [$(this).val()]);
             imageV2.markAsDirtyPage();
             return false;
         });
         // Now we can rid the submt of the burden of all those vis boxes
         var form = $('form#TrackForm');
         $(form).submit(function () {
             $('select.normalText,select.hiddenText').attr('disabled',true);
         });
         $(form).attr('method','get');
     },
 
     restoreFromBackButton: function()
     // Re-enabling vis dropdowns is necessary because initForAjax() disables them on submit.
     {
         $('select.normalText,select.hiddenText').attr('disabled',false);
@@ -2314,31 +2301,32 @@
                                     success: catchErrorOrDispatch,
                                     error: errorHandler,
                                     cmd: cmd,
                                     loadingId: showLoadingImage("imgTbl"),
                                     cache: false
                                 });
                         } else {
                             // do a full page refresh to update hgTracks image
                             jQuery('body').css('cursor', 'wait');
                             var ele;
                             if (document.TrackForm)
                                 ele = document.TrackForm;
                             else
                                 ele = document.TrackHeaderForm;
                             if (name)
-                                cart.addVarsToQueue(['hgFind.matches'], [name]);
+                                // Add or update form input with gene to highlight
+                                suggestBox.updateFindMatches(name);
                             ele.submit();
                         }
                     }
                 }
         } else if (cmd === 'zoomCodon' || cmd === 'zoomExon') {
             var num, ajaxCmd, msg;
             if (cmd === 'zoomCodon') {
                 msg = "Please enter the codon number to jump to:";
                 ajaxCmd = 'codonToPos';
             } else {
                 msg = "Please enter the exon number to jump to:";
                 ajaxCmd = 'exonToPos';
             }
             rightClick.myPrompt(msg, function(results) {
                 $.ajax({
@@ -2581,31 +2569,31 @@
                 } else if (tdbIsFolderContent(rec)) {
                     // supertrack children need to have _sel set to trigger superttrack reshaping
                     cart.setVars( [ id, id+"_sel" ], [ 'hide', 0 ] ); 
                 } else {
                     cart.setVars([id], ['hide']);  // Others, just set vis hide.
                 }
                 $(document.getElementById('tr_' + id)).remove();
                 imageV2.afterImgChange(true);
             } else if (!imageV2.mapIsUpdateable) {
                 jQuery('body').css('cursor', 'wait');
                 if (selectUpdated) {
                     // assert(document.TrackForm);
                     document.TrackForm.submit();
                 } else {
                         // Add vis update to queue then submit
-                        cart.addVarsToQueue([id], [cmd]);
+                        cart.setVars([id], [cmd], null, false); // synchronous
                         document.TrackHeaderForm.submit();
                 }
             } else {
                 imageV2.requestImgUpdate(id, id + "=" + cmd, "", cmd);
             }
         }
     },
 
     makeHitCallback: function (title)
     {   // stub to avoid problem with a function closure w/n a loop
         return function(menuItemClicked, menuObject) {
             rightClick.hit(menuItemClicked, menuObject, title); return true;
         };
     },
 
@@ -3696,43 +3684,43 @@
         reg = new RegExp("(<span class='trackTiming'>[\\S\\s]+?</span>)");
         a = reg.exec(response);
         if (a && a[1]) {
             $('.trackTiming').replaceWith(a[1]);
         }
     },
 
     loadSuggestBox: function ()
     {
         if ($('#positionInput').length) {
             if (!suggestBox.initialized) { // only call init once
                 suggestBox.init(getDb(),
                             $("#suggestTrack").length > 0,
                             function (item) {
                                 genomePos.set(item.id, getSizeFromCoordinates(item.id));
+                                if ($("#suggestTrack").length && $('#hgFindMatches').length) {
+                                    // Set cart variables to open the hgSuggest gene track and highlight
+                                    // the chosen transcript.  These variables will be submittted by the goButton
+                                    // click handler.
+                                    vis.makeTrackVisible($("#suggestTrack").val());
+                                    cart.addVarsToQueue(["hgFind.matches"],[$('#hgFindMatches').val()]);
+                                }
                                 $("#goButton").click();
                             },
                             function (position) {
                                 genomePos.set(position, getSizeFromCoordinates(position));
-                            });
                             }
-            // Make sure suggestTrack is visible when user chooses via gene select (#3484).
-            if ($("#suggestTrack").length) {
-                $(document.TrackForm || document.TrackHeaderForm).submit(function(event) {
-                                               if ($('#hgFindMatches').length) {
-                                                   vis.makeTrackVisible($("#suggestTrack").val());
-                                               }
-                                           });
+                );
             }
         }
     },
     
     afterImgChange: function (dirty)
     {   // Standard things to do when manipulations change image without ajax update.
         dragReorder.init();
         dragSelect.load(false);
         imageV2.highlightRegion();
         if (dirty)
             imageV2.markAsDirtyPage();
     },
     
     afterReload: function (id)
     {   // Reload various UI widgets after updating imgTbl map.
@@ -4181,34 +4169,30 @@
 
     navigateInPlace: function (params, disabledEle, keepCurrentTrackVisible)
     {   // request an hgTracks image, using params
         // disabledEle is optional; this element will be enabled when update is complete
         // If keepCurrentTrackVisible is true, we try to maintain relative position of the item
         // under the mouse after the in-place update.
         // Tim thinks we should consider disabling all UI input while we are doing in-place update.
         // TODO: waitOnFuction?
     
         // No ajax image update if there are too many tracks!
         if (imageV2.manyTracks()) {
             imageV2.fullReload(params);
             return false;  // Shouldn't return from fullReload but I have seen it in FF
         }
     
-        // If UCSC Genes (or any suggestion) is supposed to be made visible, then do so
-        if ($("#suggestTrack").length && $('#hgFindMatches').length)
-            vis.makeTrackVisible($("#suggestTrack").val());
-
         jQuery('body').css('cursor', 'wait');
         var currentId, currentIdYOffset;
         if (keepCurrentTrackVisible) {
             var item = rightClick.currentMapItem || imageV2.lastTrack;
             if (item) {
                 var top = $(document.getElementById("tr_" + item.id)).position().top;
                 if (top >= $(window).scrollTop()
                 || top < $(window).scrollTop() + $(window).height()) {
                     // don't bother if the item is not currently visible.
                     currentId = item.id;
                     currentIdYOffset = top - $(window).scrollTop();
                 }
             }
         }
         $.ajax({