c683ecb63d721deb02fa8ab15bf66f70f1c3a326 max Sat Jul 25 18:25:00 2026 -0700 hgBlat/hgc: single-page BLAT results view with shareable alignment links Add a modern single-page BLAT results table (hgBlat.js) and a non-frameset alignment view (showSomeAlignmentModern in hgc, gated by the blatNewPage cart var). Share/reopen a result set from a durable bigPsl custom track pinned in the cart via a saved session (htcBlatAlign / loadBlatShareSessionIfAny). Factor the shared helpers into a new blatShare module (lib/blatShare.c, inc/blatShare.h). diff --git src/hg/hgBlat/hgBlat.c src/hg/hgBlat/hgBlat.c index 8a603a00fb0..0463a8d0eda 100644 --- src/hg/hgBlat/hgBlat.c +++ src/hg/hgBlat/hgBlat.c @@ -29,30 +29,35 @@ #include "hash.h" #include "botDelay.h" #include "trashDir.h" #include "trackHub.h" #include "hgConfig.h" #include "errCatch.h" #include "portable.h" #include "portable.h" #include "dystring.h" #include "chromInfo.h" #include "net.h" #include "fuzzyFind.h" #include "chromAlias.h" #include "subText.h" #include "jsHelper.h" +#include "obscure.h" +#include "jsonWrite.h" +#include "bigBed.h" +#include "bigPsl.h" +#include "blatShare.h" struct cart *cart; /* The user's ui state. */ struct hash *oldVars = NULL; boolean orgChange = FALSE; boolean dbChange = FALSE; boolean allGenomes = FALSE; boolean allResults = FALSE; boolean autoRearr = FALSE; static long enteredMainTime = 0; boolean autoBigPsl = FALSE; // DEFAULT VALUE change to TRUE in future /* for earlyBotCheck() function at the beginning of main() */ #define delayFraction 0.5 /* standard penalty is 1.0 for most CGIs */ @@ -460,48 +465,327 @@ { safef(url, sizeof(url), "%s?position=%s:%d-%d&db=%s&ss=%s+%s&%s%s", browserUrl, psl->tName, psl->tStart + 1, psl->tEnd, database, pslName, faName, uiState, unhideTrack); htmStart(stdout, "Redirecting"); jsInlineF("location.replace('%s');\n", url); printf("\n", url); htmlEnd(); } } /* forward declaration to reduce churn */ static void getCustomName(char *database, struct cart *cart, struct psl *psl, char **pName, char **pDescription); +static void printBlatHitLinks(struct psl *psl, char *database, char *browserUrl, char *hgcUrl, + char *pslName, char *faName, char *customText, char *uiState, char *unhideTrack) +/* Print the "browser", "new tab" and "details" hyperlinks for a single BLAT hit. + * Used by the classic
"Hyperlink" listing. */
+{
+char *browserHelp = "Open a Genome Browser showing this match";
+char *helpText = "Open a Genome Browser with the BLAT results, but in a new internet browser tab";
+// new-tab icon (Font Awesome "arrow-up-right-from-square", CC BY 4.0)
+char *icon = "";
+
+if (customText)
+ {
+ printf("browser ",
+ browserHelp, browserUrl, psl->tName, psl->tStart + 1, psl->tEnd, database,
+ customText, uiState, unhideTrack);
+ printf("new tab%s ",
+ helpText, browserUrl, psl->tName, psl->tStart + 1, psl->tEnd, database,
+ customText, unhideTrack, icon);
+ }
+else
+ {
+ if (autoBigPsl)
+ {
+ // skip ss variable
+ printf("browser ",
+ browserHelp, browserUrl, psl->tName, psl->tStart + 1, psl->tEnd, database,
+ uiState, unhideTrack);
+ printf("new tab%s ",
+ helpText, browserUrl, psl->tName, psl->tStart + 1, psl->tEnd, database,
+ unhideTrack, icon);
+ }
+ else
+ {
+ printf("browser ",
+ browserHelp, browserUrl, psl->tName, psl->tStart + 1, psl->tEnd, database,
+ pslName, faName, uiState, unhideTrack);
+ printf("new tab%s ",
+ helpText, browserUrl, psl->tName, psl->tStart + 1, psl->tEnd, database,
+ pslName, faName, unhideTrack, icon);
+ }
+ }
+printf("",
+ hgcUrl, psl->tStart, pslName, cgiEncode(faName), psl->qName, psl->tName,
+ psl->tStart, psl->tEnd, database, uiState);
+printf("details ");
+}
+
+static char *chromTypeNote(char *tName)
+/* Return a short explanation for _alt/_fix/_random/chrUn sequences, or NULL for a normal chrom. */
+{
+if (endsWith(tName, "_fix"))
+ return "Assembly fix patch: corrects an error in the reference assembly.";
+if (endsWith(tName, "_alt"))
+ return "Alternate haplotype: an alternate sequence for this region.";
+if (endsWith(tName, "_random"))
+ return "Unlocalized sequence: known chromosome, position not determined.";
+if (startsWith(tName, "chrUn"))
+ return "Unplaced sequence: chromosome of origin unknown.";
+return NULL;
+}
+
+static char *blatBrowserUrl(struct psl *psl, char *database, char *browserUrl,
+ char *pslName, char *faName, char *customText, char *uiState, char *unhideTrack, boolean withUiState)
+/* Return a Genome Browser URL for one BLAT hit. withUiState appends the hgsid; it is included on
+ * the in-tab link but omitted from the new-tab link, matching the classic hyperlink behavior. */
+{
+struct dyString *dy = dyStringNew(256);
+dyStringPrintf(dy, "%s?position=%s:%d-%d&db=%s", browserUrl, psl->tName, psl->tStart + 1, psl->tEnd, database);
+if (customText)
+ dyStringPrintf(dy, "&hgt.customText=%s", customText);
+else if (!autoBigPsl && pslName != NULL)
+ dyStringPrintf(dy, "&ss=%s+%s", pslName, faName);
+if (withUiState)
+ dyStringPrintf(dy, "&%s", uiState);
+dyStringPrintf(dy, "%s", unhideTrack);
+return dyStringCannibalize(&dy);
+}
+
+static boolean pslListMultiQuery(struct psl *pslList)
+/* Return TRUE if the list contains more than one distinct query (qName). */
+{
+struct psl *psl;
+for (psl = pslList->next; psl != NULL; psl = psl->next)
+ if (!sameString(psl->qName, pslList->qName))
+ return TRUE;
+return FALSE;
+}
+
+static struct sqlConnection *blatLocusConn(char *database, struct subText **retSubList)
+/* If the database has a "locusName" table, return a fresh connection for its range queries and
+ * build the abbreviation-expansion subList; otherwise return NULL with an empty subList. */
+{
+struct subText *subList = NULL;
+struct sqlConnection *locusConn = NULL;
+if (sqlDatabaseExists(database))
+ {
+ struct sqlConnection *conn = hAllocConn(database);
+ if (sqlTableExists(conn, "locusName"))
+ {
+ locusConn = hAllocConn(database);
+ slSafeAddHead(&subList, subTextNew("ig:", "intergenic "));
+ slSafeAddHead(&subList, subTextNew("ex:", "exon "));
+ slSafeAddHead(&subList, subTextNew("in:", "intron "));
+ slSafeAddHead(&subList, subTextNew("|", "-"));
+ }
+ hFreeConn(&conn);
+ }
+*retSubList = subList;
+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();
+jsIncludeFile("hgBlat.js", NULL);
+
+struct jsonWrite *jw = jsonWriteNew();
+jsonWriteObjectStart(jw, NULL);
+
+jsonWriteObjectStart(jw, "config");
+jsonWriteString(jw, "db", database);
+jsonWriteString(jw, "organism", 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);
+/* Sharing a link only makes sense when a durable bigPsl custom track was made from the results
+ * (autoBigPsl); otherwise there is nothing for the shared session to reopen from. */
+jsonWriteBoolean(jw, "canShare", autoBigPsl);
+/* The classic "Old BLAT result page" view re-reads the trash .pslx from the current search, so it
+ * is only offered on a fresh search (pslName set), not on a shared-link reopen rebuilt from the
+ * durable custom track (where the trash files may be long gone). */
+jsonWriteBoolean(jw, "canOldPage", pslName != NULL);
+jsonWriteString(jw, "hgsid", cartSessionId(cart));
+jsonWriteStringf(jw, "newSearchUrl", "hgBlat?db=%s&%s", database, uiState);
+char *posStr = cartOptionalString(cart, "position");
+if (posStr != NULL)
+ {
+ jsonWriteString(jw, "backUrl", browserUrl);
+ jsonWriteString(jw, "backPos", posStr);
+ }
+struct dyString *va = dyStringNew(128);
+dyStringPrintf(va, "%s?db=%s", browserUrl, database);
+if (customText)
+ dyStringPrintf(va, "&hgt.customText=%s", customText);
+else if (!autoBigPsl && pslName != NULL)
+ dyStringPrintf(va, "&ss=%s+%s", pslName, faName);
+dyStringPrintf(va, "&%s%s", uiState, unhideTrack);
+jsonWriteString(jw, "viewAllUrl", va->string);
+dyStringFree(&va);
+jsonWriteStringf(jw, "geneUrlBase", "%s?db=%s&%s&position=", browserUrl, database, uiState);
+jsonWriteObjectEnd(jw); // config
+
+jsonWriteListStart(jw, "hits");
+int rank = 0;
+for (psl = pslList; psl != NULL; psl = psl->next)
+ {
+ ++rank;
+ double ident = 100.0 - pslCalcMilliBad(psl, TRUE) * 0.1;
+ char *displayChromName = chromAliasGetDisplayChrom(database, cart, psl->tName);
+ char *inTabUrl = blatBrowserUrl(psl, database, browserUrl, pslName, faName, customText,
+ uiState, unhideTrack, TRUE);
+ char *newTabUrl = blatBrowserUrl(psl, database, browserUrl, pslName, faName, customText,
+ uiState, unhideTrack, FALSE);
+
+ jsonWriteObjectStart(jw, NULL);
+ jsonWriteNumber(jw, "rank", rank);
+ jsonWriteString(jw, "qName", psl->qName);
+ jsonWriteNumber(jw, "score", pslScore(psl));
+ jsonWriteDouble(jw, "identity", ident);
+ jsonWriteString(jw, "chrom", displayChromName);
+ char *note = chromTypeNote(psl->tName);
+ if (note != NULL)
+ jsonWriteString(jw, "chromNote", note);
+ jsonWriteString(jw, "strand", psl->strand);
+ jsonWriteNumber(jw, "tStart", psl->tStart + 1);
+ jsonWriteNumber(jw, "tEnd", psl->tEnd);
+ jsonWriteNumber(jw, "span", psl->tEnd - psl->tStart);
+ jsonWriteNumber(jw, "qStart", psl->qStart + 1);
+ jsonWriteNumber(jw, "qEnd", psl->qEnd);
+ jsonWriteNumber(jw, "qSize", psl->qSize);
+ jsonWriteNumber(jw, "matches", psl->match + psl->repMatch);
+ jsonWriteNumber(jw, "misMatch", psl->misMatch);
+ jsonWriteNumber(jw, "gaps", psl->qNumInsert + psl->tNumInsert);
+ jsonWriteNumber(jw, "blocks", psl->blockCount);
+ jsonWriteString(jw, "browserUrl", inTabUrl);
+ jsonWriteString(jw, "newTabUrl", newTabUrl);
+ if (pslName != NULL)
+ jsonWriteStringf(jw, "detailsUrl", "%s?o=%d&g=htcUserAli&i=%s+%s+%s&c=%s&l=%d&r=%d&db=%s&%s",
+ hgcUrl, psl->tStart, pslName, cgiEncode(faName), psl->qName, psl->tName,
+ psl->tStart, psl->tEnd, database, uiState);
+ else
+ /* Shared-link reopen: there is no trash .pslx, but the durable bigPsl custom track (now in
+ * this cart) lets hgc's htcBlatAlign rebuild the base alignment from the stored query seq. */
+ jsonWriteStringf(jw, "detailsUrl", "%s?g=htcBlatAlign&db=%s&c=%s&o=%d&l=%d&r=%d&i=%s&%s",
+ hgcUrl, database, psl->tName, psl->tStart, psl->tStart, psl->tEnd,
+ cgiEncode(psl->qName), uiState);
+ if (locusConn)
+ {
+ struct sqlResult *sr = hRangeQuery(locusConn, "locusName", psl->tName, psl->tStart, psl->tEnd, NULL, 0);
+ char **row = sqlNextRow(sr);
+ if (row != NULL)
+ {
+ char *raw = row[4];
+ char *full = subTextString(subList, raw);
+ jsonWriteString(jw, "locusText", full);
+ freeMem(full);
+ char *type = NULL, *genes = raw;
+ if (startsWith("ig:", raw))
+ { type = "intergenic"; genes = raw + 3; }
+ else if (startsWith("ex:", raw))
+ { type = "exon"; genes = raw + 3; }
+ else if (startsWith("in:", raw))
+ { type = "intron"; genes = raw + 3; }
+ if (type != NULL)
+ {
+ jsonWriteString(jw, "locusType", type);
+ jsonWriteListStart(jw, "locusGenes");
+ char *dupe = cloneString(genes);
+ char *words[128];
+ int n = chopByChar(dupe, '|', words, ArraySize(words));
+ int i;
+ for (i = 0; i < n; ++i)
+ jsonWriteString(jw, NULL, words[i]);
+ freeMem(dupe);
+ jsonWriteListEnd(jw);
+ }
+ }
+ 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 printNewDisplayBanner(char *uiState)
+/* On the classic hyperlink results page, offer a one-click switch to the modern Table display.
+ * The link sets the blatNewPage cart variable (so the choice sticks for future searches) and
+ * reopens the current results (blatReopen) in the new format.
+ * The banner is on by default but can be turned off in hg.conf (blatNewPageBanner=off) to stop
+ * advertising the new page - without releasing new CGIs - while the display itself stays available
+ * to users who already opted in or use a direct link. */
+{
+if (!cfgOptionBooleanDefault("blatNewPageBanner", TRUE))
+ return;
+printf(""
+ ""
+ "There is a new BLAT results page, with a sortable and filterable table of hits, "
+ "gene loci and query coverage."
+ ""
+ "Try the new page\n", uiState);
+}
+
void showAliPlaces(char *pslName, char *faName, char *customText, char *database,
enum gfType qType, enum gfType tType,
char *organism, boolean feelingLucky)
/* Show all the places that align. */
{
boolean useBigPsl = cfgOptionBooleanDefault("useBlatBigPsl", TRUE);
struct lineFile *lf = pslFileOpen(pslName);
struct psl *pslList = NULL, *psl;
char *browserUrl = hgTracksName();
char *hgcUrl = hgcName();
char uiState[64];
char *vis;
char unhideTrack[64];
char *sort = cartUsualString(cart, "sort", pslSortList[0]);
char *output = cartUsualString(cart, "output", outputList[0]);
boolean pslOut = startsWith("psl", output);
boolean pslRawOut = sameWord("pslRaw", output);
boolean jsonOut = sameWord(output, "json");
+/* The modern table is an opt-in replacement for the classic "hyperlink" results page, controlled by
+ * the blatNewPage cart variable (set by the "Try the new display" banner, cleared by the table's
+ * "Old BLAT result page" link). It does not apply to the raw psl/JSON download formats. */
+boolean tableOut = !pslOut && !pslRawOut && !jsonOut && cartUsualBoolean(cart, "blatNewPage", FALSE);
sprintf(uiState, "%s=%s", cartSessionVarName(), cartSessionId(cart));
/* If user has hidden BLAT track, add a setting that will unhide the
track if user clicks on a browser link. */
vis = cartOptionalString(cart, "hgUserPsl");
if (vis != NULL && sameString(vis, "hide"))
snprintf(unhideTrack, sizeof(unhideTrack), "&hgUserPsl=dense");
else
unhideTrack[0] = 0;
while ((psl = pslNext(lf)) != NULL)
{
if (psl->match >= minMatchShown)
slAddHead(&pslList, psl);
@@ -550,33 +834,37 @@
pslTabOut(psl, stdout);
if (pslRawOut)
exit(0);
printf("");
printf("");
}
else if (jsonOut)
{
webStartText();
pslWriteAllJson(pslList, stdout, database, TRUE);
exit(0);
}
else // hyperlink
{
+ if (!tableOut)
+ {
+ printNewDisplayBanner(uiState);
printf("BLAT Search Results
");
+ }
char* posStr = cartOptionalString(cart, "position");
- if (posStr != NULL)
+ if (posStr != NULL && !tableOut)
printf("Go back to %s on the Genome Browser.
\n", browserUrl, posStr);
if (autoBigPsl)
{
char *trackName = NULL;
char *trackDescription = NULL;
getCustomName(database, cart, pslList, &trackName, &trackDescription);
psl = pslList;
char item[1024];
safef(item, sizeof item, "%s %s %s", pslName,faName,psl->qName);
struct dyString *url = dyStringNew(256);
dyStringPrintf(url, "http%s://%s", sameOk(getenv("HTTPS"), "on") ? "s" : "", getenv("HTTP_HOST"));
dyStringPrintf(url, "%s", hgcUrl+2);
@@ -763,47 +1051,39 @@
printf(" Custom track description: ");
cgiMakeTextVar( "trackDescription", trackDescription,50);
printf(" ");
printf("\n");
printInfoIcon("The BLAT results below are temporary and will be replaced by your next BLAT search. "
"However, when saved as a custom track with the button on the left, BLAT results are stored on our "
"servers and can be saved as stable session (View > My Sessions) links that can be shared via email or in manuscripts. "
"\nWe have never cleaned up the data under stable session links so far. "
"To reduce track clutter in your own sessions, you can delete BLAT custom tracks from the main Genome Browser "
"view using the little trash icon next to each custom track.
");
puts(" ");
printf("");
}
- boolean hasDb = sqlDatabaseExists(database);
struct sqlConnection *locusConn = NULL;
struct subText *subList = NULL;
- if (hasDb)
- {
- struct sqlConnection *conn = hAllocConn(database);
- if (cfgOptionBooleanDefault("blatShowLocus", FALSE) && sqlTableExists(conn, "locusName") )
- {
- locusConn = hAllocConn(database);
- slSafeAddHead(&subList, subTextNew("ig:", "intergenic "));
- slSafeAddHead(&subList, subTextNew("ex:", "exon "));
- slSafeAddHead(&subList, subTextNew("in:", "intron "));
- slSafeAddHead(&subList, subTextNew("|", "-"));
- }
- hFreeConn(&conn);
- }
+ if (tableOut || cfgOptionBooleanDefault("blatShowLocus", FALSE))
+ locusConn = blatLocusConn(database, &subList);
+ if (tableOut)
+ printBlatResultsApp(pslList, database, organism, browserUrl, hgcUrl, pslName, faName, customText, uiState, unhideTrack, locusConn, subList);
+ else
+ {
printf("");
// find maximum query name size for padding calculations and
// find maximum target chrom name size for padding calculations
int maxQChromNameSize = 0;
int maxTChromNameSize = 0;
for (psl = pslList; psl != NULL; psl = psl->next)
{
int qLen = strlen(psl->qName);
maxQChromNameSize = max(maxQChromNameSize,qLen);
int tLen = strlen(psl->tName);
maxTChromNameSize = max(maxTChromNameSize,tLen);
}
maxQChromNameSize = max(maxQChromNameSize,5);
maxTChromNameSize = max(maxTChromNameSize,5);
@@ -820,72 +1100,31 @@
printf("SCORE START END QSIZE IDENTITY CHROM ");
spaceOut(stdout, maxTChromNameSize - 5);
printf(" STRAND START END SPAN\n");
printf("----------------------------------------------------------------------------------------------------------");
if (locusConn)
repeatCharOut(stdout, '-', 25);
repeatCharOut(stdout, '-', maxQChromNameSize - 5);
repeatCharOut(stdout, '-', maxTChromNameSize - 5);
printf("\n");
for (psl = pslList; psl != NULL; psl = psl->next)
{
- char *browserHelp = "Open a Genome Browser showing this match";
- char *helpText = "Open a Genome Browser with the BLAT results, but in a new internet browser tab";
- // XX putting SVG into C code like this is ugly. define somewhere? maybe have globals for these?
- char *icon = "";
-
-
- if (customText)
- {
- printf("browser ",
- browserHelp, browserUrl, psl->tName, psl->tStart + 1, psl->tEnd, database,
- customText, uiState, unhideTrack);
- printf("new tab%s ",
- helpText, browserUrl, psl->tName, psl->tStart + 1, psl->tEnd, database,
- customText, unhideTrack, icon);
- }
- else
- {
- if (autoBigPsl)
- {
- // skip ss variable
- printf("browser ",
- browserHelp, browserUrl, psl->tName, psl->tStart + 1, psl->tEnd, database,
- uiState, unhideTrack);
- printf("new tab%s ",
- helpText, browserUrl, psl->tName, psl->tStart + 1, psl->tEnd, database,
- unhideTrack, icon);
- }
- else
- {
- printf("browser ",
- browserHelp, browserUrl, psl->tName, psl->tStart + 1, psl->tEnd, database,
- pslName, faName, uiState, unhideTrack);
- printf("new tab%s ",
- helpText, browserUrl, psl->tName, psl->tStart + 1, psl->tEnd, database,
- pslName, faName, unhideTrack, icon);
- }
- }
- printf("",
- hgcUrl, psl->tStart, pslName, cgiEncode(faName), psl->qName, psl->tName,
- psl->tStart, psl->tEnd, database, uiState);
- printf("details ");
+ printBlatHitLinks(psl, database, browserUrl, hgcUrl, pslName, faName, customText, uiState, unhideTrack);
// print name of this locus
if (locusConn)
{
struct sqlResult *sr = hRangeQuery(locusConn, "locusName", psl->tName, psl->tStart, psl->tEnd, NULL, 0);
char **row;
row = sqlNextRow(sr);
if (row != NULL)
{
char *desc = row[4];
char *descLong = subTextString(subList, desc);
printf("%-25s", descLong);
freeMem(descLong);
}
sqlFreeResult(&sr);
@@ -909,30 +1148,31 @@
printf(" What is chrom_fix?");
else if (endsWith(seq, "_alt"))
printf(" What is chrom_alt?");
else if (endsWith(seq, "_random"))
printf(" What is chrom_random?");
else if (startsWith(seq, "chrUn"))
printf(" What is a chrUn sequence?");
printf("\n");
}
printf("\n");
webNewSection("Help");
puts("\n");
puts("\n");
}
+ }
pslFreeList(&pslList);
}
void trimUniq(bioSeq *seqList)
/* Check that all seq's in list have a unique name. Try and
* abbreviate longer sequence names. */
{
struct hash *hash = newHash(0);
bioSeq *seq;
for (seq = seqList; seq != NULL; seq = seq->next)
{
char *saferString = needMem(strlen(seq->name)+1);
char *c, *s;
@@ -1969,30 +2209,35 @@
if (allGenomes)
queryServer(serve->host, serve->port, db, seq, "query", xType, FALSE, FALSE, TRUE, seqNumber,
serve->genomeDataDir);
else
{
gfAlignStrand(conn, serve->nibDir, seq, TRUE, minMatchShown, tFileCache, gvo);
}
}
gfOutputQuery(gvo, f);
++seqNumber;
}
carefulClose(&f);
if (!allGenomes)
{
+ /* Remember the trash result files so the Table view's "Old BLAT result page" link can
+ * re-render the classic hyperlink view from them within this session without re-running BLAT
+ * (see doOldPageReopen). These are just short paths; the query sequence is not stored. */
+ cartSetString(cart, "blatPslFile", pslTn.forCgi);
+ cartSetString(cart, "blatFaFile", faTn.forCgi);
showAliPlaces(pslTn.forCgi, faTn.forCgi, NULL, serve->db, qType, tType,
organism, feelingLucky);
}
if ((!feelingLucky && !allGenomes) || (autoBigPsl && feelingLucky))
cartWebEnd();
gfFileCacheFree(&tFileCache);
}
void askForSeq(char *organism, char *db)
/* Put up a little form that asks for sequence.
* Call self.... */
{
/* ignore struct serverTable* return, but can error out if not found */
@@ -2338,47 +2583,145 @@
if (!gH->isProt)
{
printf("%d\t", gfR->qFrame);
}
}
printf("\n");
}
}
printf("\n");
}
printf("\n");
}
+static void doShareReopen(char *database, char *organism)
+/* Rebuild the Table view for a shared link (?u=&s=) from the durable bigPsl custom track that was
+ * saved with the session, without re-running BLAT and without any stored query sequence. The
+ * custom track (and its bigBed file) is kept alive by refreshNamedSessionCustomTracks for as long
+ * as the shared session exists, so this is durable. */
+{
+cartWebStart(cart, database, "%s (%s) BLAT Results",
+ trackHubSkipHubName(organism), trackHubSkipHubName(database));
+char *bbFile = blatFindPinnedBigPsl(cart);
+if (bbFile == NULL || !fileExists(bbFile))
+ {
+ printf("These shared BLAT results are no longer available. The custom track that " + "stored them has expired or been removed. Please run a new " + "BLAT search.
\n"); + cartWebEnd(); + return; + } +struct psl *pslList = pslListFromBigPslFile(bbFile); +if (pslList == NULL) + { + printf("These shared BLAT results contained no alignments.
\n"); + cartWebEnd(); + return; + } +pslSortListByVar(&pslList, cartUsualString(cart, "sort", pslSortList[0])); + +struct subText *subList = NULL; +struct sqlConnection *locusConn = blatLocusConn(database, &subList); + +char uiState[64]; +safef(uiState, sizeof uiState, "%s=%s", cartSessionVarName(), cartSessionId(cart)); +printBlatResultsApp(pslList, database, organism, hgTracksName(), hgcName(), + NULL, NULL, NULL, uiState, "", locusConn, subList); +cartWebEnd(); +} + +static void doReopenResults(char *database, char *organism) +/* Re-render the last search's results for the current session from the trash result files saved + * with it (see blatPslFile/blatFaFile), without re-running BLAT. showAliPlaces picks the classic + * or new-table format from the blatNewPage cart variable, so this backs both the classic page's + * "Try the new display" banner and the table's "Old BLAT result page" link. Those trash files are + * only guaranteed for the current session, so if they have been cleaned up, say so rather than + * showing a broken page. */ +{ +char *pslFile = cartOptionalString(cart, "blatPslFile"); +char *faFile = cartOptionalString(cart, "blatFaFile"); +cartWebStart(cart, database, "%s (%s) BLAT Results", + trackHubSkipHubName(organism), trackHubSkipHubName(database)); +if (pslFile == NULL || faFile == NULL || !fileExists(pslFile)) + printf("These BLAT results are no longer available. Please run a new " + "BLAT search.
\n"); +else + showAliPlaces(pslFile, faFile, NULL, database, gftDna, gftDna, organism, FALSE); +cartWebEnd(); +} + void doMiddle(struct cart *theCart) /* Write header and body of html page. */ { char *userSeq; char *db, *organism; boolean clearUserSeq = cgiBoolean("Clear"); allGenomes = cgiVarExists("allGenomes"); cart = theCart; dnaUtilOpen(); +/* The former "table" value of the output dropdown is now the blatNewPage toggle; migrate any stale + * cart value so the dropdown always shows a valid option. */ +if (sameOk(cartOptionalString(cart, "output"), "table")) + cartSetString(cart, "output", "hyperlink"); + +/* Short "Share a link" params: u=