b33425c626414803bbddc702caecc256c5022277
max
Fri Mar 20 09:10:59 2026 -0700
defining detailsHistogram tdb statements and an example histogram drawing code for it, used in the trexplorer track, refs #37273
diff --git src/hg/hgc/bigBedClick.c src/hg/hgc/bigBedClick.c
index 2e0e62feeed..23c972f03f3 100644
--- src/hg/hgc/bigBedClick.c
+++ src/hg/hgc/bigBedClick.c
@@ -4,30 +4,31 @@
* See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */
#include "common.h"
#include "wiggle.h"
#include "cart.h"
#include "hgc.h"
#include "hCommon.h"
#include "hgColors.h"
#include "bigBed.h"
#include "hui.h"
#include "subText.h"
#include "web.h"
#include "chromAlias.h"
#include "quickLift.h"
#include "hgConfig.h"
+#include "jsHelper.h"
static void bigGenePredLinks(char *track, char *item)
/* output links to genePred driven sequence dumps */
{
printf("
Links to sequence:
\n");
printf("\n");
puts("- \n");
hgcAnchorSomewhere("htcTranslatedPredMRna", item, "translate", seqName);
printf("Translated Protein from genomic DNA\n");
puts("
\n");
puts("- \n");
hgcAnchorSomewhere("htcGeneMrna", item, track, seqName);
printf("Predicted mRNA \n");
puts("
\n");
@@ -279,31 +280,31 @@
// skip an optional '#' on the first field name
if (i == 0 && startsWith("#", name))
name = skipBeyondDelimit(name, '#');
if (startsWith("_", name) && !(startsWith("_json", name)) && !(startsWith("json", name)))
detailsTabPrintSpecial(name, val, extraFields);
else if (slNameInList(tblFieldNames, name))
{
userTbl = (struct embeddedTbl *)hashFindVal(fieldsToEmbeddedTbl, name);
userTbl->encodedTbl = val;
printEmbeddedTable(tdb, userTbl, tableLabelsDy);
}
else
{
- printFieldLabel(name);
+ printFieldLabelWithId(name, name);
printf("%s | \n", val);
}
printCount++;
}
printf("\n");
dyStringPrintf(tableLabelsDy, "];\n");
jsInline(dyStringCannibalize(&tableLabelsDy));
return printCount;
}
struct slPair *parseDetailsTablUrls(struct trackDb *tdb)
/* Parse detailsUrls setting string into an slPair list of {offset column name, fileOrUrl} */
{
char *detailsUrlsStr = trackDbSetting(tdb, "detailsUrls");
if (!detailsUrlsStr)
@@ -527,30 +528,117 @@
}
if (isCustomTrack(tdb->track))
{
time_t timep = bbiUpdateTime(bbi);
printBbiUpdateTime(&timep);
}
char *motifPwmTable = trackDbSetting(tdb, "motifPwmTable");
if (motifPwmTable)
{
struct dnaSeq *seq = hDnaFromSeq(database, bed->chrom, bed->chromStart, bed->chromEnd, dnaLower);
if (bed->strand[0] == '-')
reverseComplement(seq->dna, seq->size);
struct dnaMotif *motif = loadDnaMotif(bed->name, motifPwmTable);
motifHitSection(seq, motif);
}
+
+ // detailsJs: load JavaScript files and export selected field data as JSON
+ char *detailsJs = trackDbSetting(tdb, "detailsJs");
+ if (detailsJs)
+ {
+ // Include each comma-separated JS file
+ char *jsFiles = cloneString(detailsJs);
+ char *words[64];
+ int jsFileCount = chopCommas(jsFiles, words);
+ int ji;
+ for (ji = 0; ji < jsFileCount; ji++)
+ {
+ char *jsFile = trimSpaces(words[ji]);
+ if (isNotEmpty(jsFile))
+ jsIncludeFile(jsFile, NULL);
+ }
+
+ // Build the bedDetails JSON object
+ struct dyString *ds = dyStringNew(1024);
+ dyStringPrintf(ds, "var bedDetails = {\"track\":\"%s\",\"chrom\":\"%s\","
+ "\"start\":%d,\"end\":%d",
+ tdb->track, chrom, bed->chromStart, bed->chromEnd);
+
+ // Export requested fields
+ char *detailsJsFieldsStr = trackDbSetting(tdb, "detailsJsFields");
+ if (detailsJsFieldsStr && extraFieldPairs)
+ {
+ dyStringAppend(ds, ",\"fields\":{");
+ char *fieldsCopy = cloneString(detailsJsFieldsStr);
+ char *fieldNames[256];
+ int nFields = chopCommas(fieldsCopy, fieldNames);
+ boolean first = TRUE;
+ int fi;
+ for (fi = 0; fi < nFields; fi++)
+ {
+ char *fn = trimSpaces(fieldNames[fi]);
+ char *fv = slPairFindVal(extraFieldPairs, fn);
+ if (fv == NULL)
+ fv = "";
+ if (!first)
+ dyStringAppendC(ds, ',');
+ dyStringPrintf(ds, "\"%s\":", fn);
+ dyStringAppendC(ds, '"');
+ // JSON-escape the value
+ char *c;
+ for (c = fv; *c; c++)
+ {
+ if (*c == '"')
+ dyStringAppend(ds, "\\\"");
+ else if (*c == '\\')
+ dyStringAppend(ds, "\\\\");
+ else if (*c == '\n')
+ dyStringAppend(ds, "\\n");
+ else
+ dyStringAppendC(ds, *c);
+ }
+ dyStringAppendC(ds, '"');
+ first = FALSE;
+ }
+ dyStringAppendC(ds, '}');
+ }
+
+ // Include detailsJsArgs if present
+ char *detailsJsArgs = trackDbSetting(tdb, "detailsJsArgs");
+ if (detailsJsArgs)
+ dyStringPrintf(ds, ",\"args\":%s", detailsJsArgs);
+
+ dyStringAppend(ds, "};\n");
+
+ // Call the default function derived from each JS filename (strip .js)
+ // e.g. barChart.js -> barChart(bedDetails)
+ for (ji = 0; ji < jsFileCount; ji++)
+ {
+ char *jsFile = trimSpaces(words[ji]);
+ if (isEmpty(jsFile))
+ continue;
+ char funcName[256];
+ safecpy(funcName, sizeof(funcName), jsFile);
+ // strip .js extension
+ char *dot = strrchr(funcName, '.');
+ if (dot)
+ *dot = '\0';
+ dyStringPrintf(ds, "$(document).ready(function() { %s(bedDetails); });\n", funcName);
+ }
+
+ jsInline(dyStringCannibalize(&ds));
+ }
}
if (!found)
{
printf("No item %s starting at %d\n", emptyForNull(item), start);
}
lmCleanup(&lm);
bbiFileClose(&bbi);
}
void genericBigBedClick(struct sqlConnection *conn, struct trackDb *tdb,
char *item, int start, int end, int bedSize)
/* Handle click in generic bigBed track. */
{
char *fileName = bbiNameFromSettingOrTable(tdb, conn, tdb->table);
bigBedClick(fileName, tdb, item, start, end, bedSize);