f01fbeba761e2b110036a9a71c05b373895ced97
braney
  Mon Feb 26 13:35:01 2018 -0800
make sure collection tree is loaded before removing wait screen.  Make
hgTracks ajax requests synchronous to remove race condition

diff --git src/hg/js/hgCollection.js src/hg/js/hgCollection.js
index 97612e7..5b8a041 100644
--- src/hg/js/hgCollection.js
+++ src/hg/js/hgCollection.js
@@ -1,27 +1,27 @@
 // hgCollection.js - custom collection builder
 
 // Copyright (C) 2018 The Regents of the University of California
 
 var hgCollection = (function() {
-    //$("#workScreen").css("display","block");
     var selectedNode;
     var selectedTree;
     var $tracks;  // the #tracks object
     var trees = [];
     var isDirty = false;
     var goTracks = false;
+    var loadFinished = false;
     var doAjaxAsync = true;
     var emptyCollectionText;
     var addWithoutCollectionText;
 
     function currentTrackItems(node) {
         // populate the menu for the currentCollection tree
         var items = {
             addItem: { // The "add" menu item
                 label: "Add",
                 action: function () {
                     if (selectedNode === undefined) {
                         alert(addWithoutCollectionText);
                         return;
                     }
                     var nodeIds = $("#tracks").jstree( "get_selected");
@@ -253,32 +253,31 @@
         $.ajax({
             data:  requestData ,
             async: doAjaxAsync,
             dataType: "JSON",
             type: "PUT",
             url: "hgCollection?cmd=saveCollection",
             trueSuccess: updatePage,
             success: catchErrorOrDispatch,
             error: errorHandler,
         });
     }
 
     function saveCollections(trees) {
         // called when the "Save" button is pressed
         $("#workScreen").css("display","block");
-        $(selectedTree).on("open_all.jstree", finishSaving);
-        $(selectedTree).jstree('open_all');
+        finishSaving();
     }
 
     function rebuildLabel() {
         // rebuild the label for tree item
         var newText = selectedNode.li_attr.shortlabel + "   (" + selectedNode.li_attr.longlabel + ")";
         $(selectedTree).jstree('rename_node', selectedNode, newText);
         isDirty = true;
     }
 
     function colorChange() {
         // change the color for a track
         isDirty = true;
         var color = $("#customColorPicker").spectrum("get"); 
         $('#customColorInput').val(color);
     }
@@ -352,64 +351,79 @@
             }
             $(selectedTree).jstree( "delete_node", node);
         }
     }
 
     function buildCollections(node, cb) {
         // called when jstree wants data to open a node for the collection tree
         cb.call(this, collectionData[node.id]);
     }
 
     function buildTracks(node, cb) {
         // called when jstree wants data to open a node for the tracks tree
         cb.call(this, trackData[node.id]);
     }
 
-    function treeReady() {
-        // called when either the track or collection tree has been loaded
+    function trackTreeReady() {
+        // called when the track tree has been loaded
+        var firstChild = $(this).find("li").first();
+        $(this).jstree("select_node", $(firstChild).attr("id"));
+    }
+
+    function collectionTreeReady() {
+        // called when the collection tree has been loaded
+        //$(selectedTree).on("load_all.jstree", collectionLoadFinished);
+        var rootNode = $(selectedTree).jstree("get_node", '#');
+        $(selectedTree).jstree('load_all', rootNode, collectionLoadFinished);
         var firstChild = $(this).find("li").first();
         $(this).jstree("select_node", $(firstChild).attr("id"));
     }
 
     function colorInputChange () {
         // called when the color text edit box changes
         $("#customColorPicker").spectrum("set",$("#customColorInput").val());
     }
 
+    function collectionLoadFinished() {
+        loadFinished = true;
+        $("#workScreen").css("display","none");
+    }
+
     function init() {
         // called at initialization time
         $body = $("body");
         $("#customColorInput").change(colorInputChange);
 
         emptyCollectionText = $('#emptyCollectionText').text();
         addWithoutCollectionText = $('#addWithoutCollectionText').text();
 
         // block user input when ajax is running
         $(document).on({
             ajaxStart: function() { $body.addClass("loading");    },
             ajaxStop: function() { $body.removeClass("loading"); }    
         });
 
         $('.gbButtonGoContainer').click(submitForm);
        
         window.addEventListener("beforeunload", function (e) {
             if (isDirty) {
                 doAjaxAsync = false;
+                if (loadFinished)
                     saveCollections(trees);
+                else
+                    e.returnValue = "ask to leave";
             }
-
-            return undefined;
         });
 
         $("#saveCollections").click ( function() { saveCollections(trees); } );
         $("#discardChanges").click ( function () { isDirty = false; window.location.reload(); });
 
         $( "#newCollectionDialog" ).dialog({ modal: true, 
             width: "50%", 
             autoOpen: false,
             dialogClass: 'myTitleClass'
             });
         $("#newCollection").click ( dialogCollection );
         $("#doNewCollection").click ( newCollection );
 
         var trackOpt = {
             hideAfterPaletteSelect : true,
@@ -418,31 +432,31 @@
             showInput: true,
             preferredFormat: "hex",
             change: colorChange,
         };
 
         $("#customColorPicker").spectrum(trackOpt);
         $.jstree.defaults.core.check_callback = checkCallback;
         $.jstree.defaults.core.themes.dots = true;
         $.jstree.defaults.contextmenu.show_at_node = false;
         var addedOne = false;
         if ( $("#currentCollection ul").length !== 0) {
             addedOne = true;
         }
 
         var newTree=$('#currentCollection');
-        newTree.on("ready.jstree", treeReady);
+        newTree.on("ready.jstree", collectionTreeReady);
         newTree.jstree({
            'plugins' : ['dnd', 'conditionalselect', 'contextmenu'],
            //'plugins' : [ 'conditionalselect', 'contextmenu'],
            'contextmenu': { "items" : currentCollectionItems},
            'core': {
                "data" : buildCollections,
                "dblclick_toggle" : false,
             },
            'dnd': {
                 "check_callback" : checkCallback,
             }
         });
         $(newTree).on("select_node.jstree", selectTreeNode);
         $(newTree).on("dblclick.jstree", doubleClickTreeNode);
         $(newTree).on("move_node.jstree", moveNode);
@@ -458,36 +472,35 @@
         treeDiv=$('#tracks');
         treeDiv.jstree({
                'plugins' : ['conditionalselect', 'contextmenu'],
                'contextmenu': { "items" : currentTrackItems},
                'dnd': {
                     "check_callback" : checkCallback,
                    'always_copy' : true,
                     is_draggable: isDraggable,
                },
                'core' :  {
                    "data" : buildTracks,
                    "check_callback" : checkCallback,
                    "dblclick_toggle" : false,
                 },
         });
-        treeDiv.on("ready.jstree", treeReady);
+        treeDiv.on("ready.jstree", trackTreeReady);
         treeDiv.on("select_node.jstree", function (evt, data)  {
             $(evt.target).jstree("open_node", data.node);
         });
         treeDiv.on('click', '.jstree-themeicon ', plusHit);
-        $("#workScreen").css("display","none");
     }
 
    function submitForm() {
     // Submit the form (from GO button -- as in hgGateway.js)
     // Show a spinner -- sometimes it takes a while for hgTracks to start displaying.
         $('.gbIconGo').removeClass('fa-play').addClass('fa-spinner fa-spin');
         goTracks = true;
         saveCollections(trees);
     }
 
     function updatePage(responseJson) {
         // called after AJAX call
         isDirty = false;
         if (!responseJson) {
             return;