src/hg/instinct/hgHeatmap2/hgHeatmap2.c 1.80

1.80 2009/10/22 18:42:17 cszeto
Added local url ability
Index: src/hg/instinct/hgHeatmap2/hgHeatmap2.c
===================================================================
RCS file: /projects/compbio/cvsroot/kent/src/hg/instinct/hgHeatmap2/hgHeatmap2.c,v
retrieving revision 1.79
retrieving revision 1.80
diff -b -B -U 1000000 -r1.79 -r1.80
--- src/hg/instinct/hgHeatmap2/hgHeatmap2.c	7 Oct 2009 00:17:25 -0000	1.79
+++ src/hg/instinct/hgHeatmap2/hgHeatmap2.c	22 Oct 2009 18:42:17 -0000	1.80
@@ -1,2378 +1,2378 @@
 /********************************************************************************/
 /* Copyright 2007-2009 -- The Regents of the University of California           */
 /********************************************************************************/
 
 /* hgHeatmap is a CGI script that produces a web page containing
  * a graphic with all chromosomes in genome, and a heatmap or two
  * on top of them. This module just contains the main routine,
  * the routine that dispatches to various page handlers, and shared
  * utility functions. */
 
 #include "common.h"
 #include "bed.h"
 #include "cart.h"
 #include "linefile.h"
 #include "customTrack.h"
 #include "genoLay.h"
 #include "hash.h"
 #include "hCommon.h"
 #include "hdb.h"
 #include "hgHeatmap2.h"
 #include "hPrint.h"
 #include "htmshell.h"
 #include "hui.h"
 #include "trackLayout.h"
 #include "web.h"
 #include "microarray.h"
 #include "ra.h"
 #include "hgStatsLib.h"
 #include "featuresLib.h" 
 #include "json.h"
 #include "hgHeatmapLib.h"
 #include "heatmapUtility.h"
 #include "hgUsers.h"
 
 static char const rcsid[] = "$Id$";
 
 /* ---- Global variables. ---- */
 struct cart *cart;	         /* This holds cgi and other variables between clicks. */
 struct hash *oldVars;	         /* Old cart hash. */
 char *database;	                 /* Name of the selected database - hg15, mm3, or the like. */
 struct trackLayout tl;           /* Dimensions of things, fonts, etc. */
 struct slRef *ghList;	         /* List of active heatmaps */
 struct hash *ghHash;	         /* Hash of active heatmaps */
 
 struct geneSetGroup *gsList;     /* List of available gene set groups */
 struct hash *gsHash;             /* Hash of available gene set groups */
 struct geneSet *allGeneSets;     /* List of current pathway collections */
 
 char *theDataset="";             /* Name of the selected dataset */
 
 char *ctFileName = NULL;                 /* Custom track file. */
 struct customTrack *ctList = NULL;  /* Custom tracks. */
 boolean hasCustomTracks = FALSE;         /* whether any custom tracks are for this db*/
 struct slName *browserLines = NULL;      /* Custom track "browser" lines. */
 
 
 static char *heatMapDbProfile = "localDb";  // database profile to use
 
 
 struct chromZoom {
     struct chromZoom *next;
     
     char *fullName;
     int size;
     int baseStart;
     int baseEnd;
 };
 
 void usage()
 /* Explain usage and exit. */
 {
 errAbort(
 	 "hgHeatmap2 - Full genome (as opposed to chromosome) view of data\n"
 	 "This is a cgi script, but it takes the following parameters:\n"
 	 "   db=<genome database>\n"
 	 "   hght_table=on where table is name of bed table\n"
 	 );
 }
 
 /******************* BEGIN helper routines ***************************/
 
 char *cartSettingsString(char *prefix, char *functionName)
 {
 if (!prefix || !functionName)
     return NULL;
 
 struct hashEl *hEl = cartFindPrefix(cart, prefix);
 if (!hEl)
     return NULL;
 
 struct dyString *dy = newDyString(1000);
 while (hEl)
     {
     char *name = hEl->name;
     char *val = hEl->val;
     dyStringPrintf(dy, "%s=%s,", name, val);
 
     hEl = hEl->next;
     }
 dyStringPrintf(dy, "%s,%s", functionName, VERSION);
 
 return dy->string;
 } 
 
 
 int experimentHeight()
 /* Return height of an individual experiment */
 {
 return 1;
 }
 
 char *dataSetRaName()
 {
 return "datasets.ra";
 }
 
 int heatmapHeight(struct genoHeatmap *gh)
 /* Return height of the heatmap. */
 {
 if (gh == NULL)
     return 0;
 
 if (sameWord(gh->dataType,"bed 15"))
     return slCount(gh->sampleList) * experimentHeight();
 
 return gh->height;
 }
 
 
 struct column *dataSetsCols()
 {
 char *raName_data = dataSetRaName();
 struct column *colList = getColumns(NULL, raName_data, NULL);
 return colList;
 }
 
 struct rgbColor *parseRGBString(char *str)
 {  /* parses a comma-separated list of colors, returns a rgbColor */
 if (!str)
     return NULL;
 
 struct slName *sl, *slList = slNameListFromComma(str);
 if (slCount(slList) != 3)
     return NULL;
 
 sl = slList;
 if (!isdigit(sl->name[0]))
     return NULL;
 int r = atoi(sl->name);
 if (r < 0 || r > 255)
     return NULL;
 
 sl = sl->next;
 if (!isdigit(sl->name[0]))
     return NULL;
 int g = atoi(sl->name);
 if (g < 0 || g > 255)
     return NULL;
 
 sl = sl->next;
 if (!isdigit(sl->name[0]))
     return NULL;
 int b = atoi(sl->name);
 if (b < 0 || b > 255)
     return NULL;
 
 struct rgbColor *rgb = NULL;
 AllocVar(rgb);
 rgb->r = r;
 rgb->g = g;
 rgb->b = b;
 
 return rgb;
 }
 
 void setUserDefinedColor(struct heatmapLay *hl)
 {
 if (!hl)
     return;
 char *lowColor  = cartOptionalString(cart, hgh2LowColor);
 char *zeroColor = cartOptionalString(cart, hgh2ZeroColor);
 char *highColor = cartOptionalString(cart, hgh2HighColor);
 
 struct rgbColor *low  = parseRGBString(lowColor);
 struct rgbColor *zero = parseRGBString(zeroColor);
 struct rgbColor *high = parseRGBString(highColor);
 
 if (!low || !zero || !high)
     return;
 
 hl->lowColor  = low;
 hl->zeroColor = zero;
 hl->highColor = high;
 }
 
 
 void parseChromString(char *chromStr, char **chrom, int *base)
 {
 if (!chromStr)
     return;
 
 chromStr = replaceChars(chromStr, ",", "");
 
 if (sameString(chromStr, "undefined"))
     return;
 
 struct slName *slList = slNameListFromString(chromStr, ':');
 
 if (slCount(slList) == 1)
     {
     char *str = slList->name;
     if (isdigit(str[0]))
 	*base = atoi(str);
     else
 	*chrom = cloneString(str);
     }
 else if (slCount(slList) == 2)
     {
     struct slName *sl = slList;
     *chrom = cloneString(sl->name);
  
     sl = sl->next;
     *base = atoi(sl->name);
     }
 else
     errAbort("Got wrong slCount(chromStr)");
 }
 
 void parseChromStrings(char *startStr, char **startChrom, int *startBase, 
 		       char *stopStr, char **stopChrom, int *stopBase)
 {
 parseChromString(startStr, startChrom, startBase);
 parseChromString(stopStr, stopChrom, stopBase);
 
 /* To conform to regular genome browser search, assume constrained to sane
  * chrom when one chrom string is ommited */
 if ((*startChrom != NULL) && (*stopChrom == NULL))
     *stopChrom = cloneString(*startChrom);
 if ((*startChrom == NULL) && (*stopChrom != NULL))
     *stopChrom = cloneString(*startChrom);
 }
 
 void setupFeaturePriority(struct genoHeatmap *gh, char *featureList)
 {
 if (!gh)
     return;
 
 struct sqlConnection *conn = getFeatureDbConn(gh);
 if (!conn)
     return;
 
 char *raName = gh->raFile;
 struct column *col, *colList = getColumns(conn, raName, gh->patDb);
 hFreeConn(&conn);
 
 struct slName *features = slNameListFromComma(featureList);
 
 int i, notPriority = slCount(features);
 
 struct dyString *priorities = newDyString(1000);
 for (col = colList; col; col = col->next)
     {
     if ((i = slNameFindIx(features, col->name)) != -1)
 	dyStringPrintf(priorities, "%s %d ", col->name, i); 
     else
 	{
 	dyStringPrintf(priorities, "%s %d ", col->name, notPriority); 
 	notPriority++;
 	}
     } 
 
 char varName[256];
 safef(varName, sizeof(varName),"%s.%s", colOrderVar, gh->patDb);
 cartSetString(cart, varName, priorities->string);
 }
 
 
 void setupFeatureVisibility(struct genoHeatmap *gh)
 {
 if (!gh)
     return;
 if (!sameWord(gh->dataType, "bed 15"))
     return;
 
 char *featureList = cartOptionalString(cart, hgh2FeatureList);
 if (!featureList)
     return;
 
 struct sqlConnection *conn = getFeatureDbConn(gh);
 if (!conn)
     return;
 
 char *raName = gh->raFile;
 struct column *col, *colList = getColumns(conn, raName, gh->patDb);
 hFreeConn(&conn);
 
 struct slName *features = slNameListFromComma(featureList);
 int i;
 for (col = colList; col; col = col->next)
     {
     char varName[256];
     safef(varName, sizeof(varName), "%s%s.vis", colConfigPrefix, col->name);
 
     if ((i = slNameFindIx(features, col->name)) != -1)
 	cartSetString(cart, varName, "1");
     else
 	cartSetString(cart, varName, "0");
     } 
 
 setupFeaturePriority(gh, featureList);
 }
 
 
 
 void setupSortByFeature(struct genoHeatmap *gh)
 {
 char *featureSort = cartOptionalString(cart, hgh2FeatureSort);
 char *featureSortDir = cartOptionalString(cart, hgh2FeatureSortDir);
 
 if (!featureSort || !featureSortDir)
     return;
 
 struct slName *f, *features = slNameListFromComma(featureSort); 
 struct slName *d, *directions = slNameListFromComma(featureSortDir);
 
 if (slCount(features) != slCount(directions))
     return;
 
 f = features;
 d = directions;
 int i;
 for (i = 0; i < slCount(features); i++)
     {
     char varName[256];
     safef(varName, sizeof(varName), "%s.%s.%s.%s", colConfigPrefix, 
 	  f->name, "sortType", gh->patDb);
 
     if (sameString(d->name, "DESC"))
 	cartSetString(cart, varName, "descending");
     else if (sameString(d->name, "ASC"))
 	cartSetString(cart, varName, "ascending");
 
     f = f->next;
     d = d->next;
     }
 }
 
 struct slName *setupSortByChromPosition(struct genoHeatmap *gh)
 {
 char *chromStart = cartOptionalString(cart, hgh2SortChromStart);
 char *chromEnd = cartOptionalString(cart, hgh2SortChromEnd);
 if (!chromStart || !chromEnd)
     return NULL;
 
 int direction = 1;
 char *sortDir = cartOptionalString(cart, hgh2SortDir);
 if (!sortDir)
     direction = 1;
 else if (sameString(sortDir, "ASC"))
     direction = 1;
 else if (sameString(sortDir, "DESC"))
     direction = -1;
 
 char *startChrom = NULL;
 int startBase = -1;
 char *stopChrom = NULL;
 int stopBase = -1;
 
 parseChromStrings(chromStart, &startChrom, &startBase, 
 		  chromEnd, &stopChrom, &stopBase);
 stopBase += 1;   // to fix a drawing issue.
 
 if (!startChrom || !stopChrom)
     return NULL;
 if (!sameString(startChrom, stopChrom))
     return NULL;   // let's force sorting to data on same chromosome.
 
 return samplesSortedByChromPos(gh, startChrom, startBase, stopBase, direction);
 }
 
 struct slName *setupSortByGene(struct genoHeatmap *gh)
 {
 char *sortGene = cartOptionalString(cart, hgh2SortGene);
 
 if (!sortGene)
     return NULL;
 
 int direction = 1;
 char *sortDir = cartOptionalString(cart, hgh2SortDir);
 
 if (!sortDir)
     direction = 1;
 else if (sameString(sortDir, "ASC"))
     direction = 1;
 else if (sameString(sortDir, "DESC"))
     direction = -1;
 
 return samplesSortedByGene(gh, sortGene, direction);
 }
 
 void setupSort(struct genoHeatmap *gh)
 {
 if (!gh)
     return;
 if (!sameWord(gh->dataType, "bed 15"))
     return;
 
 char *sortType = cartOptionalString(cart, hgh2SortType);
 
 if (!sortType)
     {
    defaultOrder(gh);
     return;
     }
 
 if (sameString(sortType, "feature"))
     {
     setupSortByFeature(gh);
     sortPersonOrder(gh);
     }
 else if (sameString(sortType, "chrom"))
     {
     struct slName *sortList = setupSortByChromPosition(gh);
     setSampleOrder(gh, sortList); 
     }
 else if (sameString(sortType, "gene"))
     {
     struct slName *sortList = setupSortByGene(gh);
     setSampleOrder(gh, sortList);	
     }
 else
     defaultOrder(gh);  // make sure gh->sampleOrder is set to default.
 
 }
 
 
 void setupMinMaxSubgroupCartVars(struct genoHeatmap *gh, struct slName *features, 
 				 struct slName *mins, struct slName *maxs, int subsetNum)
 {
 int i = 0;
 struct slName *f = features;
 struct slName *mn = mins;
 struct slName *mx = maxs;
 
 for (i = 0; i < slCount(features); i++)
     {
     char minName[128];
     safef(minName, sizeof(minName), "%s.%s.%s.%s.%d",
 	  advFilterPrefix, f->name, "min", gh->patDb, subsetNum);
 
     char maxName[128];
     safef(maxName, sizeof(maxName), "%s.%s.%s.%s.%d",
 	  advFilterPrefix, f->name, "max", gh->patDb, subsetNum);
 
     cartSetString(cart, minName, mn->name);
     cartSetString(cart, maxName, mx->name);
 
     f = f->next;
     mn = mn->next;
     mx = mx->next;
     }
 } 
 
 void setMinMaxSubgroups(struct genoHeatmap *gh)
 {
 char *subgroup1Features = cartOptionalString(cart, hgh2Subgroup1Features);
 char *subgroup2Features = cartOptionalString(cart, hgh2Subgroup2Features);
 
 if (!subgroup1Features || !subgroup2Features)
     return;
 
 char *subgroup1Mins = cartOptionalString(cart, hgh2Subgroup1Mins);
 char *subgroup1Maxs = cartOptionalString(cart, hgh2Subgroup1Maxs);
 
 char *subgroup2Mins = cartOptionalString(cart, hgh2Subgroup2Mins);
 char *subgroup2Maxs = cartOptionalString(cart, hgh2Subgroup2Maxs);
 
 if (!subgroup1Mins || !subgroup1Maxs
     || !subgroup2Mins || !subgroup2Maxs )
     return;
 
 struct slName *features1 = slNameListFromComma(subgroup1Features);
 struct slName *features2 = slNameListFromComma(subgroup2Features);
 
 struct slName *mins1 = slNameListFromComma(subgroup1Mins);
 struct slName *maxs1 = slNameListFromComma(subgroup1Maxs);
 
 struct slName *mins2 = slNameListFromComma(subgroup2Mins);
 struct slName *maxs2 = slNameListFromComma(subgroup2Maxs);
 
 if (slCount(features1) != slCount(features2))
     return;
 if (slCount(mins1) != slCount(maxs1))
     return;
 if (slCount(mins2) != slCount(maxs2))
     return;
 if ((slCount(features1) != slCount(mins1)) || (slCount(features1) != slCount(mins2)))
     return;
 
 setupMinMaxSubgroupCartVars(gh, features1, mins1, maxs1, 0);
 setupMinMaxSubgroupCartVars(gh, features2, mins2, maxs2, 1);
 }
 
 void setupCodedSubgroupCartVars(struct genoHeatmap *gh, struct slName *features, 
 				struct slName *codes, int subsetNum)
 {
 int i = 0;
 struct slName *ft = features;
 struct slName *cd = codes;
 
 struct hash *codeHash = hashNew(0);
 for (i = 0; i < slCount(features); i++)
     {
     hashAdd(codeHash, ft->name, cd->name);
     
     ft = ft->next;
     cd = cd->next;
     }
 
 char *name;
 struct hashCookie hc = hashFirst(codeHash);
 while ((name = hashNextName(&hc)) != NULL)
     {
     char code[128];
     safef(code, sizeof(code), "%s.%s.%s.%s.%d",
 	  advFilterPrefix, name, "codes", gh->patDb, subsetNum);
 
     struct dyString *dy = dyStringNew(100);
 
     struct hashEl *el = hashLookup(codeHash, name);
     while (el)
 	{
 	char *codedVal = el->val;
 	dyStringPrintf(dy, "%s", codedVal);
 	if (el->next)
 	    dyStringPrintf(dy, ",");
 	el = el->next;
 	}
     cartSetString(cart, code, dy->string);
     dyStringFree(&dy);
     }
 } 
 
 void setCodedSubgroup(struct genoHeatmap *gh, char *features, char *codes, int subset)
 {
 if (!features || !codes)
     return;
 
 struct slName *slFeatures = slNameListFromComma(features);
 struct slName *slCodes = slNameListFromComma(codes);
 
 if (!slFeatures || !slCodes)
     return;
 if (slCount(slFeatures) != slCount(slCodes))
     return;
 
 setupCodedSubgroupCartVars(gh, slFeatures, slCodes, subset);
 }
 
 
 void setCodedSubgroups(struct genoHeatmap *gh)
 {
 char *subgroup1Features = cartOptionalString(cart, hgh2Subgroup1CodedFeatures);
 char *subgroup1Codes = cartOptionalString(cart, hgh2Subgroup1Codes);
 setCodedSubgroup(gh, subgroup1Features, subgroup1Codes, 0);
 
 char *subgroup2Features = cartOptionalString(cart, hgh2Subgroup2CodedFeatures);
 char *subgroup2Codes = cartOptionalString(cart, hgh2Subgroup2Codes);
 setCodedSubgroup(gh, subgroup2Features, subgroup2Codes, 1);
 }
 
 void setSubgroups(struct genoHeatmap *gh)
 {
 setMinMaxSubgroups(gh);
 setCodedSubgroups(gh);
 }
 
 void addCustomDbHeatmaps(struct genoHeatmap **list, char *raName)
 {
 struct customTrack *ct = NULL;
 	
 ctList = customTracksParseCart(database, cart, NULL, NULL);
 
 if(!ctList)
 	return;
 
 struct genoHeatmap *gh;
 
 for(ct = ctList; ct != NULL; ct = ct->next)
 	{
 	gh = getCustomHeatmap(ct);
 	if (!gh)
 	continue;
 
 	slAddHead(list, gh);
 	}
 
 }
 
 void addUserDbHeatmaps(struct genoHeatmap **list, char *raName)
 {
 /* only to test user authentication currently */
 char *tokenIdStr = cartOptionalString(cart, hgh2UserTokenId);
 char *userIdStr = cartOptionalString(cart, hgh2UserId);
 if (!tokenIdStr || !userIdStr)
     return;
 
 int tokenId = atoi(tokenIdStr);
 int userId = atoi(userIdStr);
 
 struct slName *sl, *datasets = getUserDatasets(cart, tokenId, userId);
 if (!datasets)
     return;
 if (!slCount(datasets))
     return;
 
 struct sqlConnection *conn = hAllocConnProfile(heatMapDbProfile, database);
 if (!conn)
     return;
 
 char *name;
 struct genoHeatmap *gh;
 
 struct column *colList = getColumns(NULL, raName, NULL);
 struct column *col;
 
 if (!colList)
     return;
 
 for (col = colList; col != NULL; col = col->next)
     {
     name = (char *)(hashFindVal(col->settings, "name"));
 
     boolean userHasAccess = FALSE;
     for (sl = datasets; sl; sl = sl->next)
 	if (sameString(sl->name, name))
 	    userHasAccess = TRUE;
 
     if (!userHasAccess)
 	continue;
 
     gh = getHeatmap(conn, database, name, col->settings);
 
     if (!gh)
 	continue;
     if (!gh->private)
 	continue;  // do not public private datasets.
 
     slAddHead(list, gh);
     }
 
 hFreeConn(&conn);
 }
 
 struct genoHeatmap *getUserDbHeatmaps(char *raName)
 /* Get graphs defined. Requried to be present in the database as well as 
    in the datasets.ra file */
 {
 struct genoHeatmap *list = NULL;
 addUserDbHeatmaps(&list, raName);
 
 return list;
 }
 
 
 
 struct genoHeatmap *getDbHeatmaps(char *set, char *raName)
 /* Get graphs defined. Requried to be present in the database as well as 
    in the datasets.ra file */
 {
 struct sqlConnection *conn = hAllocConnProfile(heatMapDbProfile, database);
 if (!conn)
     return NULL;
 
 char varName[1024];
 char *name;
 struct genoHeatmap *list = NULL, *gh;
 
 struct column *colList = getColumns(NULL, raName, NULL);
 struct column *col;
 
 if (colList == NULL)
     errAbort("Couldn't find anything from %s", raName);
 
 for (col = colList; col != NULL; col = col->next)
     {
     name = (char *)(hashFindVal(col->settings, "name"));
     safef(varName, sizeof(varName), "%s%s.%s", hghDataConfigPrefix, name, hghVis);
 
     /* cgi visibility */
     char *vis;
     if (!hashLookup(cart->hash,varName))
 	/* default visibility */
 	vis = (char *)(hashFindVal(col->settings, "visibility"));
     else
 	{
 	boolean cgiVis = cartBoolean(cart,varName);
 	if (cgiVis)
 	    vis = "on";
 	else
 	    vis = "off";
 	}
 
     gh = getHeatmap(conn, database, name, col->settings);
     if (!gh)
 	continue;
     if (gh->private)
 	continue;  // do not include private datasets.
 
     slAddHead(&list,gh);
     }
 
 hFreeConn(&conn);
 
 addUserDbHeatmaps(&list, raName);
 addCustomDbHeatmaps(&list, raName);
 slReverse(&list);
 
 return list;
 }
 
 void getGenoHeatmaps(char* set)
 /* Set up ghList and ghHash with all available genome graphs */
 {
 if (isEmpty(set))
     return;
 
 char *datasetRaName = dataSetRaName();
 struct genoHeatmap *dbList = getDbHeatmaps(set, datasetRaName);
 
 struct genoHeatmap *gh;
 struct slRef *ref, *refList = NULL;
 
 /* Build up ghList from user and db lists. */
 for (gh = dbList; gh != NULL; gh = gh->next)
     refAdd(&refList, gh);
 slReverse(&refList);
 ghList = refList;
 
 /* Build up ghHash from ghList. */
 ghHash = hashNew(0);
 for (ref = ghList; ref != NULL; ref = ref->next)
     {
     gh = ref->val;
     hashAdd(ghHash, gh->name, gh);
     }
 }
 
 void getStatsFxn(char *statsFxn, boolean (**func)(float data1[], unsigned long n1,
 						 float data2[], unsigned long n2,            
 						  float *r, float *prob), float *colorCutoff)
 {
 /* get statistical function setting */
 
 if (sameWord(statsFxn,"diffMean"))
     {
     *func = aveDiff;
     // no gray color colorCutoff
     *colorCutoff = 0;
     }
 else if (sameWord(statsFxn,"ttest"))
     {
     *func = ttest;
     // this colorCutoff is equivalent of p<0.05 in -log10(p)
     *colorCutoff = hghProbCutoff;
     }
 else if (sameWord(statsFxn,"wilcoxon"))
     {
     *func = wilcoxon;
     // this colorCutoff is equivalent of p<0.05 in -log10(p)
     *colorCutoff = hghProbCutoff;
     }
 else if (sameWord(statsFxn,"expressionCoherence"))
     {
     *func = expressionCoherence;
     // this colorCutoff is equivalent of p<0.05 in -log10(p)
     *colorCutoff = hghProbCutoff;
     }
 else if (sameWord(statsFxn,"fishersExact"))
     {
     *func = fishersExact;
     // this colorCutoff is equivalent of p<0.05 in -log10(p)
     *colorCutoff = hghProbCutoff;
     }
 else if (sameWord(statsFxn,"fishersLinearDisc"))
     {
     *func = fishersLinearDisc;
     // this colorCutoff is equivalent of p<0.05 in -log10(p)
     *colorCutoff = hghProbCutoff;
     }
 else if (sameWord(statsFxn,"jarqueBera"))
     {
     *func = jarqueBera;
     // this colorCutoff is equivalent of p<0.05 in -log10(p)
     *colorCutoff = hghProbCutoff;
     }
 else if (sameWord(statsFxn,"levene"))
     {
     *func = levene;
     // this colorCutoff is equivalent of p<0.05 in -log10(p)
     *colorCutoff = hghProbCutoff;
     }
 else if (sameWord(statsFxn,"brownForsythe"))
     {
     *func = brownForsythe;
     // this colorCutoff is equivalent of p<0.05 in -log10(p)
     *colorCutoff = hghProbCutoff;
     }
 }
 
 /******************* END helper routines ***************************/
 
 /************* JSON Return strings *********************************/
 
 void jsonFeatureList(struct json *js, struct genoHeatmap *gh)
 /* JSON return string for feature panel */
 {
 struct json *new, *list = jsonAddContainerList(js, "features");
 new = list;
 
 struct sqlConnection *conn = getFeatureDbConn(gh);
 if (!conn)
     return;
 
 char *raName = gh->raFile;
 struct column *col, *colList = getColumns(conn, raName, gh->patDb);
 for (col = colList; col != NULL; col = col->next)
     {
     jsonAddString(new, "name", col->name);
     jsonAddString(new, "shortLabel", col->shortLabel);
     jsonAddString(new, "longLabel", col->longLabel);
 
     if (col->cellCoded(col, conn))
 	{
 	struct slName *slList = col->cellCodedVals(col, conn);
 	jsonAddSlName(new, "codes", slList);
 	}
     else
 	{
 	char *minValStr = col->cellMinVal(col, conn);
 	double minVal, maxVal;
 	if (!minValStr)
 	    minVal = 0.0;
 	else
 	    minVal = atof(minValStr);
 
 	char *maxValStr = col->cellMaxVal(col, conn);
 	if (!maxValStr)
 	    maxVal = 0.0;
 	else
 	    maxVal = atof(maxValStr);
 	jsonAddDouble(new, "minVal", minVal);
 	jsonAddDouble(new, "maxVal", maxVal);
 	}
     if (col->next)
 	new = jsonAddContainerToList(&list);
     }
 hFreeConn(&conn);
 }
 
 
 void jsonChromHeatmapLay(struct json *js, struct heatmapLay *hl)
 /* JSON return string for chromosome layout info */
 {
 if (!hl)
     return;
 
 jsonAddInt(js, "sampleHeight", hl->sampleHeight);
 struct json *new, *list = jsonAddContainerList(js, hl->name);
 new = list;
 
 struct hmElement *hEl;
 for (hEl = hl->elements; hEl; hEl = hEl->next)
     {
     jsonAddString(new, "chromNum", hEl->name+3);
     jsonAddInt(new, "pixelStart", hEl->pixelStart);
     jsonAddInt(new, "pixelEnd", hEl->pixelEnd);
     jsonAddInt(new, "baseStart", hEl->baseStart);  // make this and below %lu
     jsonAddInt(new, "baseEnd", hEl->baseEnd);
 
     if (hEl->next)
 	new = jsonAddContainerToList(&list);
     }
 }
 
 void jsonGenesetLayout(struct json *js, struct heatmapLay *hlList)
 /* JSON return string for geneset layout info */
 {
 if (!hlList)
     return;
 
 jsonAddInt(js, "sampleHeight", hlList->sampleHeight);
 struct json *set, *setList = jsonAddContainerList(js, "geneSetInfo");
 set = setList;
 
 struct heatmapLay *hl;
 for (hl = hlList; hl; hl = hl->next)
     {
     jsonAddString(set, "name", hl->name);
     jsonAddInt(set, "pixelStart", hl->pixelStart);
     jsonAddInt(set, "pixelEnd", hl->pixelEnd);
     
     struct json *gene, *geneList = jsonAddContainerList(set, "geneInfo");
     gene = geneList;
     struct hmElement *hEl;
     for (hEl = hl->elements; hEl; hEl = hEl->next)
 	{
 	jsonAddString(gene, "probeName", hEl->probeName);
 	jsonAddString(gene, "geneName", hEl->name);
 	jsonAddInt(gene, "pixelStart", hEl->pixelStart);
 	jsonAddInt(gene, "pixelEnd", hEl->pixelEnd);
 	
 	if (hEl->next)
 	    gene = jsonAddContainerToList(&geneList);
 	}
 
     if (hl->next)
 	set = jsonAddContainerToList(&setList);
     }
 }
 
 void jsonFeatureHeatmapLay(struct json *js, struct heatmapLay *hl)
 /* JSON return string for feature panel layout info */
 {
 if (!hl)
     return;
 
 jsonAddInt(js, "sampleHeight", hl->sampleHeight);
 struct json *new, *list = jsonAddContainerList(js, hl->name);
 new = list;
 
 struct hmElement *hEl;
 for (hEl = hl->elements; hEl; hEl = hEl->next)
     {
     jsonAddString(new, "name", hEl->name);
     jsonAddInt(new, "pixelStart", hEl->pixelStart);
     jsonAddInt(new, "pixelEnd", hEl->pixelEnd);
     
     if (hEl->next)
 	new = jsonAddContainerToList(&list);
     }
 }
 
 void jsonSampleList(struct json *js, struct genoHeatmap *gh)
 /* JSON return string for sample list */
 {
 if (!gh)
     return;
 
 jsonAddSlName(js, "samples", gh->sampleList);
 }
 
 /********************** END JSON *******************************/
 
 void adjustStats(struct genoHeatmap *gh)
 {
 if (!gh)
     return;
 
 char *correction = cartOptionalString(cart, hgh2StatsCorrection);
 if (!correction)
     return;
 
 if (sameString(correction, "bonferroni"))
     adjustBonferroni(gh);
 else if (sameString(correction, "fdr"))
     return;
 }
 
 
 
 /********************** Begin Drawing dispatchers **************/
 
 void drawGenomeStats(struct genoHeatmap *gh, char *statsFxn, char *chromStart, 
 		      char *chromEnd, int subsetNum, int width, int sampleHeight,
 		      char **statsGif, char **scaleGif)
 {
 boolean (*func)(float data1[], unsigned long n1, float data2[], 
 		unsigned long n2, float *r, float *prob) = NULL;
 
 float colorCutoff = 0.0;
 getStatsFxn(statsFxn, &func, &colorCutoff);
 
 char *startChrom = NULL;
 int startBase = -1;
 char *endChrom = NULL;
 int endBase = -1;
 
 parseChromStrings(chromStart, &startChrom, &startBase, 
 		  chromEnd, &endChrom, &endBase);
 
 struct sqlConnection *conn = hAllocConnProfile(heatMapDbProfile, gh->database); 
 struct heatmapLay *hl = chromLayout(conn, gh, startChrom, startBase, 
 				    endChrom, endBase, width, sampleHeight);
  
 if (differentWord(gh->dataType,"bed 15") || !gotAdvFilter(gh->patDb) || !func)
     return;
 
 //get the analysis results
 char *raName= gh->raFile;
 
 boolean useAccessTable = TRUE;
 char *chromName = NULL;
 
 if (startChrom && endChrom)
     if (sameString(startChrom, endChrom))
 	{
 	useAccessTable = FALSE;
 	chromName = startChrom;
 	}
 
 gh->anaResult = diffAveSubgroup(gh, subsetNum, raName, func, chromName, useAccessTable);
 adjustStats(gh);
 
 char *filename = genomeStatsGif(conn, hl, gh, colorCutoff);
 if (filename)
     *statsGif = cloneString(filename);
 
 filename = simpleScaleGif(hl, gh, 1, 1);
 if (filename)
     *scaleGif = cloneString(filename);
 
 hFreeConn(&conn);
 }
 
 void drawGenesetStats(struct genoHeatmap *gh, char *statsFxn, char *geneSetNames, 
 		      char *userGeneset, int subsetNum, int width, int sampleHeight,
 		      char **statsGif, char **scaleGif)
 {
 if (!geneSetNames)
     return;
 
 boolean (*func)(float data1[], unsigned long n1, float data2[], 
 		unsigned long n2, float *r, float *prob) = NULL;
 
 float colorCutoff = 0.0;
 getStatsFxn(statsFxn, &func, &colorCutoff);
 
 if (differentWord(gh->dataType, "bed 15") || !gotAdvFilter(gh->patDb) || !func)
     return;
 
 char *raName= gh->raFile;
 
 struct sqlConnection *conn = hAllocConnProfile(heatMapDbProfile, gh->database);
 struct heatmapLay *hl = genesetLayout(gh, geneSetNames, width, sampleHeight);
 if (!hl)
     return;
 struct geneSet *geneSets = getAllPathways(cart, getPathwayDb(), geneSetNames);
 
 gh->anaResultHash = diffSubgroupHash(gh, subsetNum, raName, geneSets, NULL, func);
 adjustStats(gh);
 
 char *filename = genesetStatsGif(conn, hl, gh, colorCutoff);
 if (filename)
     *statsGif = cloneString(filename);
 
 filename = simpleScaleGif(hl, gh, 1, 1);
 if (filename)
     *scaleGif = cloneString(filename);
 
 hFreeConn(&conn);
 
 }
 
 void drawGenomeSummary(struct json *js, struct genoHeatmap *gh, char *chromStart, 
 		       char *chromEnd, int width, int sampleHeight)
 {
 if (!gh)
     return;
 
 char *startChrom = NULL;
 int startBase = -1;
 char *endChrom = NULL;
 int endBase = -1;
 
 parseChromStrings(chromStart, &startChrom, &startBase, 
 		  chromEnd, &endChrom, &endBase);
 
 struct sqlConnection *conn = hAllocConnProfile(heatMapDbProfile, gh->database);
 struct heatmapLay *hl = chromLayout(conn, gh, startChrom, startBase, 
 				    endChrom, endBase, width, sampleHeight);
 setUserDefinedColor(hl);
 
 char *raName = gh->raFile;
 int subsetNum = cartUsualInt(cart, hgh2SubgroupNum, hgh2SubgroupDefaultNum);
 
 jsonChromHeatmapLay(js, hl);
 
 int totalHeight = heatmapHeight(gh) * hl->sampleHeight;
 
 /* This is the key function to call for subgrouping */
 struct slName **ptSubsets = NULL;
 int ifSubsets = getSubsetsIfAny(gh, subsetNum, raName, &ptSubsets);
 if (!ifSubsets)
     {
     char *gGif = genomeSummaryGif(conn, hl, gh, gh->sampleList, totalHeight, 1, 1);
     jsonAddString(js, "heatmap", gGif);
     
     char *sGif = simpleScaleGif(hl, gh, 1, 1);
     jsonAddString(js, "scale", sGif);
     }
 else
     {
     if (!ptSubsets)
 	errAbort("ptSubsets == NULL");
     int i;
 
     int height =totalHeight/ ifSubsets;
     for (i = 0; i < subsetNum; i++)
 	{
 	if (!ptSubsets[i])
 	    continue;
 
 	heatmapLayResetMinMax(hl);
 
 	char *gGif = genomeSummaryGif(conn, hl, gh, ptSubsets[i], height, i, subsetNum);
 	char heatStr[128];
 	safef(heatStr, sizeof(heatStr), "heatmap%d", i+1);
 	jsonAddString(js, heatStr, gGif);
 
 	char *sGif = simpleScaleGif(hl, gh, i, subsetNum);
 	char scaleStr[128];
 	safef(scaleStr, sizeof(scaleStr), "scale%d", i+1);
 	jsonAddString(js, scaleStr, sGif);
 	}
     }
     
 hFreeConn(&conn);
 }
 
 void drawGenesetSummary(struct json *js, struct genoHeatmap *gh, char *geneSetNames, 
 			char *userGeneset, int width, int height)
 {
 if (!gh || !geneSetNames)
     return;
 
 struct sqlConnection *conn = hAllocConnProfile(heatMapDbProfile, gh->database);
 
 struct heatmapLay *hl = genesetLayout(gh, geneSetNames, width, height);
 if (!hl)
     return;
 setUserDefinedColor(hl);
 
 jsonGenesetLayout(js, hl);
 
 char *raName = gh->raFile;
 int subsetNum = cartUsualInt(cart, hgh2SubgroupNum, hgh2SubgroupDefaultNum);
 struct slName **ptSubsets = NULL;
 int ifSubsets = getSubsetsIfAny(gh, subsetNum, raName, &ptSubsets);
 
 int totalHeight = heatmapHeight(gh) * hl->sampleHeight;
 if (!ifSubsets)
     {
     char *gGif = genesetSummaryGif(conn, hl, gh, gh->sampleList, totalHeight, 1, 1);
     jsonAddString(js, "heatmap", gGif);
 
     char *sGif = simpleScaleGif(hl, gh, 1, 1);
     jsonAddString(js, "scale", sGif); 
     }
 else
     {
     if (!ptSubsets)
 	errAbort("ptSubsets == NULL");
     
     int i;
     for (i = 0; i < subsetNum; i++)
 	{
 	if (!ptSubsets[i])
 	    continue;
 	heatmapLayResetMinMax(hl);
 
 	char *gGif = genesetSummaryGif(conn, hl, gh, ptSubsets[i], height, i, subsetNum);
 	char heatStr[128];
 	safef(heatStr, sizeof(heatStr), "heatmap%d", i+1);
 	jsonAddString(js, heatStr, gGif);
 
 	char *sGif = simpleScaleGif(hl, gh, i, subsetNum);
 	char scaleStr[128];
 	safef(scaleStr, sizeof(scaleStr), "scale%d", i+1);
 	jsonAddString(js, scaleStr, sGif);
 	}
     }
 
 hFreeConn(&conn);
 }
 
 void drawGenomeHeatmap(struct json *js, struct genoHeatmap *gh, char *chromStart, 
 		       char *chromEnd, int width, int sampleHeight)
 {
 if (!gh)
     return;
 
 char *startChrom = NULL;
 int startBase = -1;
 char *endChrom = NULL;
 int endBase = -1;
 
 parseChromStrings(chromStart, &startChrom, &startBase,
                   chromEnd, &endChrom, &endBase); 
 
 struct sqlConnection *conn = hAllocConnProfile(heatMapDbProfile, gh->database);
 struct heatmapLay *hl = chromLayout(conn, gh, startChrom, startBase, 
 				    endChrom, endBase, width, sampleHeight);
 setUserDefinedColor(hl);
 
 char *gGif = genomeGif(conn, hl, gh);
 jsonAddString(js, "heatmap", gGif);
 jsonChromHeatmapLay(js, hl);
 
 hFreeConn(&conn);
 }
 
 void drawGenomeLabels(struct json *js, char *chromStart, char *chromEnd, int width)
 {
 char *startChrom = NULL;
 int startBase = -1;
 char *endChrom = NULL;
 int endBase = -1;
 
 parseChromStrings(chromStart, &startChrom, &startBase,
                   chromEnd, &endChrom, &endBase); 
 
 struct sqlConnection *conn = hAllocConnProfile(heatMapDbProfile, database);
 struct heatmapLay *hl = chromLayout(conn, NULL, startChrom, startBase, 
 				    endChrom, endBase, width, 1);
 
 char *gGif = genomeLabelsGif(conn, hl);
 jsonAddString(js, "labels", gGif);
 jsonChromHeatmapLay(js, hl);
 
 hFreeConn(&conn);
 }
 
 void drawGenesetHeatmap(struct json *js, struct genoHeatmap *gh, char *geneSetNames, 
 			char *userGeneset, int width, int sampleHeight)
 {
 if (!gh || !geneSetNames)
     return;
 
 struct sqlConnection *conn = hAllocConnProfile(heatMapDbProfile, gh->database);
 
 struct heatmapLay *hl = genesetLayout(gh, geneSetNames, width, sampleHeight);
 if (!hl)
     return;
 
 setUserDefinedColor(hl);
 
 char *gGif = genesetGif(conn, hl, gh);
 jsonAddString(js, "heatmap", gGif);
 jsonGenesetLayout(js, hl);
 
 hFreeConn(&conn);
 }
 
 
 void drawGenesetsLabels(struct json *js, char *geneSetNames, char *userGeneset, int width)
 {
 if (!geneSetNames)
     return;
 
 struct sqlConnection *conn = hAllocConnProfile(heatMapDbProfile, database);
 
 struct heatmapLay *hl = genesetLayout(NULL, geneSetNames, width, 1);
 if (!hl)
     return;
 
 char *gGif = genesetLabelsGif(conn, hl);
 jsonAddString(js, "labels", gGif);
 jsonGenesetLayout(js, hl);
 
 hFreeConn(&conn);
 }
 
 void drawAnnotation(struct json *js, char *table, char *chromStart, char *chromEnd, int width)
 {
 if (!table)
     return;
 
 char *startChrom = NULL;
 int startBase = -1;
 char *endChrom = NULL;
 int endBase = -1;
 
 int sampleHeight = 0;
 parseChromStrings(chromStart, &startChrom, &startBase,
                   chromEnd, &endChrom, &endBase); 
 
 struct sqlConnection *conn = hAllocConnProfile(heatMapDbProfile, database);
 struct heatmapLay *hl = chromLayout(conn, NULL, startChrom, startBase, 
 				    endChrom, endBase, width, sampleHeight);
 hFreeConn(&conn);
 
 char *aGif = annotationGif(hl, table);
 jsonAddString(js, "annotation", aGif);
 jsonChromHeatmapLay(js, hl);
 }
 
 void drawFeatureHeatmap(struct json *js, struct genoHeatmap *gh, int width, int sampleHeight)
 {
 if (!gh)
     return;
 
 if (!gh->patDb)
     return;
 
 struct sqlConnection *conn = hAllocConnProfile(heatMapDbProfile, gh->database);
 struct heatmapLay *hl = featureLayout(gh, width, sampleHeight);
 
 char *fGif = featureGif(conn, hl, gh, gh->name);
 
 struct slName *sl;
 if (fGif)
     sl = slNameNew(fGif);
 else
     sl = slNameNew("");
 
 jsonAddSlName(js, "feature", sl);
 jsonFeatureHeatmapLay(js, hl);
 
 hFreeConn(&conn);
 }
 
 void addPixelOffsetHeatmapLay(struct heatmapLay *hl, int pixOffset)
 {
 if (!hl)
     return;
 
 struct hmElement *hEl;
 for (hEl = hl->elements; hEl; hEl = hEl->next)
     {
     hEl->pixelStart = hEl->pixelStart + pixOffset;
     hEl->pixelEnd = hEl->pixelEnd + pixOffset;
     }
 hl->width = hl->width + pixOffset;
 }
 
 void drawFeatureSummaryHeatmap(struct json *js, struct genoHeatmap *gh, 
 			       int width, int sampleHeight)
 {
 if (!gh)
     return;
 
 if (!gh->patDb)
     return;
 
 struct sqlConnection *conn = hAllocConnProfile(heatMapDbProfile, gh->database);
 struct heatmapLay *hl = featureLayout(gh, width, sampleHeight);
 
 char *raName = gh->raFile;
 int subsetNum = cartUsualInt(cart, hgh2SubgroupNum, hgh2SubgroupDefaultNum);
 
 /* This is the key function to call for subgrouping */
 struct slName **ptSubsets = NULL;
 int ifSubsets = getSubsetsIfAny(gh, subsetNum, raName, &ptSubsets);
 if (!ifSubsets)
     {
     int buffer = 0;
     char *fGif = featureSummaryGif(conn, hl, gh, gh->name, 
 				   gh->sampleList, buffer, 0, 1);    
     struct slName *sl;
     if (fGif)
 	sl = slNameNew(fGif);
     else
 	sl = slNameNew("");
     
     jsonAddSlName(js, "feature", sl);
     }
 else
     {
     if (!ptSubsets)
 	errAbort("ptSubsets == NULL");
     
     int i, buffer = slCount(gh->sampleList) * hl->sampleHeight;
     for (i = 0; i < subsetNum; i++)
 	{	
 	if (!ptSubsets[i])
 	    continue;
 	buffer -= slCount(ptSubsets[i]) * hl->sampleHeight;
 	}
     if (buffer <= 0)
 	buffer = 1;
     
     addPixelOffsetHeatmapLay(hl, hghSubgroupDefaultPixWidth + 3);  // add 3 pixel buffer
 
     for (i = 0; i < subsetNum; i++)
 	{
 	if (!ptSubsets[i])
 	    continue;
 	char *fGif = featureSummaryGif(conn, hl, gh, gh->name, 
 				       ptSubsets[i], buffer, i, subsetNum);    
 	struct slName *sl;
 	if (fGif)
 	    sl = slNameNew(fGif);
 	else
 	    sl = slNameNew("");
 
 	char heatStr[128];
 	safef(heatStr, sizeof(heatStr), "feature%d", i);
 	jsonAddSlName(js, heatStr, sl);
 	}
     }
 
 jsonFeatureHeatmapLay(js, hl);
     
 hFreeConn(&conn);
 }
 
 int datasetCmpShortLabel(const void *va, const void *vb)
 /* Compare to sort columns based on priority. */
 {
 const struct dataset *a = *((struct dataset **)va);
 const struct dataset *b = *((struct dataset **)vb);
 return strcasecmp(a->shortLabel, b->shortLabel);
 } 
 
 struct datasetGroup *datasetGroupFind(struct datasetGroup *list, char *string)
 /* Return first element of slName list (or any other list starting
  * with next/name fields) that matches string. */
 {
 struct datasetGroup *el;
 for (el = list; el != NULL; el = el->next)
     if (sameWord(string, el->name))
         return el;
 return NULL;
 }
 
 
 /********************** END Drawing dispatchers **************/
 
 
 /********************** BEGIN hgHeatmap2 Modes ********************/
 void modeLoad()
 {
 char *key = cartOptionalString(cart, hgh2Key);
 char *settings = retrieveSettings(cart, key);
 
 if (settings)
     hPrintf("%s", settings);
 else
     hPrintf("({ })");
 }
 
 
 void modeSave()
 {
 char *vars = cartOptionalString(cart, hgh2Vars);
 if (!vars)
     return;
 int key = saveUserSettings(cart, vars);
 
 struct json *js = newJson();
 
 char keyStr[128];
 if (key >= 0)
     safef(keyStr, sizeof(keyStr), "%d", key);
 else
     safef(keyStr, sizeof(keyStr), "n/a");
 
 jsonAddString(js, "key", keyStr);
 
 hPrintf("%s", js->print(js));
 }
 
 void modeGetUserDatasets()
 {
 struct genoHeatmap *gh;
 
 struct dataset *da;
 struct datasetGroup *gr, *groups = NULL;
 
 char *datasetRaName = dataSetRaName(); 
 /* Get all private access datasets user has access to */
 struct genoHeatmap *userGhList = getUserDbHeatmaps(datasetRaName); 
 if (!userGhList)
     jsonWarnAbort("No private datasets available to user");
 
 for (gh = userGhList; gh; gh = gh->next)
     {
     char *group = gh->group;
     if (!group)
 	group = "User Datasets";  // default name.
     
     gr = datasetGroupFind(groups, group);  // For some reason, slNameFind(...) doesn't work.
     if (!gr)
 	{
 	AllocVar(gr);
 	gr->name = cloneString(group);
 	gr->datasets = NULL;
 	slAddHead(&groups, gr);
 	}
 
     AllocVar(da);
     da->name = cloneString(gh->name);
     da->shortLabel = cloneString(gh->shortLabel);
     da->longLabel = cloneString(gh->longLabel);
-    da->url = cloneString(gh->url);
+    da->local_url = cloneString(gh->local_url);
     da->expCount = gh->expCount;  
 
     slAddHead(&gr->datasets, da);
     }
 slReverse(&groups);
 
 struct json *js = newJson();
 
 for (gr = groups; gr; gr = gr->next)
     {
     slSort(&gr->datasets, datasetCmpShortLabel);
 
     struct json *set, *group = jsonAddContainerList(js, gr->name);
     set = group;
 
     for (da = gr->datasets; da; da = da->next)
 	{
 	jsonAddString(set, "name", da->name);
 	jsonAddString(set, "shortLabel", da->shortLabel);
 	jsonAddString(set, "longLabel", da->longLabel);
-	jsonAddString(set, "url", da->url);
+	jsonAddString(set, "local_url", da->local_url);
 	jsonAddInt(set, "N", da->expCount); 
 
 	if (da->next)
 	    set = jsonAddContainerToList(&group);
 	}
     }
 
 hPrintf("%s", js->print(js));
 }
 
  
 void modeLogin()
 {
 char *login = cartOptionalString(cart, hgh2UserLogin);
 char *pass = cartOptionalString(cart, hgh2UserPass);
 char *tokenIdStr = cartOptionalString(cart, hgh2UserTokenId);
 if (!login || !pass || !tokenIdStr)
     errAbort("User or password is missing");
 
 int tokenId = atoi(tokenIdStr);
 struct json *js = hgUsersLogin(cart, login, pass, tokenId);
 hPrintf("%s", js->print(js));
 }
 
 void modeGetToken()
 {
 struct json *js = hgUsersGetToken(cart);
 hPrintf("%s", js->print(js));
 }
 
 
 void modeGetDatasets()
 {
 if (!ghList)
     return;
 
 struct genoHeatmap *gh;
 struct slRef *ref;
 
 struct dataset *da;
 struct datasetGroup *gr, *groups = NULL;
 for (ref = ghList; ref; ref = ref->next)
     {
     gh = ref->val;
 
     char *group = gh->group;
     if (!group)
 	group = "Misc.";  // default name.
     
     gr = datasetGroupFind(groups, group);  // For some reason, slNameFind(...) doesn't work.
     if (!gr)
 	{
 	AllocVar(gr);
 	gr->name = cloneString(group);
 	gr->datasets = NULL;
 	slAddHead(&groups, gr); 
 	}
 
     AllocVar(da);
     da->name = cloneString(gh->name);
     da->shortLabel = cloneString(gh->shortLabel);
     da->longLabel = cloneString(gh->longLabel);
-    da->url = cloneString(gh->url);
+    da->local_url = cloneString(gh->local_url);
     da->expCount = gh->expCount;
     slAddHead(&gr->datasets, da);
     }
 slReverse(&groups);
 
 struct json *js = newJson();
 
 for (gr = groups; gr; gr = gr->next)
     {
     slSort(&gr->datasets, datasetCmpShortLabel);
 
     struct json *set, *group = jsonAddContainerList(js, gr->name);
     set = group;
 
     for (da = gr->datasets; da; da = da->next)
 	{
 	jsonAddString(set, "name", da->name);
 	jsonAddString(set, "shortLabel", da->shortLabel);
 	jsonAddString(set, "longLabel", da->longLabel);
-	jsonAddString(set, "url", da->url);
+	jsonAddString(set, "local_url", da->local_url);
 	jsonAddInt(set, "N", da->expCount); 
 
 	if (da->next)
 	    set = jsonAddContainerToList(&group);
 	}
     }
 
 hPrintf("%s", js->print(js));
 }
 
 int getSampleHeight(struct genoHeatmap *gh)
 {
 int sampleHeight = cartUsualInt(cart, hgh2SampleHeight, hgSampleHeightDefault);
 if (sampleHeight != hgSampleHeightDefault)
     return sampleHeight;
 
 int defaultHeight = heatmapHeight(gh);
 
 sampleHeight = round((double) MIN_HEATMAP_HEIGHT / (double) defaultHeight);
 
 if (sampleHeight < 1)
     sampleHeight = 1;
 
 return sampleHeight;
 }
 
 void modeGetSummary()
 {
 char *type = cartUsualString(cart, hgh2Type, "undefined");
 char *dataset = cartOptionalString(cart, hgh2Dataset);
 char *feature = cartUsualString(cart, hgh2Feature, "false");
 int width = cartUsualInt(cart, hgh2Width, hgHeatmapDefaultPixWidth);
 
 /**** START stuff common to all datasets */
 if (!ghHash || !dataset)
     return;
 
 struct hashEl *el = hashLookup(ghHash, dataset);
 if (!el)
     jsonWarnAbort("%s is not accessible", dataset);
 
 struct genoHeatmap *gh = el->val;
 
 // don't bother with sorting for summary view, just use default order.
 // For some reason, sortPersonOrder(gh) screws this up.  So don't use.
 if (sameWord(gh->dataType, "bed 15"))
     {
     defaultOrder(gh);
     setSubgroups(gh);
     }
 int sampleHeight = getSampleHeight(gh);
 
 struct json *js = newJson();
 if (sameString(type, "chrom"))
     {
     char *chromStart = cartOptionalString(cart, hgh2ChromStart);
     char *chromEnd = cartOptionalString(cart, hgh2ChromEnd);
 
     drawGenomeSummary(js, gh, chromStart, chromEnd, width, sampleHeight);
     }
 else if (sameString(type, "geneset"))
     {
     char *geneSets = cartOptionalString(cart, hgh2GeneSets);
     char *userGeneset = cartOptionalString(cart, hgh2UserGeneSet);
 
     drawGenesetSummary(js, gh, geneSets, userGeneset, width, sampleHeight);
     }
 else
     errAbort("Unhandled type %s, mode = getHeatmaps", type);
 
 setupFeatureVisibility(gh);
 //setupSort(gh);    // set any cart variables needed to perform sort.
 if (sameWord(gh->dataType, "bed 15"))
     setSubgroups(gh);
 
 if (sameString(feature, "true"))
     {
     int featureWidth = cartUsualInt(cart, hgh2FeatureWidth, hgFeatureDefaultPixWidth);   
     drawFeatureSummaryHeatmap(js, gh, featureWidth, sampleHeight);
     }
 jsonSampleList(js, gh);
 
 hPrintf("%s", js->print(js));
 }
 
 void modeGetHeatmaps()
 {
 char *type = cartUsualString(cart, hgh2Type, "undefined");
 char *dataset = cartOptionalString(cart, hgh2Dataset);
 char *feature = cartUsualString(cart, hgh2Feature, "false");
 int width = cartUsualInt(cart, hgh2Width, hgHeatmapDefaultPixWidth);
 
 /**** START stuff common to all datasets */
 if (!ghHash || !dataset)
     return;
 
 struct hashEl *el = hashLookup(ghHash, dataset);
 if (!el)
     jsonWarnAbort("%s is not accessible", dataset);
 
 struct genoHeatmap *gh = el->val;
 
 setupFeatureVisibility(gh);
 setupSort(gh);    // set any cart variables needed to perform sort.
 
 int sampleHeight = getSampleHeight(gh);
 
 /**** END common stuff */
 
 struct json *js = newJson();
 if (sameString(type, "chrom"))
     {
     char *chromStart = cartOptionalString(cart, hgh2ChromStart);
     char *chromEnd = cartOptionalString(cart, hgh2ChromEnd);
     
     drawGenomeHeatmap(js, gh, chromStart, chromEnd, width, sampleHeight);
     }
 else if (sameString(type, "geneset"))
     {
     char *geneSets = cartOptionalString(cart, hgh2GeneSets);
     char *userGeneset = cartOptionalString(cart, hgh2UserGeneSet);
 
     drawGenesetHeatmap(js, gh, geneSets, userGeneset, width, sampleHeight);
     }
 else
     errAbort("Unhandled type %s, mode = getHeatmaps", type);
 
 if (sameString(feature, "true"))
     {
     int featureWidth = cartUsualInt(cart, hgh2FeatureWidth, hgFeatureDefaultPixWidth);
    
     drawFeatureHeatmap(js, gh, featureWidth, sampleHeight);
     }
 
 jsonSampleList(js, gh);
 
 hPrintf("%s", js->print(js));
 }
 
 
 void modeGetLabels()
 {
 char *type = cartUsualString(cart, hgh2Type, "undefined");
 int width = cartUsualInt(cart, hgh2Width, hgHeatmapDefaultPixWidth);
 
 struct json *js = newJson();
 if (sameString(type, "chrom"))
     {
     char *chromStart = cartOptionalString(cart, hgh2ChromStart);
     char *chromEnd = cartOptionalString(cart, hgh2ChromEnd);
     
     drawGenomeLabels(js, chromStart, chromEnd, width);
     }
 else if (sameString(type, "geneset"))
     {
     char *geneSets = cartOptionalString(cart, hgh2GeneSets);
     char *userGeneset = cartOptionalString(cart, hgh2UserGeneSet);
 
     drawGenesetsLabels(js, geneSets, userGeneset, width);
     }
 else
     errAbort("Unhandled type %s, mode = getLabels", type);
 
 hPrintf("%s", js->print(js));
 }
 
 void modeGetAnnotation()
 {
 char *table = "refGene";
 char *type = cartUsualString(cart, hgh2Type, "undefined");
 int width = cartUsualInt(cart, hgh2Width, hgHeatmapDefaultPixWidth);
 
 struct json *js = newJson();
 if (sameString(type, "chrom"))
     {
     char *chromStart = cartOptionalString(cart, hgh2ChromStart);
     char *chromEnd = cartOptionalString(cart, hgh2ChromEnd);
     
     drawAnnotation(js, table, chromStart, chromEnd, width);
     }
 else
     errAbort("Unhandled type %s, mode = getAnnotation", type);
 
 hPrintf("%s", js->print(js));
 }
 
 void modeGetFeatures()
 /* get a list of available features for dataset */
 {
 char *dataset = cartOptionalString(cart, hgh2Dataset);
 
 /**** START stuff common to all datasets */
 if (!ghHash || !dataset)
     return;
 
 struct hashEl *el = hashLookup(ghHash, dataset);
 if (!el)
     jsonWarnAbort("%s is not accessible", dataset);
 
 struct genoHeatmap *gh = el->val;
 
 setupFeatureVisibility(gh);
 /**** END common */
 
 struct json *js = newJson();
 jsonFeatureList(js, gh);
 
 hPrintf("%s", js->print(js));
 }
 
 void modeUpdateFeatures()
 /* updates features, assumes heatmap has not changed */
 {
 char *dataset = cartOptionalString(cart, hgh2Dataset);
 int featureWidth = cartUsualInt(cart, hgh2FeatureWidth, hgFeatureDefaultPixWidth);
 
 /**** START stuff common to all datasets */
 if (!ghHash || !dataset)
     return;
 
 struct hashEl *el = hashLookup(ghHash, dataset);
 if (!el)
     jsonWarnAbort("%s is not accessible", dataset);
 
 struct genoHeatmap *gh = el->val;
 
 setupFeatureVisibility(gh);
 setupSort(gh);    // set any cart variables needed to perform sort.
 
 /**** END common stuff */
 
 int sampleHeight = getSampleHeight(gh);
 
 struct json *js = newJson();
 drawFeatureHeatmap(js, gh, featureWidth, sampleHeight);
 
 hPrintf("%s", js->print(js));
 }
 
 
 void modeUpdateFeatureSummary()
 /* updates features, assumes heatmap has not changed */
 {
 char *dataset = cartOptionalString(cart, hgh2Dataset);
 int featureWidth = cartUsualInt(cart, hgh2FeatureWidth, hgFeatureDefaultPixWidth);
 
 /**** START stuff common to all datasets */
 if (!ghHash || !dataset)
     return;
 
 struct hashEl *el = hashLookup(ghHash, dataset);
 if (!el)
     jsonWarnAbort("%s is not accessible", dataset);
 
 struct genoHeatmap *gh = el->val;
 
 setupFeatureVisibility(gh);
 setupSort(gh);    // set any cart variables needed to perform sort.
 setSubgroups(gh);
 
 /**** END common stuff */
 
 int sampleHeight = getSampleHeight(gh);
 
 struct json *js = newJson();
 drawFeatureSummaryHeatmap(js, gh, featureWidth, sampleHeight);
 
 hPrintf("%s", js->print(js));
 }
 
 
 void modeCheckGeneList()
 /* Search genesets with a query for geneset name or containing gene */
 {
 char *searchGenes = cartOptionalString(cart, hgh2MatchingGenes);
 searchGenes = cleanUpMemberStr(replaceChars(replaceChars(searchGenes, "\n", ",")," ",","));  
     
 struct slName *valList = getGenesMatchingList(searchGenes);
 struct slName *errList = getGenesNotMatching(searchGenes);
 
 struct json *js = newJson();
 jsonAddSlName(js, "validated", valList);
 jsonAddSlName(js, "error", errList);
 
 /* output JSON string */
 hPrintf("%s", js->print(js));
 }
 
 void modeGetGenes()
 /* Search genesets with a query for geneset name or containing gene */
 {
 char *searchGenes = cartOptionalString(cart, hgh2MatchingGenes);
 struct slName *slList = getGenesMatching(searchGenes);
 
 struct json *js = newJson();
 
 if (slCount(slList) > MAX_NUM_RESPONSE)
     {
     struct slName *sl = slElementFromIx(slList, MAX_NUM_RESPONSE);
     if (sl)
 	sl->next = NULL;
     }
 
 jsonAddSlName(js, "genes", slList);
 
 /* output JSON string */
 hPrintf("%s", js->print(js));
 }
 
 void modeGetGenesets()
 /* Search genesets with a query for geneset name or containing gene */
 {
 char *searchGenes = cartOptionalString(cart, hgh2MatchingGenes);
 char *searchGenesets = cartOptionalString(cart, hgh2MatchingGenesets);
 
 /* Searches both user genesets and genesets in db */
 struct slName *slList = getGenesetsMatching(cart, searchGenes, searchGenesets);
 
 struct json *js = newJson();
 
 if (slCount(slList) > MAX_NUM_RESPONSE)
     {
     struct slName *sl = slElementFromIx(slList, MAX_NUM_RESPONSE);
     if (sl)
 	sl->next = NULL;
     }
 jsonAddSlName(js, "geneSets", slList);
 
 /* output JSON string */
 hPrintf("%s", js->print(js));
 }
 
 void modeGetGenesInGenesets()
 /* Search genesets with a query for geneset name or containing gene */
 {
 char *genesets = cartOptionalString(cart, hgh2GeneSets);
 if (!genesets)
     return;
 
 struct slName *sl, *slList = slNameListFromComma(genesets); 
 
 struct json *js = newJson();
 
 for (sl = slList; sl; sl = sl->next)
     {
     struct slName *genes = getGenesInGeneset(cart, sl->name);
     char *desc = getDescriptionOfGeneset(sl->name);
     if (desc)
 	jsonAddSlName(js, desc, genes);
     else
 	jsonAddSlName(js, sl->name, genes);
     }
 
 /* output JSON string */
 hPrintf("%s", js->print(js));
 }
 
 void modeStoreUserGeneset()
 {
 char *name = cartOptionalString(cart, hgh2UserGenesetName);
 char *members = cartOptionalString(cart, hgh2UserGenesetMembers);
 
 
 if (!name || !members)
     return;
 
 char *genesetName = storeUserGeneset(cart, name, members);
 
 struct json *js = newJson();
 jsonAddString(js, "genesetSaved", genesetName);
 
 hPrintf("%s", js->print(js));
 }
 
 
 void modeSubgroup()
 {
 char *dataset = cartOptionalString(cart, hgh2Dataset);
 if (!dataset)
     return;
 
 /**** START stuff common to all datasets */
 struct hashEl *el = hashLookup(ghHash, dataset);
 if (!el)
     jsonWarnAbort("%s is not accessible", dataset);
 
 struct genoHeatmap *gh = el->val;
 
 setupFeatureVisibility(gh);
 setupSort(gh);    // set any cart variables needed to perform sort.
 
 /**** END common stuff */
 
 
 setSubgroups(gh);
 
 int sampleHeight = getSampleHeight(gh);
 int subgroupWidth = hghSubgroupDefaultPixWidth;
 char *sGif = subgroupGif(gh, gh->name, subgroupWidth, sampleHeight);
 
 struct json *js = newJson();
 jsonAddString(js, "subgroupImg", sGif);
 
 hPrintf("%s", js->print(js));
 }
 
 void modeStats()
 {
 char *dataset = cartOptionalString(cart, hgh2Dataset);
 if (!dataset)
     return;
 
 /**** START stuff common to all datasets */
 struct hashEl *el = hashLookup(ghHash, dataset);
 if (!el)
     jsonWarnAbort("%s is not accessible", dataset);
 
 struct genoHeatmap *gh = el->val;
 
 setupFeatureVisibility(gh);
 setupSort(gh);    // set any cart variables needed to perform sort.
 
 /**** END common stuff */
 
 setSubgroups(gh);
 
 char *statsFxn = cartOptionalString(cart, hgh2StatsFxn);
 char *type = cartOptionalString(cart, hgh2Type);
 
 if (!statsFxn || !type)
     return;
 
 int sampleHeight = getSampleHeight(gh);
 int width = cartUsualInt(cart, hgh2Width, hgHeatmapDefaultPixWidth);
 int subsetNum = cartUsualInt(cart, hgh2SubgroupNum, hgh2SubgroupDefaultNum);
 
 char *statsGif = NULL;       /* JSON formatted response string */
 char *scaleGif = NULL;
 if (sameString(type, "chrom"))
     {
     char *chromStart = cartOptionalString(cart, hgh2ChromStart);
     char *chromEnd = cartOptionalString(cart, hgh2ChromEnd);
     
     drawGenomeStats(gh, statsFxn, chromStart, chromEnd, 
 		    subsetNum, width, sampleHeight, &statsGif, &scaleGif);
     }
 else if (sameString(type, "geneset"))
     {
     char *geneSets = cartOptionalString(cart, hgh2GeneSets);
     char *userGeneset = cartOptionalString(cart, hgh2UserGeneSet);
 
     drawGenesetStats(gh, statsFxn, geneSets, userGeneset, 
 		     subsetNum, width, sampleHeight, &statsGif, &scaleGif);
     }
 else
     errAbort("Unhandled type %s, mode = getHeatmaps", type);
 
 struct json *js = newJson();
 jsonAddString(js, "statsImg", statsGif);
 jsonAddString(js, "scale", scaleGif);
 
 hPrintf("%s", js->print(js));
 }
 
 void modeGetScale()
 {
 int width = cartUsualInt(cart, hgh2Width, hgHeatmapDefaultPixWidth);
 
 char *chromStart = cartOptionalString(cart, hgh2ChromStart);
 char *chromEnd = cartOptionalString(cart, hgh2ChromEnd);
 
 char *startChrom = NULL;
 int startBase = -1;
 char *endChrom = NULL;
 int endBase = -1;
 
 parseChromStrings(chromStart, &startChrom, &startBase,
                   chromEnd, &endChrom, &endBase); 
 
 struct sqlConnection *conn = hAllocConnProfile(heatMapDbProfile, database);
 struct genoLayChrom *chrom, *allChromList = genoLayDbChroms(conn, FALSE);
 
 if (!allChromList)
     return;
 
 if (!startChrom)
     startChrom = cloneString(allChromList->fullName);
 if (!endChrom)
     for (chrom = allChromList; chrom; chrom = chrom->next)
         if (!chrom->next)
             endChrom = cloneString(chrom->fullName);
 
 int keepChroms = 0;
 double totalBases = 0.0;
 for (chrom = allChromList; chrom != NULL; chrom = chrom->next)
     { 
     if (sameString(chrom->fullName, startChrom))
         keepChroms = 1;
     
     if (keepChroms)
         {
         int baseStart = -1;
         int baseEnd = -1;
         if (sameString(chrom->fullName, startChrom) && (startBase != -1))
             baseStart = startBase;
 
         if (sameString(chrom->fullName, endChrom) && (endBase != -1))
             {
             if (endBase > chrom->size)
                 endBase = chrom->size;
             baseEnd = endBase + 1;
             }
 
         if (baseStart < 0)
             baseStart = 0;
         if (baseEnd < 0)
             baseEnd = chrom->size;
 
         totalBases += baseEnd - baseStart;
         }
     
     if (sameString(chrom->fullName, endChrom))
         keepChroms = 0;
     }
 
 struct heatmapLay *hl = chromLayout(conn, NULL, startChrom, startBase,
 				    endChrom, endBase, width, 1);
 hFreeConn(&conn); 
 
 char *scaleGif = genomeScaleGif(hl, totalBases);
 
 struct json *js = newJson();
 jsonAddString(js, "scaleImg", scaleGif);
 
 hPrintf("%s", js->print(js));
 }
 
 
 void toolTipQuery()
 {
 char *dataset = cartOptionalString(cart, hgh2Dataset);
 
 char *sampleId = cartOptionalString(cart, hgh2Sample);
 char *probeId = cartOptionalString(cart, hgh2Probe);
 char *feature = cartOptionalString(cart, hgh2Feature);
 
 char *chromStart = cartOptionalString(cart, hgh2ChromStart);
 char *chromEnd = cartOptionalString(cart, hgh2ChromEnd);
 
 /**** START stuff common to all datasets */
 if (!ghHash || !dataset)
     return;
 
 struct hashEl *el = hashLookup(ghHash, dataset);
 if (!el)
     return;
 struct genoHeatmap *gh = el->val;
 
 setupFeatureVisibility(gh);
 setupSort(gh);    // set any cart variables needed to perform sort.
 
 /**** END common stuff */
 
 if (!sampleId)
     jsonWarnAbort("Tooltip query requires sample id.");
 
 char *val = NULL;
 if (probeId)
     val = toolTipByProbe(gh, sampleId, probeId);
 else if (feature)
     val = toolTipByFeature(gh, sampleId, feature);
 else if (chromStart && chromEnd)
     {
     char *startChrom = NULL;
     int start = -1;
     char *stopChrom = NULL;
     int stop = -1;
    
     parseChromStrings(chromStart, &startChrom, &start,
 		      chromEnd, &stopChrom, &stop); 
 
     if (!sameString(startChrom, stopChrom))
 	jsonWarnAbort("Tooltip query across two (or more) chromosomes not allowed.");
     if (start == -1 || stop == -1)
 	jsonWarnAbort("Tooltip query start/stop value not defined.");
 
     val = toolTipByChromPos(gh, sampleId, startChrom, start, stop);
     }
 else
     jsonWarnAbort("Tooltip query ill-defined.");
 
 char *patient = NULL;
 patient= toolTipByPatient(gh, sampleId);
 
 struct json *js = newJson();
 if (val)
     jsonAddString(js, "value", val);
 else
     jsonAddString(js, "value", "N/A");
 
 if (patient)
     jsonAddString(js, "patient", patient);
 else
     jsonAddString(js, "value", "N/A");
 
 hPrintf("%s", js->print(js));
 }
 
 
 void toolTipGeneQuery()
 {
 char *dataset = cartOptionalString(cart, hgh2Dataset);
 
 char *chromStart = cartOptionalString(cart, hgh2ChromStart);
 char *chromEnd = cartOptionalString(cart, hgh2ChromEnd);
 
 /**** START stuff common to all datasets */
 if (!dataset || !chromStart || !chromEnd)
     jsonWarnAbort("Tooltip (refGene) query ill-defined.");
 
 int maxNumGenes = 10;  // Maximum # of genes to return in response
 char *startChrom = NULL;
 int start = -1;
 char *stopChrom = NULL;
 int stop = -1;
 
 parseChromStrings(chromStart, &startChrom, &start,
 		  chromEnd, &stopChrom, &stop); 
 
 if (!sameString(startChrom, stopChrom))
     jsonWarnAbort("Tooltip query across two (or more) chromosomes not allowed.");
 if (start == -1 || stop == -1)
     jsonWarnAbort("Tooltip query start/stop value not defined.");
 
 char *val = toolTipGeneTrackByChromPos(database, dataset, startChrom,
 				       start, stop, maxNumGenes);
   
 struct json *js = newJson();
 if (val)
     jsonAddString(js, "value", val);
 else
     jsonAddString(js, "value", "N/A");
 
 hPrintf("%s", js->print(js));
 }
 
 
 void modeToolTipQuery()
 {
 char *dataset = cartOptionalString(cart, hgh2Dataset);
 if (!dataset)
     return;
 
 if (sameString(dataset, "refGene"))
     toolTipGeneQuery();
 else
     toolTipQuery();
 }
 
 /* ----------------------------------------------------------- */
 
 void dispatchRoutines()
 /* Look at command variables in cart and figure out which
  * page to draw. */
 {
 /* retrieve cart variables, handle various modes */
 char *mode = cartOptionalString(cart, hgh2Mode);
 if (!mode)
     errAbort("%s is required.", hgh2Mode);
 
 if (sameString(mode, "getDatasets"))
     modeGetDatasets();
 else if (sameString(mode, "getHeatmaps"))
     modeGetHeatmaps();
 else if (sameString(mode, "getCircleMap"))
     modeGetCircleMap();
 else if (sameString(mode, "getLabels"))
     modeGetLabels();
 else if (sameString(mode, "getAnnotation"))
     modeGetAnnotation();
 else if (sameString(mode, "getSummary"))
     modeGetSummary();
 else if (sameString(mode, "getFeatures"))
     modeGetFeatures();
 else if (sameString(mode, "updateFeatures"))
     modeUpdateFeatures();
 else if (sameString(mode, "updateFeatureSummary"))
     modeUpdateFeatureSummary();
 else if (sameString(mode, "getGenesets"))
     modeGetGenesets();
 else if (sameString(mode, "getGenes"))
     modeGetGenes();
 else if (sameString(mode, "checkGeneList"))
     modeCheckGeneList();
 else if (sameString(mode, "subgroup"))
     modeSubgroup();
 else if (sameString(mode, "stats"))
     modeStats();
 else if (sameString(mode, "tooltipQuery"))
     modeToolTipQuery();
 else if (sameString(mode, "getGenesInGenesets"))
     modeGetGenesInGenesets();
 else if (sameString(mode, "storeUserGeneset"))
     modeStoreUserGeneset();
 else if (sameString(mode, "getScale"))
     modeGetScale();
 else if (sameString(mode, "save"))
     modeSave();
 else if (sameString(mode, "load"))
     modeLoad();
 else if (sameString(mode, "getToken"))
     modeGetToken();
 else if (sameString(mode, "login"))
     modeLogin();
 else if (sameString(mode, "getUserDatasets"))
     modeGetUserDatasets();
 else
     errAbort("Incorrect mode = %s", mode);
 
 cartRemovePrefix(cart, advFilterPrefix);
 cartRemovePrefix(cart, colConfigPrefix);
 cartRemovePrefix(cart, colOrderVar);
 cartRemovePrefix(cart, hghPrefix);
 cartRemovePrefix(cart, hgh2Prefix);
 }
 
 void hghDoUsualHttp()
 /* Wrap html page dispatcher with code that writes out
  * HTTP header and write cart back to database. */
 {
 cartWriteCookie(cart, hUserCookie());
 printf("Content-Type:application/x-javascript\r\n\r\n");
 
 /* Dispatch other pages, that actually want to write HTML. */
 cartWarnCatcher(dispatchRoutines, cart, jsonEarlyWarningHandler);
 cartCheckout(&cart);
 }
 
 void dispatchLocation()
 /* When this is called no output has been written at all.  We
  * look at command variables in cart and figure out if we just
  * are going write an HTTP location line, which happens when we
  * want to invoke say the genome browser or gene sorter without 
  * another intermediate page.  If we need to do more than that
  * then we call hghDoUsualHttp. */
 {
 /* set up the global database variable, currently force to be hg18, instead of
  * using getDbAndGeneome(cart, &database, &genome, oldVars). This avoids a bug 
  * when the cart is set to a different db, which occurs when a user was browsing 
  * a different genome in the general genome browser */
 database = cloneString("hg18");
 cartSetString(cart, "db", database); /* custom tracks needs this */
 
 /* set up heatmap data for all display modes*/
 theDataset = cartUsualString(cart, hghDataSet, "ALL");
 getGenoHeatmaps(theDataset);
 
 /* For other cases we want to print out some of the usual HTTP
  * lines including content-type */
 hghDoUsualHttp();
 }
 
 char *excludeVars[] = {"Submit", "submit", NULL};
 
 int main(int argc, char *argv[])
 /* Process command line. */
 {
 htmlPushEarlyHandlers();
 cgiSpoof(&argc, argv);
 htmlSetStyle(htmlStyleUndecoratedLink);
 if (argc != 1)
     usage();
 oldVars = hashNew(12);
 cart = cartForSession(hUserCookie(), excludeVars, oldVars);
 dispatchLocation();
 return 0;
 }