773aa9879899908033ea6e64d3344edf069a578e
angie
  Wed Apr 20 14:47:01 2016 -0700
Performance improvements: don't do any unnecessary drawing before the first uiState arrives; autocompleteFromTree is actually quite slow so defer it until after the page is displayed.

diff --git src/hg/js/hgGateway.js src/hg/js/hgGateway.js
index 24a851f..3e4181e 100644
--- src/hg/js/hgGateway.js
+++ src/hg/js/hgGateway.js
@@ -790,79 +790,86 @@
             // When user clicks on icon, set the taxId (default database);
             // scroll the image to that species and clear the species autocomplete input.
             onClick = setTaxId.bind(null, taxId, null, true, true);
             // Onclick for both the icon and its sibling label:
             $('.jwIconSprite' + name).parent().children().click(onClick);
         }
     }
 
     function addCategory(cat, item) {
         // Clone item, add category: cat to it and return it (helper function, see below).
         var clone = {};
         _.assign(clone, item, { category: cat });
         return clone;
     }
 
-    function autocompleteFromTree(node) {
+    function autocompleteFromNode(node) {
         // Traverse dbDbTree to make autocomplete result lists for all non-leaf node labels.
         // Returns an object mapping each label of node and descendants to a list of
         // result objects with the same structure that we'd get from a server request.
         if (! node) {
             return;
         }
         var searchObj = {};
         var myResults = [];
         var label = node[0], taxId = node[1], kids = node[3];
         var addMyLabel;
         if (!kids || kids.length === 0) {
             // leaf node: return autocomplete result for species
             myResults = [ { genome: label,
                             label: label,
                             taxId: taxId } ];
         } else {
             // Accumulate the list of all children's result lists;
             // keep each's child searchObj mappings unless the child is a leaf
             // (which would be redundant with server autocomplete results).
             addMyLabel = addCategory.bind(null, label);
             myResults = _.flatten(
                 _.map(kids, function(kid) {
                     var kidLabel = kid[0], kidKids = kid[3];
-                    var kidObj = autocompleteFromTree(kid);
+                    var kidObj = autocompleteFromNode(kid);
                     // Clone kid's result list and add own label as category:
                     var kidResults = _.map(kidObj[kidLabel], addMyLabel);
                     // Add kid's mappings to searchObj only if kid is not a leaf.
                     if (kidKids && kidKids.length > 0) {
                         _.assign(searchObj, kidObj);
                     }
                     return kidResults;
                 })
             );
         }
         // Exclude some overly broad categories:
         if (label !== 'root' && label !== 'cellular organisms') {
             searchObj[label] = myResults;
         }
+        return searchObj;
+    }
+
+    function autocompleteFromTree(node, searchObj) {
+        // Traverse dbDbTree to make autocomplete result lists for all non-leaf node labels.
+        // searchObj is extended to map each label of node and descendants to a list of
+        // result objects with the same structure that we'd get from a server request.
+        _.assign(searchObj, autocompleteFromNode(node));
         // Add aliases for some common names that map to scientific names in the tree.
         _.forEach(commonToSciNames, function(sciName, commonName) {
             var label, addMyLabel;
             if (searchObj[sciName]) {
                 label = sciName + ' (' + commonName + ')';
                 addMyLabel = addCategory.bind(null, label);
                 searchObj[commonName] = _.map(searchObj[sciName], addMyLabel);
             }
         });
-        return searchObj;
     }
 
     function makeStripe(id, color, stripeHeight, scrollTop, onClickStripe) {
         // Return an empty div with specified background color and height
         var $stripe = $('<div class="jwRainbowStripe">');
         $stripe.attr('id', 'rainbowStripe' + id);
         $stripe.attr('title', 'Click to scroll the tree display');
         $stripe.css('background-color', color);
         $stripe.height(stripeHeight);
         $stripe.click(onClickStripe.bind(null, scrollTop));
         return $stripe;
     }
 
     function makeRainbowSliderStripes($slider, onClickStripe, svgHeight, stripeColors, stripeTops) {
         // Set up the rainbow slider bar for the speciesPicker.
@@ -1083,37 +1090,35 @@
             }
         } else {
             // parent node: splice out any child nodes with no active leaves
             for (i = kids.length - 1;  i >= 0;  i--) {
                 if (pruneInactive(kids[i], activeGenomes, activeTaxIds)) {
                     hasActiveLeaf = true;
                 } else {
                     kids.splice(i, 1);
                 }
             }
         }
         return hasActiveLeaf;
     }
 
     function drawSpeciesPicker() {
-        // Prune inactive genomes from dbDbTree.
         // If dbDbTree is nonempty and SVG is supported, draw the tree; if SVG is not supported,
         // use the space to suggest that the user install a better browser.
         // If dbDbTree doesn't exist, leave the "Represented Species" section hidden.
         var svg, spTree, stripeTops;
-        var activeTaxIds = _.invert(activeGenomes);
-        if (dbDbTree && pruneInactive(dbDbTree, activeGenomes, activeTaxIds)) {
+        if (dbDbTree) {
             if (document.createElementNS) {
                 // Draw the phylogenetic tree and do layout adjustments
                 svg = document.getElementById('speciesTree');
                 spTree = speciesTree.draw(svg, dbDbTree, uiState.hubs,
                                           { onClickSpeciesName: 'hgGateway.onClickSpeciesLabel',
                                             onClickHubName: 'hgGateway.onClickHubName',
                                             hgHubConnectUrl: 'hgHubConnect?hgsid=' + window.hgsid,
                                             containerWidth: $('#speciesPicker').width()
                                             });
                 setSpeciesPickerSizes(spTree.width, spTree.height);
                 highlightLabelForDb(uiState.db, uiState.taxId);
                 stripeTops = rainbow.draw(svg, dbDbTree,
                                           spTree.yTree, spTree.height, spTree.leafTops);
             } else {
                 $('#speciesTreeContainer').html(getBetterBrowserMessage);
@@ -1536,56 +1541,64 @@
         $form.submit();
     }
 
     function replaceHgsidInLinks() {
         // Substitute '$hgsid' with real hgsid in <a> href's.
         $('a').each(function(ix, aEl) {
             var href = aEl.getAttribute('href');
             if (href && href.indexOf('$hgsid') >= 0) {
                 aEl.setAttribute('href', href.replace('$hgsid', window.hgsid));
             }
         });
     }
 
     function init() {
         // Boot up the page; initialize elements and install event handlers.
+        var searchObj = {};
+        // We need a bound function to pass into autocompleteCat.init below;
+        // however, autocompleteFromTree is even slower than drawing the tree because of
+        // all the copying.  So bind now, fill in searchObj later.
+        var processSpeciesResults = processSpeciesAutocompleteItems.bind(null, searchObj);
         cart.setCgi('hgGateway');
         cart.debug(debugCartJson);
         // Get state from cart
         cart.send({ getUiState: {} }, handleRefreshState);
         cart.flush();
+        // Prune inactive genomes from dbDbTree.
+        var activeTaxIds = _.invert(activeGenomes);
+        if (dbDbTree && ! pruneInactive(dbDbTree, activeGenomes, activeTaxIds)) {
+            // no more dbDbTree descendants left after pruning
+            dbDbTree = null;
+        }
 
-        // When page has loaded, draw the species tree, do layout adjustments and
-        // initialize event handlers.
+        // When page has loaded, do layout adjustments and initialize event handlers.
         $(function() {
-            var searchObj = autocompleteFromTree(dbDbTree);
-            var processSpeciesResults = processSpeciesAutocompleteItems.bind(null, searchObj);
             scrollbarWidth = findScrollbarWidth();
-            drawSpeciesPicker();
             setRightColumnWidth();
             setupFavIcons();
             autocompleteCat.init($('#speciesSearch'),
                                  { baseUrl: 'hgGateway?hggw_term=',
                                    watermark: speciesWatermark,
                                    onSelect: setDbFromAutocomplete,
                                    onServerReply: processSpeciesResults,
                                    enterSelectsIdentical: true });
-            updateFindPositionSection(uiState);
             $('#selectAssembly').change(onChangeDbMenu);
             $('#positionDisplay').click(onClickCopyPosition);
             $('#copyPosition').click(onClickCopyPosition);
             $('.jwGoButtonContainer').click(goToHgTracks);
             $(window).resize(setRightColumnWidth.bind(null, scrollbarWidth));
             $(window).resize(updateGoButtonPosition);
             replaceHgsidInLinks();
+            // Fill in searchObj here once everything is displayed.
+            autocompleteFromTree(dbDbTree, searchObj);
         });
     }
 
     return { init: init,
              // For use by speciesTree.draw SVG (text-only onclick):
              onClickSpeciesLabel: onClickSpeciesLabel,
              onClickHubName: onClickHubName
            };
 
 }()); // hgGateway
 
 hgGateway.init();