ef77b08098f12682c3318243d14dfb52cda203f8
angie
  Mon Apr 25 11:50:43 2016 -0700
Cath's suggestion from #15277#note-190: Use the same width for the select assembly menu as for the position input and center the Go button vertically.  Also fixed IE8 clobbering of activeGenomes global.  refs #15277

diff --git src/hg/js/hgGateway.js src/hg/js/hgGateway.js
index 3e4181e..f3e9801 100644
--- src/hg/js/hgGateway.js
+++ src/hg/js/hgGateway.js
@@ -11,35 +11,34 @@
 // speciesTree: module that exports draw() function for drawing a phylogenetic tree
 // (and list of hubs above the tree, if any) in a pre-existing SVG element -- see
 // 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 || undefined;
-var activeGenomes = activeGenomes || undefined;
-var cart = cart || undefined;
+// Globals (pragma for jshint):
+/* globals dbDbTree, activeGenomes, cart */
+/* globals calculateHgTracksWidth */ // function is defined in utils.js
+
 
 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;
             } else if (setting === 'href') {
@@ -768,30 +767,32 @@
                                   'existing browser to ' +
                                   '<A HREF="https://www.mozilla.org/en-US/firefox/new/">' +
                                   ' FireFox</A> or ' +
                                   '<A HREF="https://www.google.com/chrome/browser/">' +
                                   'Chrome</A>.' +
                                   '</P>';
 
     // Globals
     // Set this to true to see server requests and responses in the console.
     var debugCartJson = false;
     // This is a global (within wrapper function scope) so event handlers can use it
     // without needing to bind functions.
     var scrollbarWidth = 0;
     // This holds everything we need to know to draw the page: taxId, db, hubs, description etc.
     var uiState = {};
+    // This is dbDbTree after pruning -- null if dbDbTree has no children left
+    var prunedDbDbTree;
 
     function setupFavIcons() {
         // Set up onclick handlers for shortcut buttons and labels
         var i, name, taxId, onClick;
         for (i = 0;  i < favIconTaxId.length;  i++) {
             name = favIconTaxId[i][0];
             taxId = favIconTaxId[i][1];
             // 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);
         }
     }
 
@@ -986,41 +987,58 @@
         resizeSliderIcon($sliderIcon, svgHeight, sliderBarHeight);
         $sliderIcon.draggable({ axis: 'y',
                                 containment: '#speciesGraphic',
                                 drag: onDragSlider
                                 });
         $speciesPicker.scroll(onScrollImage);
     }
 
     function findScrollbarWidth() {
         var widthPlain = $("#sbTestContainerDPlain").width();
         var widthInsideScroll = $("#sbTestContainerDInsideScroll").width();
         $('#sbTestContainer').hide();
         return widthPlain - widthInsideScroll;
     }
 
+    function updateGoButtonPosition() {
+        // If there's enough room for the Go button to be to the right of the inputs,
+        // set its height to the midpoint of theirs.
+        var $goButton = $('.jwGoButtonContainer');
+        var goOffset = $goButton.offset();
+        var menuOffset = $('#selectAssembly').offset();
+        var inputOffset = $('#positionInput').offset();
+        var verticalMidpoint = (menuOffset.top + inputOffset.top) / 2;
+        if (goOffset.left > inputOffset.left) {
+            $goButton.offset({top: verticalMidpoint });
+        } else {
+            // If the window shrinks and there's no longer room for the button, undo the above.
+            $goButton.css('top', 0);
+        }
+    }
+
     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;
         var rightColumnWidth = ($('#pageContent').width() -
                                 $('#selectSpeciesSection').width() -
                                 ieFudge - extraFudge);
         if (rightColumnWidth >= 400) {
             $('#findPositionSection').width(rightColumnWidth);
         }
+        updateGoButtonPosition();
     }
 
     function setSpeciesPickerSizes(svgWidth, svgHeight) {
         // Adjust widths and heights of elements in #speciesPicker according to svg size.
         $('#speciesTree').width(svgWidth);
         $('#speciesTree').height(svgHeight);
         $('#speciesTreeContainer').height(svgHeight);
         // Make #speciesTreeContainer skinnier if a scrollbar is taking up space
         // in #speciesPicker.
         var leftover = ($("#speciesPicker").width() - scrollbarWidth);
         $("#speciesTreeContainer").width(leftover);
     }
 
     function highlightLabel(selectedName, scrollToItem) {
         // Highlight the selected species.
@@ -1089,102 +1107,81 @@
                 }
             }
         } 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() {
+    function drawSpeciesPicker(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;
         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);
             }
             $('#representedSpeciesTitle').show();
             $('#speciesGraphic').show();
-            if (dbDbTree) {
+            if (dbDbTree && document.createElementNS) {
                 // 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:
         function overwriteWithPos() {
             $('#positionInput').val(newPos);
         }
         window.setTimeout(overwriteWithPos, 0);
     }
 
-    function updateGoButtonPosition() {
-        // If there's room for the go button to appear to the right of #selectAssembly and
-        // #positionInput, align the top of the go button with the bottom of #selectAssembly.
-        // Otherwise let it hang out below.
-        var $fpic = $('#findPosInputContainer');
-        var fpicOffset = $fpic.offset();
-        var fpicRight = fpicOffset.left + $fpic.width();
-        var $button = $('.jwGoButtonContainer');
-        var buttonOffset = $button.offset();
-        var $select;
-        if (buttonOffset.left > fpicRight) {
-            // Align button top with select bottom.
-            $select = $('#selectAssembly');
-            buttonOffset.top = $select.offset().top + $select.height();
-        } else {
-            // Button wraps around to below inputs; remove any previous vertical offsetting.
-            buttonOffset.top = fpicOffset.top + $fpic.height() + 10;
-        }
-        $button.offset(buttonOffset);
-    }
-
     function setAssemblyOptions(uiState) {
         var assemblySelectLabel = 'Assembly';
         if (uiState.dbOptions) {
             var html = '', option, i, selected;
             for (i = 0;  i < uiState.dbOptions.length;  i++) {
                 option = uiState.dbOptions[i];
                 selected = (option.value === uiState.db) ? 'selected ' : '';
                 html += '<option ' + selected + 'value="' + option.value + '">' +
                         option.label + '</option>';
             }
             $('#selectAssembly').html(html);
         }
         if (uiState.genomeLabel) {
             if (uiState.hubUrl && uiState.genomeLabel.indexOf('Hub') < 0) {
                 assemblySelectLabel = uiState.genomeLabel + ' Hub Assembly';
@@ -1239,51 +1236,67 @@
     }
 
     function updateDescription(description) {
         // We got the contents of a db's description.html -- tweak its format to fit
         // the new design.
         $('#descriptionText').html(description);
         tweakDescriptionPhotoWidth();
         // Apply JWest formatting to all anchors in description.
         // We can't simply style all <a> tags that way because autocomplete uses <a>'s.
         $('#descriptionText a').addClass('jwAnchor');
         // Apply square bullet style to all ul's in description.
         $('#descriptionText ul').addClass('jwNoBullet');
         $('#descriptionText li').addClass('jwSquareBullet');
     }
 
+    function initFindPositionContents() {
+        // Unhide contents of Find Position section and adjust layout.
+        $('#findPositionContents').show();
+        // Set assembly menu's width to same as position input.
+        var posWidth = $('#positionInput').outerWidth();
+        var $select = $('#selectAssembly');
+        $select.outerWidth(posWidth);
+        // For some reason, it doesn't set it to posWidth, it sets it to posWidth-2...
+        // detect and adjust.
+        var weirdDiff = posWidth - $select.outerWidth();
+        if (weirdDiff) {
+            $select.outerWidth(posWidth + weirdDiff);
+        }
+        updateGoButtonPosition();
+    }
+
     function updateFindPositionSection(uiState) {
+        // Update the assembly menu, positionInput and description.
         var suggestUrl = null;
         if (uiState.suggestTrack) {
             suggestUrl = 'hgSuggest?db=' + uiState.db + '&prefix=';
         }
         setAssemblyOptions(uiState);
         if (uiState.position) {
             $('#positionDisplay').text(uiState.position);
         }
         autocompleteCat.init($('#positionInput'),
                              { baseUrl: suggestUrl,
                                watermark: positionWatermark,
                                onSelect: onSelectGene,
                                enterSelectsIdentical: true,
                                onEnterTerm: goToHgTracks });
         setAssemblyDescriptionTitle(uiState.db, uiState.genome);
         updateDescription(uiState.description);
-        if (uiState.db) {
-            $('#findPositionContents').show();
+        if (uiState.db && $('#findPositionContents').css('display') === 'none') {
+            initFindPositionContents();
         }
-        updateGoButtonPosition();
     }
 
     function removeDups(inList, isDup) {
         // Return a list with only unique items from inList, using isDup(a, b) -> true if a =~ b
         var inLength = inList.length;
         // inListDups is an array of boolean flags for marking duplicates, parallel to inList.
         var inListDups = [];
         var outList = [];
         var i, j;
         for (i = 0;  i < inLength;  i++) {
             // If something has already been marked as a duplicate, skip it.
             if (! inListDups[i]) {
                 // the first time we see a value, add it to outList.
                 outList.push(inList[i]);
                 for (j = i+1;  j < inLength;  j++) {
@@ -1356,31 +1369,31 @@
     }
 
     function updateStateAndPage(jsonData) {
         // Update uiState with new values and update the page.
         var hubsChanged = !_.isEqual(jsonData.hubs, uiState.hubs);
         // In rare cases, there can be a genome (e.g. Baboon) with multiple species/taxIds
         // (e.g. Papio anubis for papAnu1 vs. Papio hamadryas for papHam1).  Changing the
         // db can result in changing the taxId too.  In that case, update the highlighted
         // species in the tree image.
         if (jsonData.taxId !== uiState.taxId) {
             highlightLabel('textEl_' + jsonData.taxId, false);
         }
         _.assign(uiState, jsonData);
         updateFindPositionSection(uiState);
         if (hubsChanged) {
-            drawSpeciesPicker();
+            drawSpeciesPicker(prunedDbDbTree);
         }
     }
 
     function handleRefreshState(jsonData) {
         if (checkJsonData(jsonData, 'handleRefreshState')) {
             updateStateAndPage(jsonData);
         }
     }
 
     function handleSetDb(jsonData) {
         // Handle the server's response to cartJson command setDb or setHubDb
         if (checkJsonData(jsonData, 'handleSetDb') &&
             trackHubSkipHubName(jsonData.db) === trackHubSkipHubName(uiState.db)) {
             updateStateAndPage(jsonData);
         } else {
@@ -1553,52 +1566,51 @@
 
     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);
+        prunedDbDbTree = dbDbTree;
         if (dbDbTree && ! pruneInactive(dbDbTree, activeGenomes, activeTaxIds)) {
-            // no more dbDbTree descendants left after pruning
-            dbDbTree = null;
+            prunedDbDbTree = null;
         }
 
         // When page has loaded, do layout adjustments and initialize event handlers.
         $(function() {
             scrollbarWidth = findScrollbarWidth();
             setRightColumnWidth();
             setupFavIcons();
             autocompleteCat.init($('#speciesSearch'),
                                  { baseUrl: 'hgGateway?hggw_term=',
                                    watermark: speciesWatermark,
                                    onSelect: setDbFromAutocomplete,
                                    onServerReply: processSpeciesResults,
                                    enterSelectsIdentical: true });
             $('#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);
+            autocompleteFromTree(prunedDbDbTree, searchObj);
         });
     }
 
     return { init: init,
              // For use by speciesTree.draw SVG (text-only onclick):
              onClickSpeciesLabel: onClickSpeciesLabel,
              onClickHubName: onClickHubName
            };
 
 }()); // hgGateway
 
 hgGateway.init();