5294eee73d4f2a805657ca650552e65c22f857c6
tdreszer
  Fri May 2 13:46:57 2014 -0700
Vis changes are now queued for update because quick vis changes followed by refresh wer creating a race condition. Redmine 13164.
diff --git src/hg/js/hgTracks.js src/hg/js/hgTracks.js
index b7e61dd..2ba14d1 100644
--- src/hg/js/hgTracks.js
+++ src/hg/js/hgTracks.js
@@ -441,42 +441,44 @@
 
         if ($(obj).hasClass('noLink'))  // TITLE_BUT_NO_LINK
             return false;
 
         if (obj.href.match('#') || obj.target.length > 0) {
             //alert("Matched # ["+obj.href+"] or has target:"+obj.target);
             return true;
         }
         var thisForm=$(obj).parents('form');
         if(thisForm == undefined || $(thisForm).length == 0)
             thisForm=$("FORM");
         if($(thisForm).length > 1 )
             thisForm=$(thisForm)[0];
         if(thisForm != undefined && $(thisForm).length == 1) {
             //alert("posting form:"+$(thisForm).attr('name'));
-            return postTheForm($(thisForm).attr('name'),obj.href);
+            var href = obj.href + vis.cartUpdatesGet();
+            return postTheForm($(thisForm).attr('name'),href);
         }
         return true;
     }
 }
 
   ///////////////////////////////////////////////
  //// visibility (mixed with group toggle) /////
 ///////////////////////////////////////////////
 var vis = {
 
     enumOrder: new Array("hide", "dense", "full", "pack", "squish"),  // map cgi enum visibility codes to strings
+    cartUpdateQueue: {},
 
     update: function (track, visibility)
     {   // Updates visibility state in hgTracks.trackDb and any visible elements on the page.
         // returns true if we modify at least one select in the group list
         var rec = hgTracks.trackDb[track];
         var selectUpdated = false;
         $("select[name=" + escapeJQuerySelectorChars(track) + "]").each(function(t) {
             $(this).attr('class', visibility == 'hide' ? 'hiddenText' : 'normalText');
             $(this).val(visibility);
             selectUpdated = true;
         });
         if(rec) {
             rec.localVisibility = visibility;
         }
         return selectUpdated;
@@ -513,48 +515,87 @@
             return setTableRowVisibility(button, prefix, "hgtgroup", "group",false,arguments[2]);
         else
             return setTableRowVisibility(button, prefix, "hgtgroup", "group",false);
     },
 
     expandAllGroups: function (newState)
     {   // Set visibility of all track groups to newState (true means expanded).
         // This code also modifies the corresponding hidden fields and the gif's of the +/- img tag.
         imageV2.markAsDirtyPage();
         $(".toggleButton[id$='_button']").each( function (i) {  // works for old img type AND new BUTTONS_BY_CSS
             vis.toggleForGroup(this,this.id.substring(0,this.id.length - 7),newState); // clip '_button' suffix
         });
         return false;
     },
     
+    cartUpdatesGet: function ()
+    {   // returns any outstanding cart updates as a string, and clears the queue
+        var updates = "";
+        for (var track in vis.cartUpdateQueue) {
+            updates += "&" + track + "=" + vis.cartUpdateQueue[track];
+        }
+
+        vis.cartUpdateQueue = {};
+        return updates;
+    },
+
+    // NOTE: could update in background, however, failing to hit "refresh" is a user choice
+    // cartUpdatesViaAjax: function ()
+    // {   // Called via timer: updates the cart via setVars.
+    // if (Object.keys(vis.cartUpdateQueue).length === 0)
+    //     return;
+    // 
+    //     var tracks = [];
+    //     var newVis = [];
+    //     for (var track in vis.cartUpdateQueue) {
+    //         tracks.push(track)
+    //         newVis.push(vis.cartUpdateQueue[track]);
+    //     }
+    //     if (tracks.length === 0)
+    //         return;
+    //     vis.cartUpdateQueue = {};
+    //     setCartVars(tracks,newVis,async=false); // sync to avoid another race condition mess
+    // },
+
+    cartUpdateAddToQueue: function (track,newVis)
+    {   // creates a string of updates to save for ajax batch or a submit
+        vis.cartUpdateQueue[track] = newVis;
+        
+        // NOTE: could update in background, however, failing to hit "refresh" is a user choice
+        // first in queue, schedule background update
+        // if (Object.keys(vis.cartUpdateQueue).length === 1)
+        //     setTimeout("vis.cartUpdatesViaAjax();",10000);
+    },
+
     initForAjax: function()
     {   // To better support the back-button, it is good to eliminate extraneous form puts
         // Towards that end, we support visBoxes making ajax calls to update cart.
         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();
                 imageV2.highlightRegion();
                 $(this).attr('class', 'hiddenText');
             } else
                 $(this).attr('class', 'normalText');
             
-            setCartVar(track,$(this).val());
+            vis.cartUpdateAddToQueue(track,$(this).val());
             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 necessarty because intiForAjax() disables them on submit.
     {
         $('select.normalText,select.hiddenText').attr('disabled',false);
     }
@@ -3204,31 +3245,32 @@
         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({
                 type: "GET",
                 url: "../cgi-bin/hgTracks",
-                data: params + "&hgt.trackImgOnly=1&hgt.ideogramToo=1&hgsid=" + getHgsid(),
+                data: params + "&hgt.trackImgOnly=1&hgt.ideogramToo=1&hgsid=" + 
+                                getHgsid() + vis.cartUpdatesGet(),
                 dataType: "html",
                 trueSuccess: imageV2.updateImgAndMap,
                 success: catchErrorOrDispatch,
                 error: errorHandler,
                 cmd: 'wholeImage',
                 loadingId: showLoadingImage("imgTbl"),
                 disabledEle: disabledEle,
                 currentId: currentId,
                 currentIdYOffset: currentIdYOffset,
                 cache: false
             });
     },
     
     highlightRegion: function()
     // highlight vertical region in imgTbl based on hgTracks.highlight (#709).