cee097b75fe4d1b29f0debd8af938ba6bf729d53 max Tue Aug 11 08:18:42 2026 -0700 hgBlat: new client-rendered BLAT search form and sortable results page, refs #37893 An opt-in modern alternative to the classic hyperlink output, gated by the blatNewForm / blatNewPage hg.conf flags. hgBlat.c emits the hit data and form config as inline JSON (hgBlatData / hgBlatFormData) into empty containers, and hgBlat.js builds the UI: the search form, a sortable/filterable DataTable with identity and query-coverage bars, a docked per-hit detail panel, a FASTA query viewer, and share-link and rename-track modals. - hgBlat.css: all styling for both pages, loaded via webIncludeResourceFile (not injected from JS); layout uses CSS classes rather than inline styles. - utils.js: setupGenomeSelector, the combobox genome picker the form uses. - hgc.c: blatOldTracks / blatKeepResults - the "Keep results" checkbox lets a user accumulate BLAT result tracks instead of each search replacing the last. - hgConfCatalog: register the blatNewForm / blatNewFormBanner gates. - blatExample.fa: the example query the form loads on demand. Markup is built in JS (escaped values via htmlEncode, everything else set through DOM properties); no HTML strings in the C or a template engine. diff --git src/hg/hgBlat/hgBlat.c src/hg/hgBlat/hgBlat.c index 3f71514befe..31a0d6655ce 100644 --- src/hg/hgBlat/hgBlat.c +++ src/hg/hgBlat/hgBlat.c @@ -607,30 +607,31 @@ return locusConn; } static void printBlatResultsApp(struct psl *pslList, char *database, char *organism, char *browserUrl, char *hgcUrl, char *pslName, char *faName, char *customText, char *uiState, char *unhideTrack, struct sqlConnection *locusConn, struct subText *subList) /* "Table" output mode: emit the hit data as an inline JSON object plus an empty container, and let * hgBlat.js build the UI (summary strip, DataTable with identity/coverage bars, detail panel). * All presentation lives in hgBlat.js; this function only assembles data. * On a fresh search the per-hit "Alignment details" links go to hgc's htcUserAli (which reads the * ephemeral trash .pslx/.fa); on a shared-link reopen (pslName NULL) there is no trash, so they go * to htcBlatAlign instead, which rebuilds each alignment from the durable bigPsl custom track. */ { struct psl *psl; jsIncludeDataTablesLibs(); +webIncludeResourceFile("hgBlat.css"); jsIncludeFile("hgBlat.js", NULL); struct jsonWrite *jw = jsonWriteNew(); jsonWriteObjectStart(jw, NULL); jsonWriteObjectStart(jw, "config"); /* For assembly/GenArk hubs the internal names carry a "hub_NNN_" prefix; drop it so the Assembly * field reads cleanly (and doesn't show the prefix twice), matching the BLAT Results page title. */ jsonWriteString(jw, "db", trackHubSkipHubName(database)); jsonWriteString(jw, "organism", trackHubSkipHubName(organism)); jsonWriteString(jw, "queryName", pslList->qName); jsonWriteNumber(jw, "querySize", pslList->qSize); jsonWriteNumber(jw, "hitCount", slCount(pslList)); jsonWriteBoolean(jw, "multiQuery", pslListMultiQuery(pslList)); jsonWriteBoolean(jw, "hasLocus", locusConn != NULL); @@ -784,50 +785,78 @@ } sqlFreeResult(&sr); } jsonWriteObjectEnd(jw); freeMem(inTabUrl); freeMem(newTabUrl); } jsonWriteListEnd(jw); // hits jsonWriteObjectEnd(jw); // root printf("
\n"); jsInlineF("var hgBlatData = %s;\n", jw->dy->string); jsonWriteFree(&jw); } +static void printBlatBannerStyle() +/* Emit the .blatBanner rule for the classic, C-rendered pages. Those don't load hgBlat.js, which + * is where the new pages get this class from, so define the same thing here; keep the values in + * sync with the .blatBanner rule in hgBlat.js. Only the first call emits anything, so a page may + * carry more than one banner without repeating the stylesheet. */ +{ +static boolean styleDone = FALSE; +if (styleDone) + return; +styleDone = TRUE; +printf(""); +} + +static void printNewFormBanner() +/* On the classic search form, offer the JavaScript-built one. Without this the new form's "go back + * to the original page" link is a one-way door. + * Shown wherever the new form is enabled (blatNewForm), which is what makes the round trip work; + * blatNewFormBanner overrides that in either direction. On a machine where the new form is off + * there is nothing to advertise, so nothing is printed. */ +{ +if (!cfgOptionBooleanDefault("blatNewFormBanner", + cfgOptionBooleanDefault("blatNewForm", FALSE))) + return; +printBlatBannerStyle(); +printf("