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 = $('
'); $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 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();