915f56e3f34f66d58bf6573dc4dfddd068bd4090
angie
  Tue Apr 19 13:36:48 2016 -0700
Simplify the generation of hgGateway's dbDbTaxonomy tree info and support mirrors better: new hg.conf setting hgGateway.dbDbTaxonomy enables tree display and specifies relative URL to file.  Instead of dbDbTaxonomy.{hgwdev,rr}.js, there is only one dbDbTaxonomy.js, built from hgcentraltest (hgwdev).  hgGateway.c's HTML output includes JS encoding the set of active genomes+taxIds.  hgGateway.js uses that to prune the tree so it contains only active genomes; now we can use the hgwdev tree on the RR and see only RR species.  Also updated dbDbTaxonomy.js to get the latest hgwdev species as of 4/18/16.
refs #15277

diff --git src/hg/js/hgGateway.js src/hg/js/hgGateway.js
index 158f237..24a851f 100644
--- src/hg/js/hgGateway.js
+++ src/hg/js/hgGateway.js
@@ -13,31 +13,32 @@
 // hg/hgGateway/hgGateway.html.
 
 // rainbow: module that exports draw() function and colors.  draw() adds stripes using
 // a spectrum of colors that are associated to species groups.  The hgGateway view code
 // uses coordinates of stripes within the tree image to create a corresponding "rainbow"
 // slider bar to the left of the phylogenetic tree container.
 
 // autocompleteCat: customized JQuery autocomplete plugin that includes watermark and
 // can display results broken down by category (for example, genomes from various
 // assembly hubs and native genomes).
 
 // hgGateway: module of mostly view/controller code (model state comes directly from server).
 
 // Globals:
 /* globals calculateHgTracksWidth */ // pragma for jshint; function is defined in utils.js
-var dbDbTree = dbDbTree || ['dbDbTree is missing!', []];
+var dbDbTree = dbDbTree || undefined;
+var activeGenomes = activeGenomes || undefined;
 var cart = cart || undefined;
 
 function svgCreateEl(type, config) {
     // Helper function for creating a new SVG element and initializing its
     // properties and attributes.  Type is something like 'rect', 'text', 'g', etc;
     // config is an object like { id: 'newThingie', x: 0, y: 10, title: 'blah blah' }.
     var svgns = 'http://www.w3.org/2000/svg';
     var xlinkns = 'http://www.w3.org/1999/xlink';
     var el = document.createElementNS(svgns, type);
     var title, titleEl;
     if (el) {
         _.forEach(config, function(value, setting) {
             if (setting === 'textContent') {
                 // Text content (the text in a text element or title element) is a property:
                 el.textContent = value;
@@ -793,30 +794,33 @@
             $('.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) {
         // 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(
@@ -965,31 +969,30 @@
             normalizedTop = imageTop / svgHeight;
             moveSlider(normalizedTop);
         };
 
         // This might be called before the species image has been created; if so, do nothing.
         if (! $speciesTree || speciesTree.length === 0) {
             return;
         }
 
         makeRainbowSliderStripes($sliderBar, onClickStripe, svgHeight, stripeColors, stripeTops);
         resizeSliderIcon($sliderIcon, svgHeight, sliderBarHeight);
         $sliderIcon.draggable({ axis: 'y',
                                 containment: '#speciesGraphic',
                                 drag: onDragSlider
                                 });
-        $sliderIcon.show();
         $speciesPicker.scroll(onScrollImage);
     }
 
     function findScrollbarWidth() {
         var widthPlain = $("#sbTestContainerDPlain").width();
         var widthInsideScroll = $("#sbTestContainerDInsideScroll").width();
         $('#sbTestContainer').hide();
         return widthPlain - widthInsideScroll;
     }
 
     function setRightColumnWidth() {
         // Adjust the width of the "Find Position" section so it fits to the right of the
         // "Browse/Select Species" section.
         var ieFudge = scrollbarWidth ? scrollbarWidth + 4 : 0;
         var extraFudge = 4;
@@ -1041,48 +1044,100 @@
             return matches[1];
         } else {
             return null;
         }
     }
 
     function highlightLabelForDb(db, taxId) {
         var hubName = hubNameFromDb(db);
         if (hubName) {
             highlightLabel('textEl_' + hubName, true);
         } else {
             highlightLabel('textEl_' + taxId, true);
         }
     }
 
+    function pruneInactive(node, activeGenomes, activeTaxIds) {
+        // Return true if some leaf descendant of node is in activeGenomes or activeTaxIds.
+        // Remove any child that returns false.
+        // If one of {genome, taxId} matches but not the other, tweak the other to match dbDb,
+        // Since we'll be using the hgwdev dbDbTree on the RR which may have been tweaked.
+        var genome = node[0], taxId = node[1], kids = node[3];
+        var hasActiveLeaf = false, i, dbDbTaxId, dbDbGenome;
+        if (!kids || kids.length === 0) {
+            // leaf node: is it active?
+            dbDbTaxId = activeGenomes[genome];
+            if (dbDbTaxId) {
+                hasActiveLeaf = true;
+                node[1] = dbDbTaxId;
+            }
+            // Yet another special case for Baboon having one genome with two species...
+            // maybe we should just change dbDb?
+            else if (_.startsWith(genome, 'Baboon ') && (taxId === 9555 || taxId === 9562)) {
+                hasActiveLeaf = true;
+            } else {
+                dbDbGenome = activeTaxIds[taxId];
+                if (dbDbGenome) {
+                    hasActiveLeaf = true;
+                    node[0] = dbDbGenome;
+                }
+            }
+        } 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 (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);
-            initRainbowSlider(spTree.height, rainbow.colors, stripeTops);
+                stripeTops = rainbow.draw(svg, dbDbTree,
+                                          spTree.yTree, spTree.height, spTree.leafTops);
             } else {
                 $('#speciesTreeContainer').html(getBetterBrowserMessage);
             }
+            $('#representedSpeciesTitle').show();
+            $('#speciesGraphic').show();
+            if (dbDbTree) {
+                // This needs to be done after things are visible so the slider gets the
+                // right position.
+                initRainbowSlider(spTree.height, rainbow.colors, stripeTops);
+            }
+        }
     }
 
     function onSelectGene(item) {
         // Set the position from an autocomplete result;
         // set hgFindMatches and make sure suggestTrack is in pack mode for highlighting the match.
         var newPos = item.id;
         var settings;
         $('#positionDisplay').text(newPos);
         if (uiState.suggestTrack) {
             settings = { 'hgFind.matches': item.internalId };
             settings[uiState.suggestTrack] = 'pack';
             cart.send({ cgiVar: settings });
             cart.flush();
         }
         // Overwrite the selected item w/actual position after the autocomplete plugin is done: