bf1d058d73bdb153279eb4e530d1257d2ccc5675
angie
  Mon Jun 23 10:43:04 2014 -0700
Added ENCODE Regulatory summary tracks for clustered DNase and TFBS,with support for filtering based on BED5 score and factor/cellType/treatment.
refs #11461

diff --git src/hg/hgVai/hgVai.c src/hg/hgVai/hgVai.c
index da8ee5e..884a60a 100644
--- src/hg/hgVai/hgVai.c
+++ src/hg/hgVai/hgVai.c
@@ -70,30 +70,36 @@
 	"  padding-top: 8px; padding-bottom: 5px; margin-top: 5px; margin-bottom: 5px }\n"
 	".sectionLiteHeader { font-weight: bold; font-size:larger; color:#000000;"
 	"  text-align:left; vertical-align:bottom; white-space:nowrap; }\n"
 	"div.sectionLiteHeader.noReorderRemove { padding-bottom:5px; }\n"
 	"div.sourceFilter { padding-top: 5px; padding-bottom: 5px }\n"
 	"</style>\n");
 }
 
 
 INLINE void startCollapsibleSection(char *sectionSuffix, char *title, boolean onByDefault)
 // Wrap shared args to jsBeginCollapsibleSectionFontSize
 {
 jsBeginCollapsibleSectionFontSize(cart, "hgva", sectionSuffix, title, onByDefault, "1.1em");
 }
 
+INLINE void startSmallCollapsibleSection(char *sectionSuffix, char *title, boolean onByDefault)
+// Wrap shared args to jsBeginCollapsibleSectionFontSize
+{
+jsBeginCollapsibleSectionFontSize(cart, "hgva", sectionSuffix, title, onByDefault, "0.9em");
+}
+
 #define endCollapsibleSection jsEndCollapsibleSection
 
 
 static struct dyString *onChangeStart()
 /* Start up a javascript onChange command */
 {
 struct dyString *dy = jsOnChangeStart();
 jsTextCarryOver(dy, hgvaRegionType);
 jsTextCarryOver(dy, hgvaRange);
 return dy;
 }
 
 static char *onChangeClade()
 /* Return javascript executed when they change clade. */
 {
@@ -240,30 +246,138 @@
 printf("<FORM ACTION=\"%s\" NAME=\"mainForm\" ID=\"mainForm\" METHOD=%s>\n",
 	cgiScriptName(), cartUsualString(cart, "formMethod", "GET"));
 cartSaveSession(cart);
 
 //#*** ------------------ end verbatim ---------------
 
 printf("<div class='sectionLiteHeader noReorderRemove'>"
        "Select Genome Assembly and Region</div>\n");
 
 /* Print clade, genome and assembly line. */
 hgGatewayCladeGenomeDb();
 
 //#*** No longer ending form here...
 }
 
+void factorSourceListInputProperties(struct trackDb *tdb, struct slName **retFactorList,
+			     struct slName **retCellTypeList, struct slName **retTreatmentList)
+/* Get a list of factor names used in factorSource track. */
+{
+char *inputsTable = trackDbSetting(tdb, "inputTrackTable");
+if (isEmpty(inputsTable))
+    errAbort("track %s does not have an inputTrackTable setting", tdb->track);
+char query[2048];
+struct sqlConnection *conn = hAllocConn(database);
+if (retFactorList && retCellTypeList && retTreatmentList)
+    {
+    sqlSafef(query, sizeof(query), "select distinct(factor) from %s order by factor", inputsTable);
+    *retFactorList = sqlQuickList(conn, query);
+    sqlSafef(query, sizeof(query), "select distinct(cellType) from %s order by cellType",
+	     inputsTable);
+    *retCellTypeList = sqlQuickList(conn, query);
+    sqlSafef(query, sizeof(query), "select distinct(treatment) from %s order by treatment",
+	     inputsTable);
+    *retTreatmentList = sqlQuickList(conn, query);
+    }
+else
+    errAbort("factorSourceListInputProperties: ret args must be non-NULL.");
+hFreeConn(&conn);
+}
+
+void printMultiselect(char *track, char *label, char *var, struct slName *optionList)
+/* Print a label and multi-select, with hgva_track_var as cart var, with options in optionList,
+ * marking as selected any options stored in cart. */
+{
+printf("%s: ", label);
+char cartVar[1024];
+safef(cartVar, sizeof(cartVar), "hgva_filter_%s_%s", track, var);
+struct hash *selHash = NULL;
+if (cartListVarExists(cart, cartVar))
+    selHash = hashFromSlNameList(cartOptionalSlNameList(cart, cartVar));
+printf("<select id=\"%s\" name=\"%s\" style=\"display: none; font-size:.9em;\" "
+       "class=\"filterBy\" multiple>\n", cartVar, cartVar);
+char *selected = "";
+if (!selHash || hashLookup(selHash, "All"))
+    selected = " selected";
+printf("<option%s>All</option>", selected);
+struct slName *option;
+for (option = optionList;  option != NULL;  option = option->next)
+    {
+    selected = "";
+    if (selHash && hashLookup(selHash, option->name))
+	selected = " selected";
+    printf("<option%s>%s</option>", selected, option->name);
+    }
+printf("</select>\n");
+char shadowVar[1024];
+safef(shadowVar, sizeof(shadowVar), "%s%s", cgiMultListShadowPrefix(), cartVar);
+cgiMakeHiddenVar(shadowVar, "1");
+//printf("<script>$(document).ready(function(){ ddcl.setup($('#%s')[0]); });</script>\n", cartVar);
+}
+
+void printFilterOptions(struct trackDb *tdb)
+/* Print a collapsible filter section for tdb, with controls depending on tdb->type. */
+{
+char sectionName[512], cartVar[512];
+safef(sectionName, sizeof(sectionName), "%s_filter", tdb->track);
+if (sameString(tdb->type, "factorSource"))
+    {
+    puts("<TABLE>");
+    startSmallCollapsibleSection(sectionName, "filter items", FALSE);
+    struct slName *factorOptions = NULL, *cellTypeOptions = NULL, *treatmentOptions = NULL;
+    factorSourceListInputProperties(tdb, &factorOptions, &cellTypeOptions,
+				    &treatmentOptions);
+    printMultiselect(tdb->track, "factor", "name", factorOptions);
+    printMultiselect(tdb->track, "cell type", "cellType", cellTypeOptions);
+    printMultiselect(tdb->track, "treatment", "treatment", treatmentOptions);
+    puts("<BR>");
+    puts("minimum peak score [0-1000]: ");
+    safef(cartVar, sizeof(cartVar), "hgva_filter_%s_score", tdb->track);
+    char *defaultScore = cartUsualString(cart, cartVar, "0");
+    printf("<input type=text name=\"%s\" size=12 value=\"%s\"><BR>",
+	   cartVar, defaultScore);
+    // The dimensions of ui-dropdownchecklist multiselects are not correct when
+    // the item is hidden.  So, when this filter section is made visible, reinit them.
+    printf("<script>\n"
+	   "$(function(){"
+	   "$('tr[id^=\"%s-\"]').bind('show',"
+	   "  function(jqev) { \n"
+	   "    var $multisels = $(jqev.target).find('.filterBy');\n"
+	   "    var multiselElList = $multisels.each(function(ix, el){ return el; });\n"
+	   "    ddcl.reinit(multiselElList, true);"
+	   "  });\n"
+	   "});"
+	   "</script>\n", sectionName);
+    puts("</TABLE>");
+    endCollapsibleSection();
+    }
+if (startsWith("bed 5", tdb->type)) //#*** TODO: detect bed# properly
+    {
+    puts("<TABLE>");
+    startSmallCollapsibleSection(sectionName, "filter items", FALSE);
+    //#*** Also watch out for noScoreFilter or whatever it's called
+    puts("minimum peak score [0-1000]: ");
+    safef(cartVar, sizeof(cartVar), "hgva_filter_%s_score", tdb->track);
+    char *defaultScore = cartUsualString(cart, cartVar, "0");
+    printf("<input type=text name=\"%s\" size=12 value=\"%s\"><BR>",
+	   cartVar, defaultScore);
+    puts("</TABLE>");
+    endCollapsibleSection();
+    }
+}
+
+
 typedef boolean TdbFilterFunction(struct trackDb *tdb, void *filterData);
 /* Return TRUE if tdb passes filter criteria. */
 
 void rFilterTrackList(struct trackDb *trackList, struct slRef **pPassingRefs,
 		      TdbFilterFunction *filterFunc, void *filterData)
 /* Recursively apply filterFunc and filterData to all tracks in trackList and
  * their subtracks. Add an slRef to pPassingRefs for each track/subtrack that passes.
  * Caller should slReverse(pPassingRefs) when recursion is all done. */
 {
 struct trackDb *tdb;
 for (tdb = trackList;  tdb != NULL;  tdb = tdb->next)
     {
     if (tdb->subtracks != NULL)
 	rFilterTrackList(tdb->subtracks, pPassingRefs, filterFunc, filterData);
     if (filterFunc(tdb, filterData))
@@ -602,105 +716,143 @@
 }
 
 void selectDbSnp(boolean gotSnp)
 /* Offer to include rsID (and other fields, or leave that for advanced output??) if available */
 {
 if (!gotSnp)
     return;
 startCollapsibleSection("dbSnp", "Known variation", TRUE);
 cartMakeCheckBox(cart, "hgva_rsId", TRUE);
 printf("Include <A HREF='http://www.ncbi.nlm.nih.gov/projects/SNP/' TARGET=_BLANK>dbSNP</A> "
        "rs# ID if one exists<BR>\n");
 puts("<BR>");
 endCollapsibleSection();
 }
 
+boolean isRegulatoryTrack(struct trackDb *tdb, void *filterData)
+/* For now, just look for a couple specific tracks by tableName. */
+{
+//#*** NEED METADATA
+return (sameString("wgEncodeRegDnaseClusteredV2", tdb->table) ||
+	sameString("wgEncodeRegTfbsClusteredV3", tdb->table));
+}
+
+struct slRef *findRegulatoryTracks()
+/* Look for the very limited set of Regulation tracks that hgVai offers. */
+{
+struct slRef *regTrackRefList = NULL;
+tdbFilterGroupTrack(fullTrackList, fullGroupList, isRegulatoryTrack,
+		    NULL, NULL, &regTrackRefList);
+return regTrackRefList;
+}
+
+void selectRegulatory(struct slRef *trackRefList)
+/* If trackRefList is non-NULL, make a collapsible section with a checkbox for each track,
+ * labelled with longLabel, and optionally some filtering options. */
+{
+if (trackRefList != NULL)
+    {
+    startCollapsibleSection("Regulation", "Regulatory regions", FALSE);
+    // Use a table with checkboxes in one column and label/other stuff that depends on
+    // checkbox in other column.
+    puts("<TABLE>");
+    struct slRef *ref;
+    for (ref = trackRefList;  ref != NULL;  ref = ref->next)
+	{
+	struct trackDb *tdb = ref->val;
+	char cartVar[512];
+	safef(cartVar, sizeof(cartVar), "hgva_track_%s_%s", database, tdb->track);
+	puts("<TR><TD valign=top>");
+	cartMakeCheckBox(cart, cartVar, FALSE);
+	puts("</TD><TD>");
+	struct trackDb *topTdb = trackDbTopLevelSelfOrParent(tdb);
+	printf("<A HREF=\"%s?%s&g=%s\">%s</A><BR>\n", hgTrackUiName(), cartSidUrlString(cart),
+	       topTdb->track, tdb->longLabel);
+	printFilterOptions(tdb);
+	puts("</TD></TR>");
+	}
+    puts("</TABLE><BR>");
+    endCollapsibleSection();
+    }
+}
+
 boolean isConsElTrack(struct trackDb *tdb, void *filterData)
 /* This is a TdbFilterFunction to get "phastConsNwayElements" tracks. */
 {
 return (startsWith("phastCons", tdb->table) && stringIn("Elements", tdb->table));
 }
 
 boolean isConsScoreTrack(struct trackDb *tdb, void *filterData)
 /* This is a TdbFilterFunction to get tracks that start with "phastCons" (no Elements)
  * or "phyloP". */
 {
 return ((startsWith("phastCons", tdb->table) && !stringIn("Elements", tdb->table))
 	|| startsWith("phyloP", tdb->table));
 }
 
 void findCons(struct slRef **retElTrackRefList, struct slRef **retScoreTrackRefList)
 /* See if this database has Conserved Elements and/or Conservation Scores */
 {
 tdbFilterGroupTrack(fullTrackList, fullGroupList, isConsElTrack,
 		    NULL, NULL, retElTrackRefList);
 tdbFilterGroupTrack(fullTrackList, fullGroupList, isConsScoreTrack,
 		    NULL, NULL, retScoreTrackRefList);
 }
 
-void trackRefListToCheckboxes(struct slRef *trackRefList)
+void trackCheckBoxSection(char *sectionSuffix, char *title, struct slRef *trackRefList)
+/* If trackRefList is non-NULL, make a collapsible section with a checkbox for each track,
+ * labelled with longLabel. */
+{
+if (trackRefList != NULL)
     {
+    startCollapsibleSection(sectionSuffix, title, FALSE);
     struct slRef *ref;
     for (ref = trackRefList;  ref != NULL;  ref = ref->next)
 	{
 	struct trackDb *tdb = ref->val;
 	char cartVar[512];
 	safef(cartVar, sizeof(cartVar), "hgva_track_%s_%s", database, tdb->track);
 	cartMakeCheckBox(cart, cartVar, FALSE);
 	struct trackDb *topTdb = trackDbTopLevelSelfOrParent(tdb);
 	printf("<A HREF=\"%s?%s&g=%s\">%s</A><BR>\n", hgTrackUiName(), cartSidUrlString(cart),
 	       topTdb->track, tdb->longLabel);
 	}
-}
-
-void selectCons(struct slRef *elTrackRefList, struct slRef *scoreTrackRefList)
-/* Offer checkboxes for optional Conservation scores. */
-{
-if (elTrackRefList == NULL && scoreTrackRefList == NULL)
-    return;
-if (elTrackRefList != NULL)
-    {
-    startCollapsibleSection("ConsEl", "Conserved elements", FALSE);
-    trackRefListToCheckboxes(elTrackRefList);
     puts("<BR>");
     endCollapsibleSection();
     }
-if (scoreTrackRefList != NULL)
-    {
-    startCollapsibleSection("ConsScore", "Conservation scores",
-			    FALSE);
-    trackRefListToCheckboxes(scoreTrackRefList);
-    endCollapsibleSection();
-    }
 }
 
 void selectAnnotations()
 /* Beyond predictions of protein-coding effect, what other basic data can we integrate? */
 {
 struct slName *dbNsfpTables = findDbNsfpTables();
 boolean gotSnp = findSnpBed4("", NULL, NULL);
+struct slRef *regTrackRefList = findRegulatoryTracks();
 struct slRef *elTrackRefList = NULL, *scoreTrackRefList = NULL;
 findCons(&elTrackRefList, &scoreTrackRefList);
-if (dbNsfpTables == NULL && !gotSnp && elTrackRefList == NULL && scoreTrackRefList == NULL)
+if (dbNsfpTables == NULL && !gotSnp && elTrackRefList == NULL && scoreTrackRefList == NULL
+    && regTrackRefList == NULL)
     return;
 puts("<BR>");
 printf("<div class='sectionLiteHeader'>Select More Annotations (optional)</div>\n");
 // Make wrapper table for collapsible sections:
 puts("<TABLE border=0 cellspacing=5 cellpadding=0 style='padding-left: 10px;'>");
 selectDbNsfp(dbNsfpTables);
 selectDbSnp(gotSnp);
-selectCons(elTrackRefList, scoreTrackRefList);
+selectRegulatory(regTrackRefList);
+trackCheckBoxSection("ConsEl", "Conserved elements", elTrackRefList);
+trackCheckBoxSection("ConsScore", "Conservation scores", scoreTrackRefList);
 puts("</TABLE>");
 }
 
 void selectFiltersFunc()
 /* Options to restrict variants based on gene region/soTerm from gpFx */
 {
 startCollapsibleSection("filtersFunc", "Functional role", FALSE);
 printf("Include variants annotated as<BR>\n");
 cartMakeCheckBox(cart, "hgva_include_intergenic", TRUE);
 printf("intergenic<BR>\n");
 cartMakeCheckBox(cart, "hgva_include_upDownstream", TRUE);
 printf("upstream/downstream of gene<BR>\n");
 cartMakeCheckBox(cart, "hgva_include_nmdTranscript", TRUE);
 printf("in transcript already subject to nonsense-mediated decay (NMD)<BR>\n");
 cartMakeCheckBox(cart, "hgva_include_exonLoss", TRUE);
@@ -849,62 +1001,65 @@
  * If we get bold & offer 1000Genomes VCF, will def. need handling of split chroms.
  * Are we really going to offer genome-wide in hgVai?
  * Up-front limit on #rows of input ?
  *
  * Eventually, we might want a FormatVep that produces structs that are passed
  * forward to multiple output writers... I would want to send it lots of gratorData
  * like a formatter, but it would produce rows like an annoGrator.
  * Maybe annoGrators should accept a bunch of input rows like formatters?
  * or would this grator wrap all the input grators inside?
  */
 
 void doMainPage()
 /* Print out initial HTML of control page. */
 {
 jsInit();
-jsIncludeFile("jquery-ui.js", NULL);
 webIncludeResourceFile("jquery-ui.css");
-jsIncludeFile("hgVarAnnogrator.js", NULL);
+webIncludeResourceFile("ui.dropdownchecklist.css");
 boolean alreadyAgreed = cartUsualBoolean(cart, "hgva_agreedToDisclaimer", FALSE);
 printf("<script>\n"
        "$(document).ready(function() { hgva.disclaimer.init(%s, hgva.userClickedAgree); });\n"
        "</script>\n", alreadyAgreed ? "true" : "false");
 addSomeCss();
 printAssemblySection();
 
 /* Check for variant custom tracks.  If there are none, tell user they need to
  * upload at least one. */
 struct slRef *varTrackList = NULL, *varGroupList = NULL;
 tdbFilterGroupTrack(fullTrackList, fullGroupList, isVariantCustomTrack, NULL,
 		    &varGroupList, &varTrackList);
 puts("<BR>");
 // Make wrapper table for collapsible sections:
 selectVariants(varGroupList, varTrackList);
 boolean gotGP = selectGenes();
 if (gotGP)
     {
     selectAnnotations();
     selectFilters();
     selectOutput();
     submitAndDisclaimer();
     }
 printf("</FORM>");
 
 jsReloadOnBackButton(cart);
 
 webNewSection("Using the Variant Annotation Integrator");
 webIncludeHelpFile("hgVaiHelpText", FALSE);
+jsIncludeFile("jquery-ui.js", NULL);
+jsIncludeFile("hgVarAnnogrator.js", NULL);
+jsIncludeFile("ui.dropdownchecklist.js", NULL);
+jsIncludeFile("ddcl.js", NULL);
 }
 
 void doUi()
 /* Set up globals and make web page */
 {
 cartWebStart(cart, database, "Variant Annotation Integrator");
 doMainPage();
 cartWebEnd();
 /* Save variables. */
 cartCheckout(&cart);
 }
 
 void checkVariantTrack(struct trackDb *tdb)
 /* variantTrack should be either pgSnp or VCF. */
 {
@@ -1014,153 +1169,262 @@
 return tableName;
 }
 
 char *tagFromTableName(char *tableName, char *suffix)
 /* Generate a tag for VEP's extras column or VCF's info column. */
 {
 char *p = strstr(tableName, "dbNsfp");
 if (p != NULL)
     tableName = p + strlen("dbNsfp");
 int suffixLen = (suffix == NULL) ? 0 : strlen(suffix);
 int tagSize = strlen(tableName) + suffixLen + 1;
 char *tag = cloneStringZ(tableName, tagSize);
 if (isNotEmpty(suffix))
     safecat(tag, tagSize, suffix);
 touppers(tag);
+// Some custom shortenings, to avoid very long tag names:
+(void)strSwapStrs(tag, tagSize, "POLYPHEN", "PP");
+(void)strSwapStrs(tag, tagSize, "MUTATION", "MUT");
+(void)strSwapStrs(tag, tagSize, "PHYLOP", "PHP");
+(void)strSwapStrs(tag, tagSize, "PHASTCONS", "PHC");
+(void)strSwapStrs(tag, tagSize, "ELEMENTS", "EL");
+(void)strSwapStrs(tag, tagSize, "PRIMATES", "PRIM");
+(void)strSwapStrs(tag, tagSize, "PLACENTAL", "PLAC");
+if (regexMatch(tag, "^PH.*[0-9]WAY"))
+    (void)strSwapStrs(tag, tagSize, "WAY", "W");
+(void)strSwapStrs(tag, tagSize, "WGENCODEREGDNASECLUSTERED", "DNASE");
+(void)strSwapStrs(tag, tagSize, "WGENCODEREGTFBSCLUSTERED", "TFBS");
 return tag;
 }
 
 enum PolyPhen2Subset stripSubsetFromTrackName(char *trackName)
 /* trackName may have a _suffix for a subset of PolyPhen2; convert that to enum
  * and zero out the suffix so we have the real trackName. */
 {
 enum PolyPhen2Subset subset = noSubset;
 char *p = strchr(trackName, ':');
 if (p != NULL)
     {
     if (sameString(p+1, "HDIV"))
 	subset = HDIV;
     else if (sameString(p+1, "HVAR"))
 	subset = HVAR;
     else
 	errAbort("unrecognized suffix in track_suffix '%s'", trackName);
     *p = '\0';
     }
 return subset;
 }
 
 void updateGratorListAndVepExtra(struct annoGrator *grator, struct annoGrator **pGratorList,
 				 struct annoFormatter *vepOut, enum PolyPhen2Subset subset,
-				 char *column, char *description)
+				 char *column, char *description, boolean isReg)
 /* If grator is non-NULL, add it to gratorList and vepOut's list of items for EXTRAs column. */
 {
 if (grator == NULL)
     return;
 slAddHead(pGratorList, grator);
 if (vepOut != NULL)
     {
     char *tableName = tableNameFromSourceName(grator->streamer.name);
     char *suffix = NULL;
     if (subset == HDIV)
 	suffix = "HDIV";
     else if (subset == HVAR)
 	suffix = "HVAR";
     char *tag = tagFromTableName(tableName, suffix);
     if (isEmpty(description))
 	description = grator->streamer.name;
+    if (isReg)
+	annoFormatVepAddRegulatory(vepOut, (struct annoStreamer *)grator, tag, description, column);
+    else
 	annoFormatVepAddExtraItem(vepOut, (struct annoStreamer *)grator, tag, description, column);
     }
 }
 
 INLINE void updateGratorList(struct annoGrator *grator, struct annoGrator **pGratorList)
 /* If grator is non-NULL, add it to gratorList. */
 {
-updateGratorListAndVepExtra(grator, pGratorList, NULL, 0, NULL, NULL);
+updateGratorListAndVepExtra(grator, pGratorList, NULL, 0, NULL, NULL, FALSE);
 }
 
 void addDbNsfpSeqChange(char *trackName, struct annoAssembly *assembly, struct hash *gratorsByName,
 			struct annoGrator **pGratorList)
 // If the user has selected dbNsfp* data, we also need the underlying dbNsfpSeqChange
 // data, so annoFormatVep can tell whether the variant and gpFx are consistent with the
 // variant and transcript that dbNsfp used to calculate scores.
 {
 //#*** Yet another place where we need metadata:
 char *seqChangeTable = "dbNsfpSeqChange";
 if (hashFindVal(gratorsByName, seqChangeTable) == NULL)
     {
     char *fileName = fileNameFromTable(seqChangeTable);
     if (fileName == NULL)
 	errAbort("'%s' requested, but I can't find fileName for %s",
 		 trackName, seqChangeTable);
     struct annoGrator *grator = gratorFromBigDataFileOrUrl(fileName, assembly, NO_MAXROWS,
 							   agoNoConstraint);
     updateGratorList(grator, pGratorList);
     hashAdd(gratorsByName, seqChangeTable, grator);
     }
 }
 
+static struct dyString *dyInfo = NULL;
+
+struct hash *getTrackFilterVars(char *track)
+/* Return a hash of filter variable names (cart variable suffixes) to slName lists of values. */
+{
+char filterPrefix[512];
+safef(filterPrefix, sizeof(filterPrefix), "hgva_filter_%s_", track);
+struct slPair *filterVars = cartVarsWithPrefix(cart, filterPrefix), *var;
+int prefixLen = strlen(filterPrefix);
+struct hash *varHash = hashNew(0);
+for (var = filterVars;  var != NULL;  var = var->next)
+    {
+    char *varName = var->name+prefixLen;
+    char *val = var->val;
+    struct hashEl *hel = hashLookup(varHash, varName);
+    if (hel != NULL)
+	slNameAddHead((struct slName **)(&hel->val), val);
+    else
+	hashAdd(varHash, varName, slNameNew(val));
+    }
+return varHash;
+}
+
+INLINE boolean isNotAll(struct slName *valList)
+/* Return TRUE unless valList has one element with name "All" (for multiselects). */
+{
+if (slCount(valList) == 1 && sameString(valList->name, "All"))
+    return FALSE;
+return TRUE;
+}
+
+void factorSourceGratorAddFilter(struct annoGrator *grator, char *name, struct slName *valList)
+/* Add filter to factorSource grator. */
+//#*** Do these smarts belong here in hgVai?  Probably not -- should be an hg/lib module with
+//#*** UI/metadata smarts.
+{
+struct annoStreamer *gStreamer = (struct annoStreamer *)grator;
+struct annoFilter *filter = NULL;
+if (sameString(name, "name") || sameString(name, "cellType") || sameString(name, "treatment"))
+    {
+    if (valList && isNotAll(valList))
+	filter = annoFilterFromAsColumn(gStreamer->asObj, name, afMatch, valList);
+    }
+else if (sameString(name, "score"))
+    filter = annoFilterFromAsColumn(gStreamer->asObj, name, afGTE, valList);
+else
+    errAbort("Unrecognized filter name '%s' for %s, type=factorSource", name, gStreamer->name);
+if (filter)
+    gStreamer->addFilters(gStreamer, filter);
+}
+
+void bed5AddFilter(struct annoGrator *grator, char *name, struct slName *valList)
+/* Add filter to bed 5 grator. */
+{
+struct annoStreamer *gStreamer = (struct annoStreamer *)grator;
+struct annoFilter *filter = NULL;
+if (sameString(name, "name"))
+    {
+    if (valList && isNotAll(valList))
+	filter = annoFilterFromAsColumn(gStreamer->asObj, name, afMatch, valList);
+    }
+else if (sameString(name, "score"))
+    filter = annoFilterFromAsColumn(gStreamer->asObj, name, afGTE, valList);
+else
+    errAbort("Unrecognized filter name '%s' for %s, type=bed 5", name, gStreamer->name);
+if (filter)
+    gStreamer->addFilters(gStreamer, filter);
+}
+
+void addFiltersToGrator(struct annoGrator *grator, struct trackDb *tdb)
+/* Look for filter variables in the cart and add filters to grator accordingly. */
+{
+struct hash *varHash = getTrackFilterVars(tdb->track);
+struct hashEl *hel, *helList = hashElListHash(varHash);
+for (hel = helList;  hel != NULL;  hel = hel->next)
+    {
+    char *filterName = hel->name;
+    struct slName *valList = hel->val;
+//#*** Need a much better way to dispatch...
+    if (sameString("factorSource", tdb->type))
+	factorSourceGratorAddFilter(grator, filterName, valList);
+    else if (startsWith("bed 5", tdb->type))
+	bed5AddFilter(grator, filterName, valList);
+    else
+	dyStringPrintf(dyInfo, "Ignoring %s filter %s\n", tdb->track, filterName);
+    }
+hashFree(&varHash);
+}
+
 void addOutputTracks(struct annoGrator **pGratorList, struct hash *gratorsByName,
 		     struct annoFormatter *vepOut, struct annoAssembly *assembly, char *chrom,
 		     boolean doHtml)
 // Construct grators for tracks selected to appear in EXTRAS column
 {
 char trackPrefix[128];
 safef(trackPrefix, sizeof(trackPrefix), "hgva_track_%s_", database);
 int trackPrefixLen = strlen(trackPrefix);
 struct slPair *trackVar, *trackVars = cartVarsWithPrefix(cart, trackPrefix);
 for (trackVar = trackVars;  trackVar != NULL;  trackVar = trackVar->next)
     {
     char *val = trackVar->val;
     if (! (sameWord(val, "on") || atoi(val) > 0))
 	continue;
     char *trackName = trackVar->name + trackPrefixLen;
     if (sameString(trackName, "dbNsfpPolyPhen2"))
 	// PolyPhen2 must have a suffix now -- skip obsolete cartVar from existing carts
 	continue;
     struct annoGrator *grator = hashFindVal(gratorsByName, trackName);
     if (grator != NULL)
 	// We already have this as a grator:
 	continue;
     enum PolyPhen2Subset subset = noSubset;
     char *description = NULL;
     char *column = NULL;
+    boolean isReg = FALSE;
     if (startsWith("dbNsfp", trackName))
 	{
 	// trackName for PolyPhen2 has a suffix for subset -- strip it if we find it:
 	subset = stripSubsetFromTrackName(trackName);
 	description = dbNsfpDescFromTableName(trackName, subset, doHtml);
 	addDbNsfpSeqChange(trackName, assembly, gratorsByName, pGratorList);
 	char *fileName = fileNameFromTable(trackName);
 	if (fileName != NULL)
 	    grator = gratorFromBigDataFileOrUrl(fileName, assembly, NO_MAXROWS, agoNoConstraint);
 	}
     else
 	{
 	struct trackDb *tdb = tdbForTrack(database, trackName, &fullTrackList);
 	if (tdb != NULL)
 	    {
 	    grator = gratorFromTrackDb(assembly, tdb->table, tdb, chrom, NO_MAXROWS, NULL,
 				       agoNoConstraint);
 	    if (grator != NULL)
+		{
 		//#*** Need something more sophisticated but this works for our
 		//#*** limited selection of extra tracks:
 		if (asColumnFind(grator->streamer.asObj, "name") != NULL)
 		    column = "name";
+		addFiltersToGrator(grator, tdb);
+		}
 	    description = tdb->longLabel;
+	    isReg = isRegulatoryTrack(tdb, NULL);
 	    }
 	}
-    updateGratorListAndVepExtra(grator, pGratorList, vepOut, subset, column, description);
+    updateGratorListAndVepExtra(grator, pGratorList, vepOut, subset, column, description, isReg);
     if (grator != NULL)
 	hashAdd(gratorsByName, trackName, grator);
     }
 }
 
 void addFilterTracks(struct annoGrator **pGratorList, struct hash *gratorsByName,
 		     struct annoAssembly *assembly, char *chrom)
 // Add grators for filters (not added to vepOut):
 {
 if (!cartUsualBoolean(cart, "hgva_include_snpCommon", TRUE))
     {
     struct annoGrator *grator = gratorForSnpBed4(gratorsByName, "Common", assembly, chrom,
 						 agoMustNotOverlap, NULL);
     updateGratorList(grator, pGratorList);
     }
@@ -1818,30 +2082,31 @@
 if (varTdb == NULL)
     {
     if (isHubTrack(variantTrack))
 	warn("Can't find hub track '%s'", variantTrack);
     else
 	warn("Can't find tdb for variant track '%s'", variantTrack);
     }
 else
     checkVariantTrack(varTdb);
 return varTdb;
 }
 
 void doQuery()
 /* Translate simple form inputs into anno* components and execute query. */
 {
+dyInfo = dyStringNew(0);
 char *chrom = NULL;
 uint start = 0, end = 0;
 if (sameString(regionType, hgvaRegionTypeRange))
     getCartPosOrDie(&chrom, &start, &end);
 struct annoAssembly *assembly = getAnnoAssembly(database);
 
 char *geneTrack = cartString(cart, "hgva_geneTrack");
 struct trackDb *geneTdb = tdbForTrack(database, geneTrack, &fullTrackList);
 if (geneTdb == NULL)
     {
     warn("Can't find tdb for gene track %s", geneTrack);
     doUi();
     return;
     }
 
@@ -1897,48 +2162,50 @@
 struct annoGrator *gratorList = NULL;
 slAddHead(&gratorList, gpVarGrator);
 if (snpGrator != NULL)
     slAddHead(&gratorList, snpGrator);
 
 // Text or HTML output?
 char *outFormat = cartUsualString(cart, "hgva_outFormat", "vepTab");
 boolean doHtml = sameString(outFormat, "vepHtml");
 
 // Initialize VEP formatter:
 struct annoFormatter *vepOut = annoFormatVepNew("stdout", doHtml,
 						primary, primaryLongLabel,
 						(struct annoStreamer *)gpVarGrator,
 						geneTdb->longLabel,
 						(struct annoStreamer *)snpGrator,
-						snpDesc);
+						snpDesc, assembly);
 addOutputTracks(&gratorList, gratorsByName, vepOut, assembly, chrom, doHtml);
 addFilterTracks(&gratorList, gratorsByName, assembly, chrom);
 
 slReverse(&gratorList);
 
 if (doHtml)
     {
     webStart(cart, database, "Annotated Variants in VEP/HTML format");
     }
 else
     {
     // Undo the htmlPushEarlyHandlers() because after this point they make ugly text:
     popWarnHandler();
     popAbortHandler();
     textOpen();
     webStartText();
     }
+if (isNotEmpty(dyInfo->string))
+    puts(dyInfo->string);
 struct annoGratorQuery *query = annoGratorQueryNew(assembly, primary, gratorList, vepOut);
 struct slName *comment;
 for (comment = commentList;  comment != NULL;  comment = comment->next)
     vepOut->comment(vepOut, comment->name);
 if (chrom != NULL)
     annoGratorQuerySetRegion(query, chrom, start, end);
 annoGratorQueryExecute(query);
 annoGratorQueryFree(&query);
 
 if (doHtml)
     webEnd();
 else
     textOutClose(&compressPipeline);
 }