c7221bad6eda31fe88c6f9880276d8f993ff876e
chmalee
  Thu Mar 5 12:37:14 2026 -0800
Disable recent/popular species list selection in the hgConvert toDb dropdown if there are no chains available to them. Leave them in the list but unselectable, refs #36232

diff --git src/hg/js/utils.js src/hg/js/utils.js
index 13d1a418f1a..1cd28c0b827 100644
--- src/hg/js/utils.js
+++ src/hg/js/utils.js
@@ -4813,31 +4813,31 @@
             // Set db to the key (database name or accession) so the autocomplete
             // select handler can save it to recent genomes
             d.db = 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 initSpeciesAutoCompleteDropdown(inputId, selectFunction, baseUrl = null,
-        watermark = null, onServerReply = null, onError = null) {
+        watermark = null, onServerReply = null, onError = null, onFilterDropdown = null) {
 /* Generic function for turning an <input> element into a species search bar with an autocomplete
  * list separating results by category.
  * Required arguments:
  *     inputId: id of the input element itself, not created here
  *     selectFunction: the function to call when the user actually clicks on a result
  * Optional arguments:
  *     baseUrl: where we send requests to which will return json we can parse into a list
  *         of results, defaults to 'hubApi/findGenome?browser=mustExist&q='
  *     watermark: placeholder text in the input
  *     onServerReply: function to run after querying baseUrl, by default use processFindGenome()
  *         to standardize on hubApi, but can be something else
  *     onError: function to call when the server returns an error (e.g. HTTP 400)
  *         signature: onError(jqXHR, textStatus, errorThrown, searchTerm) => results array or null
  */
     let defaultSearchUrl = "hubApi/findGenome?browser=mustExist&q=";
@@ -4889,43 +4889,50 @@
                         return word.length > 0;  // Filter out empty strings
                     });
 
                     // Apply bolding for each word separately
                     words.forEach(function(word) {
                        // Escape special regex characters in each word
                        var escapedWord = word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
 
                        // Create case-insensitive regex to find all occurrences
                        var regex = new RegExp('(' + escapedWord + ')', 'gi');
 
                        // Replace matches with bolded version (preserves original case)
                        label = label.replace(regex, '<b>$1</b>');
                    });
                 }
-                return $("<li></li>")
+                let $li = $("<li></li>")
                     .data("ui-autocomplete-item", item)
                     .append($("<a></a>").html(label))
                     .appendTo(ul);
+                if (item.disabled) {
+                    $li.attr('title', item.disabledReason || 'Not available');
+                    $li.css({'opacity': '0.5'});
+                    $li.find('a').css({'pointer-events': 'none'});
+                }
+                return $li;
             }
         }
     );
     autocompleteCat.init($("[id='"+inputId+"']"), {
         baseUrl: baseUrl !== null ? baseUrl : defaultSearchUrl,
         watermark: watermark,
         onSelect: selectFunction,
         onServerReply: onServerReply !== null ? onServerReply : processFindGenome,
         onError: onError,
+        onFilterDropdown: onFilterDropdown,
         showRecentGenomes: true,
         enterSelectsIdentical: false
     });
 }
 
 function setupGenomeSearchBar(config) {
 /* Higher-level wrapper for setting up a genome search bar with standard boilerplate.
  * This handles the common pattern used across CGIs: error handling, DOMContentLoaded,
  * element binding, search button handler, item validation, and label update.
  *
  * config object properties:
  *   inputId (required): ID of the search input element
  *   labelElementId: ID of the element to update with selected genome label (default: 'genomeLabel')
  *   onSelect: Callback function(item, labelElement) when genome is selected.
  *             Called AFTER standard validation and label update.