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/hgLiftOver/hgLiftOver.c src/hg/hgLiftOver/hgLiftOver.c
index 4cf26e6d4d4..9d909e4bfc1 100644
--- src/hg/hgLiftOver/hgLiftOver.c
+++ src/hg/hgLiftOver/hgLiftOver.c
@@ -9,30 +9,31 @@
 #include "portable.h"
 #include "linefile.h"
 #include "dnautil.h"
 #include "fa.h"
 #include "cheapcgi.h"
 #include "htmshell.h"
 #include "hdb.h"
 #include "hui.h"
 #include "cart.h"
 #include "web.h"
 #include "hash.h"
 #include "liftOver.h"
 #include "liftOverChain.h"
 #include "errCatch.h"
 #include "hgConfig.h"
+#include "jsHelper.h"
 
 
 /* CGI Variables */
 #define HGLFT_USERDATA_VAR "hglft_userData"     /* typed/pasted in data */
 #define HGLFT_DATAFILE_VAR "hglft_dataFile"     /* file of data to convert */
 #define HGLFT_FROMORG_VAR "hglft_fromOrg"         /* FROM organism */
 #define HGLFT_FROMDB_VAR "hglft_fromDb"         /* FROM assembly */
 #define HGLFT_TOORG_VAR   "hglft_toOrg"           /* TO organism */
 #define HGLFT_TODB_VAR   "hglft_toDb"           /* TO assembly */
 #define HGLFT_ERRORHELP_VAR "hglft_errorHelp"      /* Print explanatory text */
 #define HGLFT_REFRESHONLY_VAR "hglft_doRefreshOnly"      /* Just refresh drop-down lists */
 #define HGLFT_LAST_CHAIN "hglft_lastChain"
 #define HGLFT_EXTRA_NAME_INFO "hglft_extranameinfo" /* Include input position in output item names */
 
 /* liftOver options: */
@@ -59,90 +60,93 @@
 ".value = 1;"
 "document.mainForm.submit();";
 
 char *chainStringVal(struct liftOverChain *chain)
 /* keep the last chain in memory in this format */
 {
 char chainS[64];
 safef(chainS, sizeof(chainS), "%s.%s", chain->fromDb, chain->toDb);
 return cloneString(chainS);
 }
 
 void webMain(struct liftOverChain *chain, boolean multiple, boolean keepSettings, int minSizeQ, 
 	     int minChainT, float minBlocks, float minMatch, boolean fudgeThick, boolean extraNameInfo)
 /* set up page for entering data */
 {
-struct dbDb *dbList;
 char *fromOrg = hOrganism(chain->fromDb), *toOrg = hOrganism(chain->toDb);
 char *chainString = chainStringVal(chain);
 cgiParagraph(
     "This tool converts genome coordinates and annotation files "
     "from the original to the new assembly using an alignment.  "
     "The input regions can be entered into the text box or uploaded as a file.  "
     "For files over 500Mb, use the command-line tool described in our "
     "<a href=\"../goldenPath/help/hgTracksHelp.html#Liftover\">LiftOver documentation</a>."
     "&nbsp;&nbsp;If a pair of assemblies cannot be selected from the pull-down menus,"
     " a sequential lift may still be possible (e.g., mm9 to mm10 to mm39).&nbsp;&nbsp;"
     "If your desired conversion is still not available, please "
     "<a href=\"../../contacts.html\">contact us</a>."
     "");
 
 /* create HMTL form */
 puts("<FORM ACTION=\"../cgi-bin/hgLiftOver\" METHOD=\"POST\" "
        " ENCTYPE=\"multipart/form-data\" NAME=\"mainForm\">\n");
 cartSaveSession(cart);
 
 /* create HTML table for layout purposes */
 puts("\n<TABLE WIDTH=\"100%\">\n");
-
-/* top two rows -- genome and assembly menus */
-cgiSimpleTableRowStart();
-cgiTableField("Original genome: ");
-cgiTableField("Original assembly: ");
-cgiTableField("New genome: ");
-cgiTableField("New assembly: ");
-cgiTableRowEnd();
-
-cgiSimpleTableRowStart();
-
-/* genome */
-cgiSimpleTableFieldStart();
-dbList = hGetLiftOverFromDatabases();
-printSomeGenomeListHtmlNamed(HGLFT_FROMORG_VAR, chain->fromDb, dbList, "change", onChange);
-cgiTableFieldEnd();
-
-/* from assembly */
-cgiSimpleTableFieldStart();
-printAllAssemblyListHtmlParm(chain->fromDb, dbList, HGLFT_FROMDB_VAR, 
-			     TRUE, "change", onChange);
-cgiTableFieldEnd();
-
-/* to assembly */
-
-cgiSimpleTableFieldStart();
-// Genark is generating some less than fully populated dbDb structures
-// so we don't expect them to free without causing problems.
-//dbDbFreeList(&dbList);   
-dbList = hGetLiftOverToDatabases(chain->fromDb);
+printf("<TR>\n");
+jsIncludeAutoCompleteLibs();
+char *searchBarId = "fromGenomeSearch";
+printf("<input name='%s' value='%s' type='hidden'></input>\n", HGLFT_FROMDB_VAR, chain->fromDb);
+printf("<input name='%s' value='%s' type='hidden'></input>\n", HGLFT_FROMORG_VAR, fromOrg);
+printf("<input name='formMethod' value='GET' type='hidden'></input>\n");
+printf("<TD class='searchCell'>\n");
+printGenomeSearchBar(searchBarId, "Search any species, genome or assembly name", NULL, TRUE, "Change original genome:", NULL);
+jsInlineF(
+    "setupGenomeSearchBar({\n"
+    "    inputId: '%s',\n"
+    "    labelElementId: 'fromGenomeLabel',\n"
+    "    apiUrl: 'hubApi/findGenome?browser=mustExist&liftable=true&q=',\n"
+    "    onSelect: function(item) {\n"
+    "        document.mainForm."HGLFT_REFRESHONLY_VAR".value=1;\n"
+    "        document.mainForm."HGLFT_FROMDB_VAR".value=item.genome;\n"
+    "        document.mainForm."HGLFT_FROMORG_VAR".value=item.commonName.split('(')[0].trim();\n"
+    "        document.mainForm.submit();\n"
+    "    }\n"
+    "});\n"
+    , searchBarId
+);
+printf("</TD>\n");
+printf("<TD class='searchCell' ALIGN=CENTER>\n");
+printf("<div class='flexContainer'>\n");
+printf("<span>Currently selected genome:</span>\n");
+printf("<span id='fromGenomeLabel'>%s (%s)</span>\n", fromOrg, chain->fromDb);
+printf("</TD>\n");
+
+// print select/options for toDb, it is more intuitive than a search bar
+struct dbDb *dbList = hGetLiftOverToDatabases(chain->fromDb);
+printf("<TD class='searchCell'>\n");
+printf("<label>Change new genome:</label>\n");
+printf("<div class='flexContainer'>\n");
 printLiftOverGenomeList(HGLFT_TOORG_VAR, chain->toDb, dbList, "change", onChange);
-cgiTableFieldEnd();
-
-cgiSimpleTableFieldStart();
+printf("</div></td>\n");
+printf("<TD class='searchCell'>\n");
+printf("<div class='flexContainer'>\n");
+printf("<label>Change new assembly:</label>\n");
 printAllAssemblyListHtmlParm(chain->toDb, dbList, HGLFT_TODB_VAR, TRUE, NULL, NULL);
-cgiTableFieldEnd();
+printf("</div></td>\n");
 
-cgiTableRowEnd();
 cgiTableEnd();
 
 printf("<br>");
 cgiSimpleTableStart();
 
 cgiSimpleTableRowStart();
 cgiTableField("Minimum ratio of bases that must remap:");
 cgiSimpleTableFieldStart();
 cgiMakeDoubleVar(HGLFT_MINMATCH, (keepSettings) ? minMatch : chain->minMatch,6);
 puts("&nbsp;");
 printInfoIcon("The minimum ratio of basepairs of the input region covered by an alignment. Regions scoring lower than this will not be lifted at all.");
 cgiTableFieldEnd();
 cgiTableRowEnd();
 
 cgiSimpleTableRowStart();
@@ -426,31 +430,31 @@
 fromDb = cartCgiUsualString(cart, HGLFT_FROMDB_VAR, "0");
 toOrg = cartCgiUsualString(cart, HGLFT_TOORG_VAR, "0");
 toDb = cartCgiUsualString(cart, HGLFT_TODB_VAR, "0");
 cartOrg = hOrganism(cartDb);
 
 if (sameWord(fromOrg,"0"))
     fromOrg = NULL;
 if (sameWord(fromDb,"0"))
     fromDb = NULL;
 if (sameWord(toOrg,"0"))
     toOrg = NULL;
 if (sameWord(toDb,"0"))
     toDb = NULL;
 if (sameWord(cartDb,"0"))
     cartDb = NULL;
-if ((fromDb != NULL) && !sameOk(fromOrg, hOrganism(fromDb)))
+if ((fromDb != NULL) && !sameOk(strLower(fromOrg), strLower(hOrganism(fromDb))))
     fromDb = NULL;
 if ((toDb != NULL) && !sameOk(toOrg, hOrganism(toDb)))
     toDb = NULL;
 
 for (this = chainList; this != NULL; this = this->next)
     {
     if (sameOk(this->fromDb ,fromDb) && sameOk(this->toDb, toDb))
         {
         choice = this;
         break;
         }
     double score = scoreLiftOverChain(this, fromOrg, fromDb, toOrg, toDb, cartOrg, cartDb, dbRank, dbDbHash);
     if (score > bestScore)
 	{
 	choice = this;