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>." " 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). " "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(" "); 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;