a7088ea8bd1828450e85dca8a04b937e7bd38ca1
galt
Mon Oct 8 12:38:24 2018 -0700
Sqashed BLAT ALl Genomes branch
diff --git src/hg/hgBlat/hgBlat.c src/hg/hgBlat/hgBlat.c
index cb65a54..8b7872f 100644
--- src/hg/hgBlat/hgBlat.c
+++ src/hg/hgBlat/hgBlat.c
@@ -1,48 +1,296 @@
/* hgBlat - CGI-script to manage fast human genome sequence searching. */
/* Copyright (C) 2014 The Regents of the University of California
* See README in this or parent directory for licensing information. */
+#include Go back to %s on the Genome Browser. BLAT on DNA is designed to\n"
"quickly find sequences of 95%% and greater similarity of length 25 bases or\n"
"more. It may miss more divergent or shorter sequence alignments. It will find\n"
"perfect sequence matches of 20 bases.\n"
@@ -883,36 +1640,184 @@
"for academic, personal, and non-profit purposes. Non-exclusive commercial\n"
"licenses are also available. See the \n"
"Kent Informatics\n"
"website for details. For more information on the graphical version of BLAT, click the Help \n"
"button on the top menu bar");
if (hIsGsidServer())
printf(". ");
if (!sameString(output, "psl no header"))
pslxWriteHead(stdout, qType, tType);
for (psl = pslList; psl != NULL; psl = psl->next)
pslTabOut(psl, stdout);
printf("
");
}
-else
+else // hyperlink
{
printf("BLAT Search Results
");
char* posStr = cartOptionalString(cart, "position");
if (posStr != NULL)
printf("");
- printf(" ACTIONS QUERY SCORE START END QSIZE IDENTITY CHRO STRAND START END SPAN\n");
- printf("---------------------------------------------------------------------------------------------------\n");
+
+ // find maximum target chrom name size for padding calculations
+ int maxChromNameSize = 0;
+ for (psl = pslList; psl != NULL; psl = psl->next)
+ {
+ int l = strlen(psl->tName);
+ maxChromNameSize = max(maxChromNameSize,l);
+ }
+ maxChromNameSize = max(maxChromNameSize,5);
+
+ // header padding
+ char temp[256];
+
+ temp[0] = 0;
+ safecatRepeatChar(temp, sizeof temp, ' ', (maxChromNameSize - 5));
+
+ printf(" ACTIONS QUERY SCORE START END QSIZE IDENTITY CHROM ");
+ printf("%s", temp);
+ printf(" STRAND START END SPAN\n");
+
+ printf("------------------------------------------------------------------------------------------------------");
+ temp[0] = 0;
+ safecatRepeatChar(temp, sizeof temp, '-', (maxChromNameSize - 5));
+ printf("%s\n", temp);
+
for (psl = pslList; psl != NULL; psl = psl->next)
{
if (customText)
printf("",
browserUrl, psl->tName, psl->tStart + 1, psl->tEnd, database,
customText, uiState, unhideTrack);
else
printf("",
browserUrl, psl->tName, psl->tStart + 1, psl->tEnd, database,
pslName, faName, uiState, unhideTrack);
printf("browser ");
printf("",
hgcUrl, psl->tStart, pslName, cgiEncode(faName), psl->qName, psl->tName,
psl->tStart, psl->tEnd, database, uiState);
printf("details ");
- printf("%-14s %5d %5d %5d %5d %5.1f%% %4s %2s %9d %9d %6d\n",
+
+ temp[0] = 0;
+ safecpy(temp, sizeof temp, psl->tName);
+ int padding = maxChromNameSize - strlen(psl->tName);
+ safecatRepeatChar(temp, sizeof temp, ' ', padding);
+
+ printf("%-14s %5d %5d %5d %5d %5.1f%% %s %-2s %9d %9d %6d\n",
psl->qName, pslScore(psl), psl->qStart+1, psl->qEnd, psl->qSize,
100.0 - pslCalcMilliBad(psl, TRUE) * 0.1,
- skipChr(psl->tName), psl->strand, psl->tStart+1, psl->tEnd,
+ temp, psl->strand, psl->tStart+1, psl->tEnd,
psl->tEnd - psl->tStart);
}
printf("
\n");
puts("\n");
puts("
\n", gH->queryRC ? "-" : "+", gH->dnaSize);
+
+/* Put together query command. */
+safef(buf, sizeof buf, "%s%s %d", gfSignature(), gH->type, gH->dnaSize);
+mustWriteFd(gH->sd, buf, strlen(buf));
+
+if (read(gH->sd, buf, 1) < 0)
+ errAbort("queryServerFinish: read failed: %s", strerror(errno));
+if (buf[0] != 'Y')
+ errAbort("Expecting 'Y' from server, got %c", buf[0]);
+mustWriteFd(gH->sd, gH->dna, gH->dnaSize);
+
+if (gH->complex)
+ {
+ char *s = netRecieveString(gH->sd, buf);
+ if (!s)
+ errAbort("expected response from gfServer with tileSize");
+ dyStringPrintf(gH->dbg,"%s
\n", s); // from server: tileSize 4
+ }
+
+for (;;)
+ {
+ if (netGetString(gH->sd, buf) == NULL)
+ break;
+ if (sameString(buf, "end"))
+ {
+ dyStringPrintf(gH->dbg,"%d matches
\n", matchCount);
+ break;
+ }
+ else if (startsWith("Error:", buf))
+ {
+ errAbort("%s", buf);
+ break;
+ }
+ else
+ {
+ dyStringPrintf(gH->dbg,"%s
\n", buf);
+ // chop the line into words
+ int numHits = 0;
+ char *line = buf;
+ char *word=NULL;
+ int i=0;
+ struct gfResult *gfR = NULL;
+ AllocVar(gfR);
+ gfR->qStrand = (gH->queryRC ? '-' : '+');
+ while ((word = nextWord(&line)) != NULL)
+ {
+ if (i == 0)
+ { // e.g. 3139
+ gfR->qStart = sqlUnsigned(word);
+ }
+ if (i == 1)
+ { // e.g. 3220
+ gfR->qEnd = sqlUnsigned(word);
+ }
+ if (i == 2)
+ { // e.g. hg38.2bit:chr1
+ char *colon = strchr(word, ':');
+ if (colon)
+ {
+ gfR->chrom = cloneString(colon+1);
+ }
+ else
+ { // e.g. chr10.nib
+ char *dot = strchr(word, '.');
+ if (dot)
+ {
+ *dot = 0;
+ gfR->chrom = cloneString(word);
+ }
+ else
+ errAbort("Expecting colon or period in the 3rd field of gfServer response");
+ }
+ }
+ if (i == 3)
+ { // e.g. 173515
+ gfR->tStart = sqlUnsigned(word);
+ }
+ if (i == 4)
+ { // e.g. 173586
+ gfR->tEnd = sqlUnsigned(word);
+ }
+ if (i == 5)
+ { // e.g. 14
+ numHits = sqlUnsigned(word);
+
+ // Frustrated with weird little results with vastly exaggerated hit-score,
+ // I have flattened that out so it maxes out at #tiles that could fit
+ // It seems to still work just fine. I am going to leave it for now.
+ // One minor note, by suppressing extra scores for short exons,
+ // it will not prefer alignments with the same number of hits, but more exons.
+ if (!gH->complex) // dna xType
+ {
+ // maximum tiles that could fit in qSpan
+ int limit = ((gfR->qEnd - gfR->qStart) - 6)/5;
+ ++limit; // for a little extra.
+ if (numHits > limit)
+ numHits = limit;
+ }
+
+ gfR->numHits = numHits;
+ }
+ if (gH->complex)
+ {
+ if (i == 6)
+ { // e.g. + or -
+ gfR->tStrand = word[0];
+ }
+ if (i == 7)
+ { // e.g. 0,1,2
+ gfR->tFrame = sqlUnsigned(word);
+ }
+ if (!gH->isProt)
+ {
+ if (i == 8)
+ { // e.g. 0,1,2
+ gfR->qFrame = sqlUnsigned(word);
+ }
+ }
+ }
+ else
+ {
+ gfR->tStrand = '+'; // dna search only on + target strand
+ }
+ ++i;
+ }
+
+ if (gH->complex)
+ {
+ char *s = netGetLongString(gH->sd);
+ if (s == NULL)
+ break;
+ dyStringPrintf(gH->dbg,"%s
\n", s); //dumps out qstart1 start1 qstart2 tstart2 ...
+ freeMem(s);
+ }
+
+ slAddHead(&gH->gfList, gfR);
+ }
+ ++matchCount;
+ }
+slReverse(&gH->gfList);
+
+unTranslateCoordinates(gH); // convert back to untranslated coordinates
+slSort(&gH->gfList, slGfResultsCmp); // sort by tStrand, chrom, tStart
+
+
+
+struct qFrameResults
+/* Information about hits on a genome assembly */
+ {
+ int maxGeneHits; /* Highest gene hit-count */
+ char *maxGeneChrom; /* Target Chrom for gene with max gene hits */
+ int maxGeneTStart; /* Target Start Coordinate for gene with max hits */
+ int maxGeneTEnd; /* Target End Coordinate for gene with max hits*/
+ int maxGeneExons; /* Number of Exons in gene with max hits */
+ char maxGeneTStrand;/* + or - TStrand for gene with max hits */
+ };
+
+int qFrame = 0;
+int qFrameStart = 0;
+int qFrameEnd = 0;
+
+if (gH->complex && !gH->isProt) // rnax, dnax
+ {
+ qFrameEnd = 2;
+ }
+
+struct qFrameResults r[3];
+
+for(qFrame = qFrameStart; qFrame <= qFrameEnd; ++qFrame)
+ {
+ findBestGene(gH, qFrame); // find best gene-line thing with most hits
+
+ r[qFrame].maxGeneHits = gH->maxGeneHits;
+ r[qFrame].maxGeneChrom = cloneString(gH->maxGeneChrom);
+ r[qFrame].maxGeneTStart = gH->maxGeneTStart;
+ r[qFrame].maxGeneTEnd = gH->maxGeneTEnd;
+ r[qFrame].maxGeneExons = gH->maxGeneExons;
+ r[qFrame].maxGeneTStrand = gH->maxGeneTStrand;
+
+ }
+
+// combine the results from all 3 qFrames
+
+if (gH->complex && !gH->isProt) // rnax, dnax
+ {
+ int biggestHit = -1;
+ int bigQFrame = -1;
+
+ // find the qFrame with the biggest hits
+ for(qFrame = qFrameStart; qFrame <= qFrameEnd; ++qFrame)
+ {
+ if (r[qFrame].maxGeneHits > biggestHit)
+ {
+ bigQFrame = qFrame;
+ biggestHit = r[qFrame].maxGeneHits;
+ }
+ }
+
+ // save combined final answers back in gH
+ gH->maxGeneHits = 0;
+ gH->maxGeneTStrand = r[bigQFrame].maxGeneTStrand;
+ gH->maxGeneChrom = cloneString(r[bigQFrame].maxGeneChrom);
+ gH->maxGeneExons = r[bigQFrame].maxGeneExons;
+ gH->maxGeneTStart = r[bigQFrame].maxGeneTStart;
+ gH->maxGeneTEnd = r[bigQFrame].maxGeneTEnd;
+
+ for(qFrame = qFrameStart; qFrame <= qFrameEnd; ++qFrame)
+ {
+
+ // ignore frames that do not have same tStrand, chrom, and overlap with the best
+ if (!(
+ (r[qFrame].maxGeneTStrand == r[bigQFrame].maxGeneTStrand) &&
+ sameOk(r[qFrame].maxGeneChrom, r[bigQFrame].maxGeneChrom) &&
+ // overlap start,end between this and bigQFrame
+ (
+ (r[qFrame].maxGeneTEnd > r[bigQFrame].maxGeneTStart) &&
+ (r[bigQFrame].maxGeneTEnd > r[qFrame].maxGeneTStart)
+ )
+ ))
+ {
+ continue; // incompatible with best result, skip it
+ }
+
+ // Add total hits
+ gH->maxGeneHits += r[qFrame].maxGeneHits; // should be pretty accurate
+ // Find biggest exon count
+ if (gH->maxGeneExons < r[qFrame].maxGeneExons) // often underestimates actual exon count.
+ {
+ gH->maxGeneExons = r[qFrame].maxGeneExons;
+ }
+ // Adjust tStart
+ if (gH->maxGeneTStart > r[qFrame].maxGeneTStart)
+ {
+ gH->maxGeneTStart = r[qFrame].maxGeneTStart;
+ }
+ // Adjust tEnd
+ if (gH->maxGeneTEnd < r[qFrame].maxGeneTEnd)
+ {
+ gH->maxGeneTEnd = r[qFrame].maxGeneTEnd;
+ }
+
+ }
+
+ gH->maxGeneHits /= 3; // average over 3 frames.
+
+ char qStrand = (gH->queryRC ? '-' : '+');
+ safef(gH->maxGeneStrand, sizeof gH->maxGeneStrand, "%c%c", qStrand, gH->maxGeneTStrand);
+
+ }
+
+
+close(gH->sd);
+}
+
+int gfConnectEx(char *host, char *port)
+/* Try to connect to gfServer */
+{
+int conn = -1;
+if (allGenomes)
+ conn = gfMayConnect(host, port); // returns -1 on failure
+else
+ conn = gfConnect(host, port); // errAborts on failure.
+return conn;
+}
+
+void blatSeq(char *userSeq, char *organism, char *database, int dbCount)
/* Blat sequence user pasted in. */
{
FILE *f;
struct dnaSeq *seqList = NULL, *seq;
struct tempName pslTn, faTn;
int maxSingleSize, maxTotalSize, maxSeqCount;
int minSingleSize = minMatchShown;
char *genome, *db;
char *type = cgiString("type");
char *seqLetters = cloneString(userSeq);
struct serverTable *serve;
int conn;
int oneSize, totalSize = 0, seqCount = 0;
boolean isTx = FALSE;
boolean isTxTx = FALSE;
boolean txTxBoth = FALSE;
struct gfOutput *gvo;
boolean qIsProt = FALSE;
enum gfType qType, tType;
struct hash *tFileCache = gfFileCacheNew();
-boolean feelingLucky = cgiBoolean("Lucky");
+// allGenomes ignores I'm Feeling Lucky for simplicity
+boolean feelingLucky = cgiBoolean("Lucky") && !allGenomes;
+char *xType = NULL;
+if (allGenomes)
+ {
+ db = database;
+ genome = organism;
+ }
+else
getDbAndGenome(cart, &db, &genome, oldVars);
-if(!feelingLucky)
+
+
+if(!feelingLucky && !allGenomes)
cartWebStart(cart, db, "%s BLAT Results", trackHubSkipHubName(organism));
+
/* Load user sequence and figure out if it is DNA or protein. */
if (sameWord(type, "DNA"))
{
seqList = faSeqListFromMemText(seqLetters, TRUE);
uToT(seqList);
isTx = FALSE;
+ xType = "dna";
}
else if (sameWord(type, "translated RNA") || sameWord(type, "translated DNA"))
{
seqList = faSeqListFromMemText(seqLetters, TRUE);
uToT(seqList);
isTx = TRUE;
isTxTx = TRUE;
+ xType = "rnax";
txTxBoth = sameWord(type, "translated DNA");
+ if (txTxBoth)
+ xType = "dnax";
}
else if (sameWord(type, "protein"))
{
seqList = faSeqListFromMemText(seqLetters, FALSE);
isTx = TRUE;
qIsProt = TRUE;
+ xType = "prot";
}
-else
+else // BLAT's Guess
{
seqList = faSeqListFromMemTextRaw(seqLetters);
- isTx = !seqIsDna(seqList);
+ isTx = !seqIsDna(seqList); // only tests first element, assumes the rest are the same type.
if (!isTx)
{
+ xType = "dna";
for (seq = seqList; seq != NULL; seq = seq->next)
{
seq->size = dnaFilteredSize(seq->dna);
dnaFilter(seq->dna, seq->dna);
toLowerN(seq->dna, seq->size);
subChar(seq->dna, 'u', 't');
}
}
else
{
for (seq = seqList; seq != NULL; seq = seq->next)
{
seq->size = aaFilteredSize(seq->dna);
aaFilter(seq->dna, seq->dna);
toUpperN(seq->dna, seq->size);
}
qIsProt = TRUE;
+ xType = "prot";
}
}
if (seqList != NULL && seqList->name[0] == 0)
{
freeMem(seqList->name);
seqList->name = cloneString("YourSeq");
}
trimUniq(seqList);
/* If feeling lucky only do the first one. */
if(feelingLucky && seqList != NULL)
{
seqList->next = NULL;
}
@@ -683,142 +1391,179 @@
{
qType = gftDna;
tType = gftDna;
}
pslxWriteHead(f, qType, tType);
if (qType == gftProt)
{
minSingleSize = 14;
}
else if (qType == gftDnaX)
{
minSingleSize = 36;
}
-
+int seqNumber = 0;
/* Loop through each sequence. */
for (seq = seqList; seq != NULL; seq = seq->next)
{
printf(" "); fflush(stdout); /* prevent apache cgi timeout by outputting something */
oneSize = realSeqSize(seq, !isTx);
// Impose half the usual bot delay per sequence
+
+ if (dbCount == 0)
hgBotDelayFrac(0.5);
if (++seqCount > maxSeqCount)
{
warn("More than %d input sequences, stopping at %s
(see also: cgi-bin/hg.conf hgBlat.maxSequenceCount setting).",
maxSeqCount, seq->name);
break;
}
if (oneSize > maxSingleSize)
{
warn("Sequence %s is %d letters long (max is %d), skipping",
seq->name, oneSize, maxSingleSize);
continue;
}
if (oneSize < minSingleSize)
{
warn("Warning: Sequence %s is only %d letters long (%d is the recommended minimum)",
seq->name, oneSize, minSingleSize);
// we could use "continue;" here to actually enforce skipping,
// but let's give the short sequence a chance, it might work.
// minimum possible length = tileSize+stepSize, so mpl=16 for dna stepSize=5, mpl=10 for protein.
if (qIsProt && oneSize < 1) // protein does not tolerate oneSize==0
continue;
}
totalSize += oneSize;
if (totalSize > maxTotalSize)
{
warn("Sequence %s would take us over the %d letter limit, stopping here.",
seq->name, maxTotalSize);
break;
}
- conn = gfConnect(serve->host, serve->port);
+
+ conn = gfConnectEx(serve->host, serve->port);
+
if (isTx)
{
gvo->reportTargetStrand = TRUE;
if (isTxTx)
{
+ if (allGenomes)
+ queryServer(conn, db, seq, "transQuery", xType, TRUE, FALSE, FALSE, seqNumber);
+ else
gfAlignTransTrans(&conn, serve->nibDir, seq, FALSE, 5,
tFileCache, gvo, !txTxBoth);
if (txTxBoth)
{
reverseComplement(seq->dna, seq->size);
- conn = gfConnect(serve->host, serve->port);
+ conn = gfConnectEx(serve->host, serve->port);
+ if (allGenomes)
+ queryServer(conn, db, seq, "transQuery", xType, TRUE, FALSE, TRUE, seqNumber);
+ else
gfAlignTransTrans(&conn, serve->nibDir, seq, TRUE, 5,
tFileCache, gvo, FALSE);
}
}
else
{
+ if (allGenomes)
+ queryServer(conn, db, seq, "protQuery", xType, TRUE, TRUE, FALSE, seqNumber);
+ else
gfAlignTrans(&conn, serve->nibDir, seq, 5, tFileCache, gvo);
}
}
else
{
+ if (allGenomes)
+ queryServer(conn, db, seq, "query", xType, FALSE, FALSE, FALSE, seqNumber);
+ else
gfAlignStrand(&conn, serve->nibDir, seq, FALSE, minMatchShown, tFileCache, gvo);
reverseComplement(seq->dna, seq->size);
- conn = gfConnect(serve->host, serve->port);
+ conn = gfConnectEx(serve->host, serve->port);
+ if (allGenomes)
+ queryServer(conn, db, seq, "query", xType, FALSE, FALSE, TRUE, seqNumber);
+ else
gfAlignStrand(&conn, serve->nibDir, seq, TRUE, minMatchShown, tFileCache, gvo);
}
gfOutputQuery(gvo, f);
+ ++seqNumber;
}
carefulClose(&f);
+if (!allGenomes)
+ {
showAliPlaces(pslTn.forCgi, faTn.forCgi, NULL, serve->db, qType, tType,
organism, feelingLucky);
-if(!feelingLucky)
+ }
+
+if(!feelingLucky && !allGenomes)
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 */
findServer(db, FALSE);
/* JavaScript to update form when org changes */
char *onChangeText = ""
"document.mainForm.changeInfo.value='orgChange';"
"document.mainForm.submit();";
char *userSeq = NULL;
+char *type = NULL;
printf(
"\n");
webNewSection("About BLAT");
printf(
"
\n"); +for (gH = pfdDone; gH; gH = gH->next) + { + if (gH->hide) // hide weaker of pairs for dna and dnax with reverse-complimented queries. + continue; + if (gH->error) + printf("%s %s %s %s %d %s %s\n", + gH->faName, gH->genome, gH->db, gH->networkErrMsg, gH->queryRC, gH->type, gH->xType); + else + { + int tStart = gH->maxGeneTStart; + int tEnd = gH->maxGeneTEnd; + // convert back to neg strand coordinates for debug display + if (gH->complex && (gH->maxGeneTStrand == '-')) + { + int tempTStart = tStart; + tStart = gH->maxGeneChromSize - tEnd; + tEnd = gH->maxGeneChromSize - tempTStart; + } + printf("%s %s %s %s %d %d %s %s %d %d\n", + gH->faName, gH->genome, + gH->db, + gH->maxGeneChrom, gH->maxGeneHits, gH->queryRC, gH->type, gH->xType, tStart, tEnd); + printf("%s", gH->dbg->string); + + struct gfResult *gfR = NULL; + for(gfR=gH->gfList; gfR; gfR=gfR->next) + { + printf("(%3d)\t%4d\t%4d\t%s\t(%3d)\t%9d\t%9d\t%3d\t", + (gfR->qEnd-gfR->qStart), + gfR->qStart, gfR->qEnd, gfR->chrom, + (gfR->tEnd-gfR->tStart), + gfR->tStart, gfR->tEnd, gfR->numHits); + + if (gH->complex) + { + printf("%c\t%d\t", gfR->tStrand, gfR->tFrame); + if (!gH->isProt) + { + printf("%d\t", gfR->qFrame); + } + } + printf("\n"); + } + } + printf("\n"); + + } +printf("\n"); +} + + 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(); orgChange = sameOk(cgiOptionalString("changeInfo"),"orgChange"); if (orgChange) { cgiVarSet("db", hDefaultDbForGenome(cgiOptionalString("org"))); } getDbAndGenome(cart, &db, &organism, oldVars); char *oldDb = cloneString(db); findClosestServer(&db, &organism); /* Get sequence - from userSeq variable, or if * that is empty from a file. */ @@ -926,31 +1831,209 @@ { userSeq = cartOptionalString(cart, "seqFile"); } if (isEmpty(userSeq) || orgChange) { cartWebStart(theCart, db, "%s BLAT Search", trackHubSkipHubName(organism)); if (differentString(oldDb, db)) printf("
Note: BLAT search is not available for %s %s; " "defaulting to %s %s
Name | " + "Genome | " + "Assembly | " + ); + + printf( + "Hits | " + "Chrom | " + ); + if (debuggingGfResults) + { + printf( + "Pos | " + "Strand | " + "Exons | " + "Query RC'd | " + "Type | " + ); + } + printf("\n"); + printf("|||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
%s | %s | %s | %s | ", + gH->faName, gH->genome, gH->db, gH->networkErrMsg); + if (debuggingGfResults) + printf(" | %d | %s | ", + gH->queryRC, gH->type); + printf("\n"); + } + else + { + char pos[256]; + safef(pos, sizeof pos, "%s:%d-%d", gH->maxGeneChrom, gH->maxGeneTStart+1, gH->maxGeneTEnd); // 1-based closed coord + if (!gH->maxGeneChrom) // null + pos[0] = 0; // empty string + safef(id, sizeof id, "res%d", idCount); + printf(" | %s | %s | " + "%s | " + , gH->faName, gH->genome, id, gH->db); + + printf("%d | %s | ", gH->maxGeneHits, + gH->maxGeneChrom ? gH->maxGeneChrom : ""); + + if (debuggingGfResults) + { + printf("%s | %s | ", pos, gH->maxGeneStrand); + printf( "%d | %d | %s | ", gH->maxGeneExons, gH->queryRC, gH->xType); + } + + printf("\n"); + jsOnEventByIdF("click", id, + "document.mainForm.org.value=\"%s\";" // some have single-quotes in their value. + "document.mainForm.db.value='%s';" + "document.mainForm.submit();" + "return false;" // cancel the default link url + , gH->genome, gH->db + ); + idCount++; + } + + printf("