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: