47ea57080b515e5dad5f658c58feb8944a7e7d61 chmalee Thu Jan 29 15:30:26 2026 -0800 Replace clade/assembly dropdowns with a search bar on most CGIs. Add a recents list to hgGateway and to the species bar and to the 'Genomes' dropdown menu. Track recently selected species in localStorage. Add toGenome and fromGenome arguemnts to hubApi/liftOver in order to find appropriate liftover assemblies, refs #36232 diff --git src/hg/js/hgMyData.js src/hg/js/hgMyData.js index 0a5f15745c7..834372566c2 100644 --- src/hg/js/hgMyData.js +++ src/hg/js/hgMyData.js @@ -22,110 +22,54 @@ } else { splitVal[ix] = encodeURIComponent(ele); } }); return splitVal.join('/'); } function cgiDecode(value) { // decode an encoded value return decodeURIComponent(value); } function setDbSelectFromAutocomplete(selectEle, item) { // this has been bound to the <select> we are going to add // a new child option to + if (item.disabled || !item.genome) return; let newOpt = document.createElement("option"); newOpt.value = item.genome; newOpt.label = item.label; newOpt.selected = true; selectEle.appendChild(newOpt); const event = new Event("change"); selectEle.dispatchEvent(event); } -function processFindGenome(result, term) { - // process the hubApi/findGenome?q= result set into somthing - // jquery-ui autocomplete can use - let data = []; - let apiSkipList = new Set(["downloadTime", "downloadTimeStamp", "availableAssemblies", "browser", "elapsedTimeMs", "itemCount", "q", "totalMatchCount"]); - _.forEach(result, function(val, key) { - if (!(apiSkipList.has(key))) { - let d = { - "genome": key, - "label": `${val.commonName} (${key})`, - }; - if (val.hubUrl !== null) { - d.category = "UCSC GenArk - bulk annotated assemblies from NCBI GenBank / Refseq"; - } else { - d.category = "UCSC Genome Browser assemblies - annotation tracks curated by UCSC"; - } - data.push(d); - } - }); - return data; +function onSearchError(jqXHR, textStatus, errorThrown, term) { + return [{label: 'No genomes found', value: '', genome: '', disabled: true}]; } let autocompletes = {}; function initAutocompleteForInput(inpIdStr, selectEle) { // we must set up the autocompleteCat for each input created, once per file chosen // override the autocompleteCat.js _renderMenu to get the menu on top // of the uppy widget. // Return true if we actually set up the autocomplete, false if we have already // set it up previously if ( !(inpIdStr in autocompletes) || autocompletes[inpIdStr] === false) { - $.widget("custom.autocompleteCat", - $.ui.autocomplete, - { - _renderMenu: function(ul, items) { - var that = this; - var currentCategory = ""; - // There's no this._super as shown in the doc, so I can't override - // _create as shown in the doc -- just do this every time we render... - this.widget().menu("option", "items", "> :not(.ui-autocomplete-category)"); - $(ul).css("z-index", "99999999"); - $.each(items, - function(index, item) { - // Add a heading each time we see a new category: - if (item.category && item.category !== currentCategory) { - ul.append("<li class='ui-autocomplete-category'>" + - item.category + "</li>" ); - currentCategory = item.category; - } - that._renderItem( ul, item ); - }); - }, - _renderItem: function(ul, item) { - // In order to use HTML markup in the autocomplete, one has to overwrite - // autocomplete's _renderItem method using .html instead of .text. - // http://forum.jquery.com/topic/using-html-in-autocomplete - // Hits to assembly hub top level (not individial db names) have no item label, - // so use the value instead - return $("<li></li>") - .data("ui-autocomplete-item", item) - .append($("<a></a>").html((item.label !== null ? item.label : item.value))) - .appendTo(ul); - } - }); let selectFunction = setDbSelectFromAutocomplete.bind(null, selectEle); - autocompleteCat.init($("[id='"+inpIdStr+"']"), { - baseUrl: "hubApi/findGenome?browser=mustExist&q=", - //watermark: "Enter species name, common name, etc", - onSelect: selectFunction, - onServerReply: processFindGenome, - enterSelectsIdentical: false - }); + initSpeciesAutoCompleteDropdown(inpIdStr, selectFunction, null, null, null, onSearchError); autocompletes[inpIdStr] = true; return true; } return false; } function generateApiKey() { let apiKeyInstr = document.getElementById("apiKeyInstructions"); let apiKeyDiv = document.getElementById("apiKey"); if (!document.getElementById("spinner")) { let spinner = document.createElement("i"); spinner.id = "spinner"; spinner.classList.add("fa", "fa-spinner", "fa-spin"); document.getElementById("generateApiKey").after(spinner);