533112afe2a2005e80cdb1f82904ea65032d4302
braney
  Sat Oct 2 11:37:34 2021 -0700
split hg/lib into two separate libaries, one only used by the cgis

diff --git src/hg/lib/cartJson.c src/hg/lib/cartJson.c
deleted file mode 100644
index 2d504ff..0000000
--- src/hg/lib/cartJson.c
+++ /dev/null
@@ -1,898 +0,0 @@
-/* cartJson - parse and execute JSON commands to update cart and/or return cart data as JSON. */
-#include "common.h"
-#include "cartJson.h"
-#include "cartTrackDb.h"
-#include "cheapcgi.h"
-#include "errCatch.h"
-#include "grp.h"
-#include "hdb.h"
-#include "hgFind.h"
-#include "htmshell.h"
-#include "hubConnect.h"
-#include "hui.h"
-#include "jsonParse.h"
-#include "obscure.h"
-#include "regexHelper.h"
-#include "suggest.h"
-#include "trackDb.h"
-#include "trackHub.h"
-#include "web.h"
-
-char *cartJsonOptionalParam(struct hash *paramHash, char *name)
-/* Convenience function for a CartJsonHandler function: Look up name in paramHash.
- * Return the string contained in its jsonElement value, or NULL if not found. */
-{
-struct jsonElement *jel = hashFindVal(paramHash, name);
-if (jel)
-    return jsonStringVal(jel, name);
-return NULL;
-}
-
-char *cartJsonParamDefault(struct hash *paramHash, char *name, char *defaultVal)
-/* Convenience function for a CartJsonHandler function: Look up name in paramHash.
- * Return the string contained in its jsonElement value, or defaultVal if not found. */
-{
-struct jsonElement *jel = hashFindVal(paramHash, name);
-if (jel)
-    return jsonStringVal(jel, name);
-return defaultVal;
-}
-
-char *cartJsonRequiredParam(struct hash *paramHash, char *name, struct jsonWrite *jw, char *context)
-/* Convenience function for a CartJsonHandler function: Look up name in paramHash.
- * If found, return the string contained in its jsonElement value.
- * If not, write an error message (using context) and return NULL. */
-{
-char *param = cartJsonOptionalParam(paramHash, name);
-if (param == NULL)
-    jsonWriteStringf(jw, "error",
-		     "%s: required param %s is missing", context, name);
-return param;
-}
-
-static char *stripAnchor(char *textIn)
-/* If textIn contains an HTML anchor tag, strip it out (and its end tag). */
-{
-regmatch_t matches[3];
-if (regexMatchSubstrNoCase(textIn, "<a href[^>]+>", matches, ArraySize(matches)))
-    {
-    char *textOut = cloneString(textIn);
-    memmove(textOut+matches[0].rm_so, textOut+matches[0].rm_eo,
-	    strlen(textOut) + 1 - matches[0].rm_eo);
-    if (regexMatchSubstrNoCase(textOut, "</a>", matches, ArraySize(matches)))
-	memmove(textOut+matches[0].rm_so, textOut+matches[0].rm_eo,
-		strlen(textOut) + 1 - matches[0].rm_eo);
-    return textOut;
-    }
-return textIn;
-}
-
-static void hgPositionsJson(struct jsonWrite *jw, char *db, struct hgPositions *hgp, struct cart *cart)
-/* Write out JSON description of multiple position matches. */
-{
-struct hgPosTable *table;
-jsonWriteListStart(jw, "positionMatches");
-struct trackDb *tdbList = NULL;
-for (table = hgp->tableList; table != NULL; table = table->next)
-    {
-    if (table->posList != NULL)
-	{
-	char *tableName = table->name;
-	// clear the tdb cache if this track is a hub track
-	if (isHubTrack(tableName))
-	    tdbList = NULL;
-	struct trackDb *tdb = tdbForTrack(db, tableName, &tdbList);
-	if (!tdb && startsWith("all_", tableName))
-            tdb = tdbForTrack(db, tableName+strlen("all_"), &tdbList);
-        if (!tdb)
-            errAbort("no track for table \"%s\" found via a findSpec", tableName);
-	char *trackName = tdb->track;
-	jsonWriteObjectStart(jw, NULL);
-	jsonWriteString(jw, "name", table->name);
-	jsonWriteString(jw, "trackName", trackName);
-	jsonWriteString(jw, "description", table->description);
-	jsonWriteString(jw, "vis", hCarefulTrackOpenVis(db, trackName));
-	jsonWriteListStart(jw, "matches");
-	struct hgPos *pos;
-	for (pos = table->posList; pos != NULL; pos = pos->next)
-	    {
-	    char *encMatches = cgiEncode(pos->browserName);
-	    jsonWriteObjectStart(jw, NULL); // begin one match
-	    if (pos->chrom != NULL)
-		jsonWriteStringf(jw, "position", "%s:%d-%d",
-				 pos->chrom, pos->chromStart+1, pos->chromEnd);
-	    else
-		// GenBank results set position to GB accession instead of chr:s-e position.
-		jsonWriteString(jw, "position", pos->name);
-	    // this is magic to tell the browser to make the
-	    // composite and this subTrack visible
-	    if (tdb->parent)
-		{
-		if (tdbIsSuperTrackChild(tdb))
-		    jsonWriteStringf(jw, "extraSel", "%s=show&", tdb->parent->track);
-		else
-		    {
-		    // tdb is a subtrack of a composite or a view
-		    jsonWriteStringf(jw, "extraSel", "%s_sel=1&%s_sel=1&",
-				     trackName, tdb->parent->track);
-		    }
-		}
-	    jsonWriteString(jw, "hgFindMatches", encMatches);
-	    jsonWriteString(jw, "posName", htmlEncode(pos->name));
-	    if (pos->description)
-		{
-		stripString(pos->description, "\n");
-		jsonWriteString(jw, "description", stripAnchor(pos->description));
-		}
-	    jsonWriteObjectEnd(jw); // end one match
-	    }
-	jsonWriteListEnd(jw); // end matches
-	jsonWriteObjectEnd(jw); // end one table
-	}
-    }
-    jsonWriteListEnd(jw); // end positionMatches
-}
-
-static struct hgPositions *genomePosCJ(struct jsonWrite *jw,
-				       char *db, char *spec, char **retChromName,
-				       int *retWinStart, int *retWinEnd, struct cart *cart)
-/* Search for positions in genome that match user query.
- * Return an hgp unless there is a problem.  hgp->singlePos will be set if a single
- * position matched.
- * Otherwise display list of positions, put # of positions in retWinStart,
- * and return NULL. */
-{
-char *hgAppName = "cartJson";
-struct hgPositions *hgp = NULL;
-char *chrom = NULL;
-int start = BIGNUM;
-int end = 0;
-
-char *terms[16];
-int termCount = chopByChar(cloneString(spec), ';', terms, ArraySize(terms));
-boolean multiTerm = (termCount > 1);
-
-int i = 0;
-for (i = 0;  i < termCount;  i++)
-    {
-    trimSpaces(terms[i]);
-    if (isEmpty(terms[i]))
-	continue;
-    hgp = hgPositionsFind(db, terms[i], "", hgAppName, cart, multiTerm);
-    if (hgp == NULL || hgp->posCount == 0)
-	{
-	jsonWriteStringf(jw, "error",
-			 "Sorry, couldn't locate %s in %s %s", htmlEncode(terms[i]),
-                         trackHubSkipHubName(hOrganism(db)), hFreezeDate(db));
-	if (multiTerm)
-	    jsonWriteStringf(jw, "error",
-			     "%s not uniquely determined -- can't do multi-position search.",
-			     terms[i]);
-	*retWinStart = 0;
-	return NULL;
-	}
-    if (hgp->singlePos != NULL)
-	{
-	if (chrom != NULL && !sameString(chrom, hgp->singlePos->chrom))
-	    {
-	    jsonWriteStringf(jw, "error",
-			     "Sites occur on different chromosomes: %s, %s.",
-			     chrom, hgp->singlePos->chrom);
-	    return NULL;
-	    }
-	chrom = hgp->singlePos->chrom;
-	if (hgp->singlePos->chromStart < start)
-	    start = hgp->singlePos->chromStart;
-	if (hgp->singlePos->chromEnd > end)
-	    end = hgp->singlePos->chromEnd;
-	}
-    else
-	{
-	hgPositionsJson(jw, db, hgp, cart);
-	if (multiTerm && hgp->posCount != 1)
-	    {
-	    jsonWriteStringf(jw, "error",
-			     "%s not uniquely determined (%d locations) -- "
-			     "can't do multi-position search.",
-			     terms[i], hgp->posCount);
-	    return NULL;
-	    }
-	break;
-	}
-    }
-if (hgp != NULL)
-    {
-    *retChromName = chrom;
-    *retWinStart  = start;
-    *retWinEnd    = end;
-    }
-return hgp;
-}
-
-static void changePosition(struct cartJson *cj, char *newPosition)
-/* Update position in cart, after performing lookup if necessary.
- * Usually we don't report what we just changed, but since we might modify it,
- * print out the final value. */
-{
-char *db = cartString(cj->cart, "db");
-char *chrom = NULL;
-int start=0, end=0;
-struct hgPositions *hgp = genomePosCJ(cj->jw, db, newPosition, &chrom, &start, &end, cj->cart);
-// If it resolved to a single position, update the cart; otherwise the app can
-// present the error (or list of matches) to the user.
-if (hgp && hgp->singlePos)
-    {
-    char newPosBuf[128];
-    safef(newPosBuf, sizeof(newPosBuf), "%s:%d-%d", chrom, start+1, end);
-    cartSetString(cj->cart, "position", newPosBuf);
-    jsonWriteString(cj->jw, "position", newPosBuf);
-    }
-else
-    // Search failed; restore position from cart
-    jsonWriteString(cj->jw, "position", cartUsualString(cj->cart, "position", hDefaultPos(db)));
-}
-
-static void changePositionHandler(struct cartJson *cj, struct hash *paramHash)
-/* Update position in cart, after performing lookup if necessary.
- * Usually we don't report what we just changed, but since we might modify it,
- * print out the final value. */
-{
-char *newPosition = cartJsonRequiredParam(paramHash, "newValue", cj->jw, "changePosition");
-if (newPosition)
-    changePosition(cj, newPosition);
-}
-
-static void printGeneSuggestTrack(struct cartJson *cj, char *db)
-/* Get the gene track used by hgSuggest for db (defaulting to cart db), or null if
- * there is none for this assembly. */
-{
-if (isEmpty(db))
-    db = cartString(cj->cart, "db");
-char *track = NULL;
-if (! trackHubDatabase(db))
-    track = assemblyGeneSuggestTrack(db);
-jsonWriteString(cj->jw, "geneSuggestTrack", track);
-}
-
-static void getGeneSuggestTrack(struct cartJson *cj, struct hash *paramHash)
-/* Get the gene track used by hgSuggest for db (defaulting to cart db), or null if
- * there is none for this assembly. */
-{
-char *db = cartJsonOptionalParam(paramHash, "db");
-printGeneSuggestTrack(cj, db);
-}
-
-static void getVar(struct cartJson *cj, struct hash *paramHash)
-/* Print out the requested cart var(s). varString may be a comma-separated list.
- * If a var is a list variable, prints out a list of values for that var. */
-{
-char *varString = cartJsonRequiredParam(paramHash, "var", cj->jw, "get");
-if (! varString)
-    return;
-struct slName *varList = slNameListFromComma(varString), *var;
-for (var = varList;  var != NULL;  var = var->next)
-    {
-    if (cartListVarExists(cj->cart, var->name))
-	{
-	// Use cartOptionalSlNameList and return a list:
-	struct slName *valList = cartOptionalSlNameList(cj->cart, var->name);
-	jsonWriteSlNameList(cj->jw, var->name, valList);
-	slFreeList(&valList);
-	}
-    else
-	{
-	// Regular single-value variable (or not in the cart):
-	char *val = cartOptionalString(cj->cart, var->name);
-	jsonWriteString(cj->jw, var->name, val);
-	}
-    }
-slFreeList(&varList);
-}
-
-INLINE boolean nameIsTdbField(char *name)
-/* Return TRUE if name is a tdb->{field}, e.g. "track" or "shortLabel" etc. */
-{
-static char *tdbFieldNames[] =
-    { "track", "table", "shortLabel", "longLabel", "type", "priority", "grp", "parent",
-      "subtracks", "visibility" };
-return (stringArrayIx(name, tdbFieldNames, ArraySize(tdbFieldNames)) >= 0);
-}
-
-INLINE boolean fieldOk(char *field, struct hash *fieldHash)
-/* Return TRUE if fieldHash is NULL or field exists in fieldHash. */
-{
-return (fieldHash == NULL || hashLookup(fieldHash, field));
-}
-
-static void writeTdbSimple(struct jsonWrite *jw, struct trackDb *tdb, struct hash *fieldHash)
-/* Write JSON for the non-parent/child fields of tdb */
-{
-if (fieldOk("track", fieldHash))
-    jsonWriteString(jw, "track", tdb->track);
-if (fieldOk("table", fieldHash))
-    jsonWriteString(jw, "table", tdb->table);
-if (fieldOk("shortLabel", fieldHash))
-    jsonWriteString(jw, "shortLabel", tdb->shortLabel);
-if (fieldOk("longLabel", fieldHash))
-    jsonWriteString(jw, "longLabel", tdb->longLabel);
-if (fieldOk("type", fieldHash))
-    jsonWriteString(jw, "type", tdb->type);
-if (fieldOk("priority", fieldHash))
-    jsonWriteDouble(jw, "priority", tdb->priority);
-if (fieldOk("grp", fieldHash))
-    jsonWriteString(jw, "grp", tdb->grp);
-// NOTE: if you add a new field here, then also add it to nameIsTdbField above.
-if (tdb->settingsHash)
-    {
-    struct hashEl *hel;
-    struct hashCookie cookie = hashFirst(tdb->settingsHash);
-    while ((hel = hashNext(&cookie)) != NULL)
-        {
-        if (! nameIsTdbField(hel->name) && fieldOk(hel->name, fieldHash))
-            jsonWriteString(jw, hel->name, (char *)hel->val);
-        }
-    if (fieldOk("noGenome", fieldHash))
-        {
-        if ((hel = hashLookup(tdb->settingsHash, "tableBrowser")) != NULL)
-            {
-            if (startsWithWord("noGenome", (char *)(hel->val)))
-                jsonWriteBoolean(jw, "noGenome", TRUE);
-            }
-        }
-    }
-}
-
-static int trackDbViewCmp(const void *va, const void *vb)
-/* *** I couldn't find anything like this in hgTracks or hui, but clearly views end up
- * *** being ordered alphabetically somehow.  Anyway...
- * Compare two trackDbs, first by view if both have a view setting, then the usual way
- * (by priority, then by shortLabel). */
-{
-const struct trackDb *a = *((struct trackDb **)va);
-const struct trackDb *b = *((struct trackDb **)vb);
-// The casts are necessary here unless one adds const to trackDbSetting decl
-char *viewA = trackDbSetting((struct trackDb *)a, "view");
-char *viewB = trackDbSetting((struct trackDb *)b, "view");
-int diff = 0;
-if (isNotEmpty(viewA) && isNotEmpty(viewB))
-    diff = strcmp(viewA, viewB);
-if (diff != 0)
-    return diff;
-return trackDbCmp(va, vb);
-}
-
-static struct jsonWrite *rTdbToJw(struct trackDb *tdb, struct hash *fieldHash,
-                                  struct hash *excludeTypesHash, int depth, int maxDepth)
-/* Recursively build and return a new jsonWrite object with JSON for tdb and its children,
- * or NULL if tdb or all children have been filtered out by excludeTypesHash.
- * If excludeTypesHash is non-NULL, omit any tracks/views/subtracks with type in excludeTypesHash.
- * If fieldHash is non-NULL, include only the field names indexed in fieldHash. */
-{
-if (maxDepth >= 0 && depth > maxDepth)
-    return NULL;
-boolean doSubtracks = (tdb->subtracks && fieldOk("subtracks", fieldHash));
-// If excludeTypesHash is given and tdb is a leaf track/subtrack, look up the first word
-// of tdb->type in excludeTypesHash; if found, return NULL.
-if (excludeTypesHash && !doSubtracks)
-    {
-    char typeCopy[PATH_LEN];
-    safecpy(typeCopy, sizeof(typeCopy), tdb->type);
-    if (hashLookup(excludeTypesHash, firstWordInLine(typeCopy)))
-        return NULL;
-    }
-boolean gotSomething = !doSubtracks;
-struct jsonWrite *jwNew = jsonWriteNew();
-jsonWriteObjectStart(jwNew, NULL);
-writeTdbSimple(jwNew, tdb, fieldHash);
-if (tdb->parent && fieldOk("parent", fieldHash))
-    {
-    // We can't link to an object in JSON and better not recurse here or else infinite loop.
-    if (tdbIsSuperTrackChild(tdb))
-        {
-        // Supertracks have been omitted from fullTrackList, so add the supertrack object's
-        // non-parent/child info here.
-        jsonWriteObjectStart(jwNew, "parent");
-        writeTdbSimple(jwNew, tdb->parent, fieldHash);
-        jsonWriteObjectEnd(jwNew);
-        }
-    else
-        // Just the name so we don't have infinite loops.
-        jsonWriteString(jwNew, "parent", tdb->parent->track);
-    }
-if (doSubtracks)
-    {
-    jsonWriteListStart(jwNew, "subtracks");
-    slSort(&tdb->subtracks, trackDbViewCmp);
-    struct trackDb *subTdb;
-    for (subTdb = tdb->subtracks;  subTdb != NULL;  subTdb = subTdb->next)
-        {
-        struct jsonWrite *jwSub = rTdbToJw(subTdb, fieldHash, excludeTypesHash, depth+1, maxDepth);
-        if (jwSub)
-            {
-            gotSomething = TRUE;
-            jsonWriteAppend(jwNew, NULL, jwSub);
-            jsonWriteFree(&jwSub);
-            }
-        }
-    jsonWriteListEnd(jwNew);
-    }
-jsonWriteObjectEnd(jwNew);
-if (! gotSomething)
-    // All children were excluded; clean up and null out jwNew.
-    jsonWriteFree(&jwNew);
-return jwNew;
-}
-
-static struct hash *hashFromCommaString(char *commaString)
-/* Return a hash that stores words in comma-separated string, or NULL if string is NULL or empty. */
-{
-struct hash *hash = NULL;
-if (isNotEmpty(commaString))
-    {
-    hash = hashNew(0);
-    char *words[1024];
-    int i, wordCount = chopCommas(commaString, words);
-    for (i = 0;  i < wordCount;  i++)
-        hashStoreName(hash, words[i]);
-    }
-return hash;
-}
-
-static struct hash *hashTracksByGroup(struct trackDb *trackList)
-/* Hash group names to lists of tracks in those groups; sort each list by trackDb priority. */
-{
-struct hash *hash = hashNew(0);
-struct trackDb *tdb;
-for (tdb = trackList;  tdb != NULL;  tdb = tdb->next)
-    {
-    struct hashEl *hel = hashLookup(hash, tdb->grp);
-    struct slRef *slr = slRefNew(tdb);
-    if (hel)
-	slAddHead(&(hel->val), slr);
-    else
-	hashAdd(hash, tdb->grp, slr);
-    }
-struct hashCookie cookie = hashFirst(hash);
-struct hashEl *hel;
-while ((hel = hashNext(&cookie)) != NULL)
-    slSort(&hel->val, trackDbRefCmp);
-return hash;
-}
-
-int trackDbRefCmpShortLabel(const void *va, const void *vb)
-/* Do trackDbCmpShortLabel on list of references as opposed to actual trackDbs. */
-{
-const struct slRef *aRef = *((struct slRef **)va);
-const struct slRef *bRef = *((struct slRef **)vb);
-struct trackDb *a = aRef->val, *b = bRef->val;
-return trackDbCmpShortLabel(&a, &b);
-}
-
-static struct slRef *sortedAllTracks(struct trackDb *trackList)
-/* Return an slRef list containing all tdbs in track list, sorted by shortLabel. */
-{
-struct slRef *trackRefList = NULL;
-struct trackDb *tdb;
-for (tdb = trackList;  tdb != NULL;  tdb = tdb->next)
-    slAddHead(&trackRefList, slRefNew(tdb));
-slSort(&trackRefList, trackDbRefCmpShortLabel);
-return trackRefList;
-}
-
-static boolean writeGroupedTrack(struct jsonWrite *jw, char *name, char *label,
-                                 struct hash *fieldHash, struct hash *excludeTypesHash,
-                                 int maxDepth, struct slRef *tdbRefList)
-/* If tdbRefList is empty after excluding tracks/views/subtracks whose types are
- * in excludeTypesHash, then return FALSE and write nothing.  Otherwise write a group
- * and its tracks/views/subtracks and return TRUE. */
-{
-// Make a new jsonWrite object in case this group turns out to have no children after filtering.
-struct jsonWrite *jwNew = jsonWriteNew();
-jsonWriteObjectStart(jwNew, NULL);
-jsonWriteString(jwNew, "name", name);
-jsonWriteString(jwNew, "label", label);
-jsonWriteListStart(jwNew, "tracks");
-boolean gotSomething = FALSE;
-struct slRef *tdbRef;
-for (tdbRef = tdbRefList;  tdbRef != NULL;  tdbRef = tdbRef->next)
-    {
-    struct trackDb *tdb = tdbRef->val;
-    // First see if there are any tracks to show for this group:
-    struct jsonWrite *jwTrack = rTdbToJw(tdb, fieldHash, excludeTypesHash, 1, maxDepth);
-    if (jwTrack)
-        {
-        gotSomething = TRUE;
-        jsonWriteAppend(jwNew, NULL, jwTrack);
-        jsonWriteFree(&jwTrack);
-        }
-    }
-if (gotSomething)
-    {
-    // Group has at least one track, so append it to jw.
-    jsonWriteListEnd(jwNew);
-    jsonWriteObjectEnd(jwNew);
-    jsonWriteAppend(jw, NULL, jwNew);
-    }
-jsonWriteFree(&jwNew);
-return gotSomething;
-}
-
-void cartJsonGetGroupedTrackDb(struct cartJson *cj, struct hash *paramHash)
-/* Translate trackDb list (only a subset of the fields) into JSON array of track group objects;
- * each group contains an array of track objects that may have subtracks.  Send it in a wrapper
- * object that includes the database from which it was taken; it's possible that by the time
- * this reaches the client, the user might have switched to a new db. */
-{
-struct jsonWrite *jw = cj->jw;
-struct trackDb *fullTrackList = NULL;
-struct grp *fullGroupList = NULL;
-struct errCatch *errCatch = errCatchNew();
-if (errCatchStart(errCatch))
-    {
-    cartTrackDbInit(cj->cart, &fullTrackList, &fullGroupList, /* useAccessControl=*/TRUE);
-    }
-errCatchEnd(errCatch);
-if (errCatch->gotError)
-    {
-    warn("%s", errCatch->message->string);
-    jsonWriteObjectStart(jw, "groupedTrackDb");
-    jsonWriteString(jw, "db", cartString(cj->cart, "db"));
-    jsonWriteListStart(jw, "groupedTrackDb");
-    jsonWriteListEnd(jw);
-    jsonWriteObjectEnd(jw);
-    return;
-    }
-errCatchFree(&errCatch);
-struct hash *groupedTrackRefList = hashTracksByGroup(fullTrackList);
-// If the optional param 'fields' is given, hash the field names that should be returned.
-char *fields = cartJsonOptionalParam(paramHash, "fields");
-struct hash *fieldHash = hashFromCommaString(fields);
-char *excludeTypes = cartJsonOptionalParam(paramHash, "excludeTypes");
-struct hash *excludeTypesHash = hashFromCommaString(excludeTypes);
-// Also check for optional parameter 'maxDepth':
-int maxDepth = -1;
-char *maxDepthStr = cartJsonOptionalParam(paramHash, "maxDepth");
-if (isNotEmpty(maxDepthStr))
-    maxDepth = atoi(maxDepthStr);
-jsonWriteObjectStart(jw, "groupedTrackDb");
-jsonWriteString(jw, "db", cartString(cj->cart, "db"));
-jsonWriteListStart(jw, "groupedTrackDb");
-int nonEmptyGroupCount = 0;
-struct grp *grp;
-for (grp = fullGroupList;  grp != NULL;  grp = grp->next)
-    {
-    struct slRef *tdbRefList = hashFindVal(groupedTrackRefList, grp->name);
-    if (writeGroupedTrack(jw, grp->name, grp->label, fieldHash, excludeTypesHash,
-                          maxDepth, tdbRefList))
-        {
-        nonEmptyGroupCount++;
-        }
-    }
-if (nonEmptyGroupCount == 0)
-    {
-    // Catch-all for assembly hubs that don't declare groups for their tracks: add All Tracks
-    struct slRef *allTracks = sortedAllTracks(fullTrackList);
-    (void)writeGroupedTrack(jw, "allTracks", "All Tracks", fieldHash, excludeTypesHash,
-                            maxDepth, allTracks);
-    }
-jsonWriteListEnd(jw);
-jsonWriteObjectEnd(jw);
-}
-
-static char *hAssemblyDescription(char *db)
-/* Return a string containing db's description.html, or NULL if not found. */
-//#*** LIBIFY: Code lifted from hgFind.c's hgPositionsHelpHtml.
-{
-char *htmlPath = hHtmlPath(db);
-char *htmlString = NULL;
-if (htmlPath != NULL)
-    {
-    if (fileExists(htmlPath))
-	readInGulp(htmlPath, &htmlString, NULL);
-    else if (startsWith("http://" , htmlPath) ||
-	     startsWith("https://", htmlPath) ||
-	     startsWith("ftp://"  , htmlPath))
-	{
-	struct lineFile *lf = udcWrapShortLineFile(htmlPath, NULL, 256*1024);
-	htmlString = lineFileReadAll(lf);
-	lineFileClose(&lf);
-	}
-    }
-return htmlString;
-}
-
-static void getAssemblyInfo(struct cartJson *cj, struct hash *paramHash)
-/* Return useful things from dbDb (or track hub) and assembly description html (possibly NULL).
- * If db param is NULL, use db from cart. */
-{
-char *db = cartJsonOptionalParam(paramHash, "db");
-if (db == NULL)
-    db = cartString(cj->cart, "db");
-jsonWriteString(cj->jw, "db", db);
-jsonWriteString(cj->jw, "commonName", hGenome(db));
-jsonWriteString(cj->jw, "scientificName", hScientificName(db));
-jsonWriteString(cj->jw, "dbLabel", hFreezeDate(db));
-jsonWriteString(cj->jw, "assemblyDescription", hAssemblyDescription(db));
-}
-
-static void getHasCustomTracks(struct cartJson *cj, struct hash *paramHash)
-/* Tell whether cart has custom tracks for db.  If db param is NULL, use db from cart. */
-{
-char *db = cartJsonOptionalParam(paramHash, "db");
-if (db == NULL)
-    db = cartString(cj->cart, "db");
-jsonWriteBoolean(cj->jw, "hasCustomTracks", customTracksExistDb(cj->cart, db, NULL));
-}
-
-static void getIsSpecialHost(struct cartJson *cj, struct hash *paramHash)
-/* Tell whether we're on a development host, preview, gsid etc. */
-{
-jsonWriteBoolean(cj->jw, "isPrivateHost", hIsPrivateHost());
-jsonWriteBoolean(cj->jw, "isBetaHost", hIsBetaHost());
-jsonWriteBoolean(cj->jw, "isBrowserbox", hIsBrowserbox());
-jsonWriteBoolean(cj->jw, "isPreviewHost", hIsPreviewHost());
-}
-
-static void getHasHubTable(struct cartJson *cj, struct hash *paramHash)
-/* Tell whether central database has a hub table (i.e. server can do hubs). */
-{
-jsonWriteBoolean(cj->jw, "hasHubTable", hubConnectTableExists());
-}
-
-static void setIfUnset(struct cartJson *cj, struct hash *paramHash)
-/* For each name in paramHash, if that cart variable doesn't already have a non-empty
- * value, set it to the value. */
-{
-struct hashCookie cookie = hashFirst(paramHash);
-struct hashEl *hel;
-while ((hel = hashNext(&cookie)) != NULL)
-    {
-    if (isEmpty(cartOptionalString(cj->cart, hel->name)))
-	{
-	char *val = jsonStringVal((struct jsonElement *)(hel->val), hel->name);
-	if (val)
-	    cartSetString(cj->cart, hel->name, val);
-	}
-    }
-}
-
-static void jsonWriteValueLabel(struct jsonWrite *jw, char *value, char *label)
-/* Assuming we're already in an object, write out value and label tags & strings. */
-{
-jsonWriteString(jw, "value", value);
-jsonWriteString(jw, "label", label);
-}
-
-static void printCladeOrgDbTree(struct jsonWrite *jw)
-/* Print out the tree of clades, organisms and dbs as JSON.  Each node has value and label
- * for menu options; clade nodes and org nodes also have children and default. */
-{
-jsonWriteListStart(jw, "cladeOrgDb");
-struct slPair *clade, *cladeOptions = hGetCladeOptions();
-struct dbDb *centralDbDbList = hDbDbList();
-for (clade = cladeOptions;  clade != NULL;  clade = clade->next)
-    {
-    jsonWriteObjectStart(jw, NULL);
-    jsonWriteValueLabel(jw, clade->name, clade->val);
-    jsonWriteListStart(jw, "children");
-    struct slPair *org, *orgOptions = hGetGenomeOptionsForClade(clade->name);
-    for (org = orgOptions;  org != NULL;  org = org->next)
-        {
-        jsonWriteObjectStart(jw, NULL);
-        jsonWriteValueLabel(jw, org->name, org->val);
-        jsonWriteListStart(jw, "children");
-        struct dbDb *dbDb, *dbDbList;
-        if (isHubTrack(org->name))
-            dbDbList = trackHubGetDbDbs(clade->name);
-        else
-            dbDbList = centralDbDbList;
-        for (dbDb = dbDbList;  dbDb != NULL;  dbDb = dbDb->next)
-            {
-            if (sameString(org->name, dbDb->genome))
-                {
-                jsonWriteObjectStart(jw, NULL);
-                jsonWriteValueLabel(jw, dbDb->name, dbDb->description);
-                jsonWriteString(jw, "defaultPos", dbDb->defaultPos);
-                jsonWriteObjectEnd(jw);
-                }
-            }
-        jsonWriteListEnd(jw);   // children (dbs)
-        jsonWriteString(jw, "default", trimSpaces(hDefaultDbForGenome(org->name)));
-        jsonWriteObjectEnd(jw); // org
-        }
-    jsonWriteListEnd(jw);   // children (orgs)
-    jsonWriteString(jw, "default", trimSpaces(hDefaultGenomeForClade(clade->name)));
-    jsonWriteObjectEnd(jw); // clade
-    }
-jsonWriteListEnd(jw);
-}
-
-static void getCladeOrgDbPos(struct cartJson *cj, struct hash *paramHash)
-/* Get cart's current clade, org, db, position and geneSuggest track. */
-{
-jsonWriteObjectStart(cj->jw, "cladeOrgDb");
-printCladeOrgDbTree(cj->jw);
-char *db = cartString(cj->cart, "db");
-jsonWriteString(cj->jw, "db", db);
-char *org = cartUsualString(cj->cart, "org", hGenome(db));
-jsonWriteString(cj->jw, "org", org);
-char *clade = cartUsualString(cj->cart, "clade", hClade(org));
-jsonWriteString(cj->jw, "clade", clade);
-jsonWriteObjectEnd(cj->jw);
-char *position = cartOptionalString(cj->cart, "position");
-if (isEmpty(position))
-    position = hDefaultPos(db);
-jsonWriteString(cj->jw, "position", position);
-printGeneSuggestTrack(cj, db);
-}
-
-static void getStaticHtml(struct cartJson *cj, struct hash *paramHash)
-/* Read HTML text from a relative path under browser.documentRoot and
- * write it as an encoded JSON string */
-{
-char *tag = cartJsonOptionalParam(paramHash, "tag");
-if (isEmpty(tag))
-    tag = "html";
-char *file = cartJsonRequiredParam(paramHash, "file", cj->jw, "getStaticHtml");
-char *html = hFileContentsOrWarning(file);
-jsonWriteString(cj->jw, tag, html);
-}
-
-void cartJsonRegisterHandler(struct cartJson *cj, char *command, CartJsonHandler *handler)
-/* Associate handler with command; when javascript sends command, handler will be executed. */
-{
-hashAdd(cj->handlerHash, command, handler);
-}
-
-struct cartJson *cartJsonNew(struct cart *cart)
-/* Allocate and return a cartJson object with default handlers.
- * cart must have "db" set already. */
-{
-struct cartJson *cj;
-AllocVar(cj);
-cj->handlerHash = hashNew(0);
-cj->jw = jsonWriteNew();
-cj->cart = cart;
-cartJsonRegisterHandler(cj, "getCladeOrgDbPos", getCladeOrgDbPos);
-cartJsonRegisterHandler(cj, "changePosition", changePositionHandler);
-cartJsonRegisterHandler(cj, "get", getVar);
-cartJsonRegisterHandler(cj, "getGroupedTrackDb", cartJsonGetGroupedTrackDb);
-cartJsonRegisterHandler(cj, "getAssemblyInfo", getAssemblyInfo);
-cartJsonRegisterHandler(cj, "getGeneSuggestTrack", getGeneSuggestTrack);
-cartJsonRegisterHandler(cj, "getHasCustomTracks", getHasCustomTracks);
-cartJsonRegisterHandler(cj, "getIsSpecialHost", getIsSpecialHost);
-cartJsonRegisterHandler(cj, "getHasHubTable", getHasHubTable);
-cartJsonRegisterHandler(cj, "setIfUnset", setIfUnset);
-cartJsonRegisterHandler(cj, "getStaticHtml", getStaticHtml);
-return cj;
-}
-
-void cartJsonFree(struct cartJson **pCj)
-/* Close **pCj's contents and nullify *pCj. */
-{
-if (*pCj == NULL)
-    return;
-struct cartJson *cj = *pCj;
-jsonWriteFree(&cj->jw);
-hashFree(&cj->handlerHash);
-freez(pCj);
-}
-
-static void doOneCommand(struct cartJson *cj, char *command,
-                         struct jsonElement *paramObject)
-/* Dispatch command by name, checking for required parameters. */
-{
-CartJsonHandler *handler = hashFindVal(cj->handlerHash, command);
-if (handler)
-    {
-    struct hash *paramHash = jsonObjectVal(paramObject, command);
-    handler(cj, paramHash);
-    }
-else
-    {
-    jsonWriteStringf(cj->jw, "error",
-		     "cartJson: unrecognized command '%s'\"", command);
-    return;
-    }
-}
-
-static int commandCmp(const void *pa, const void *pb)
-/* Comparison function to put "change" commands ahead of other commands. */
-{
-struct slPair *cmdA = *((struct slPair **)pa);
-struct slPair *cmdB = *((struct slPair **)pb);
-boolean aIsChange = startsWith("change", cmdA->name);
-boolean bIsChange = startsWith("change", cmdB->name);
-if (aIsChange && !bIsChange)
-    return -1;
-if (!aIsChange && bIsChange)
-    return 1;
-return strcmp(cmdA->name, cmdB->name);
-}
-
-// Accumulate warnings so they can be JSON-ified:
-static struct dyString *dyWarn = NULL;
-
-static void cartJsonVaWarn(char *format, va_list args)
-/* Save warnings for later. */
-{
-dyStringVaPrintf(dyWarn, format, args);
-}
-
-static void cartJsonPrintWarnings(struct jsonWrite *jw)
-/* If there are warnings, write them out as JSON: */
-{
-if (dyWarn && dyStringLen(dyWarn) > 0)
-    jsonWriteString(jw, "warning", dyWarn->string);
-}
-
-static void cartJsonAbort()
-/* Print whatever warnings we have accumulated and exit. */
-{
-if (dyWarn)
-    puts(dyWarn->string);
-exit(0);
-}
-
-static void cartJsonPushErrHandlers()
-/* Push warn and abort handlers for errAbort. */
-{
-if (dyWarn == NULL)
-    dyWarn = dyStringNew(0);
-else
-    dyStringClear(dyWarn);
-pushWarnHandler(cartJsonVaWarn);
-pushAbortHandler(cartJsonAbort);
-}
-
-static void cartJsonPopErrHandlers()
-/* Pop warn and abort handlers for errAbort. */
-{
-popWarnHandler();
-popAbortHandler();
-}
-
-void cartJsonExecute(struct cartJson *cj)
-/* Get commands from cgi, print Content-type, execute commands, print results as JSON. */
-{
-cartJsonPushErrHandlers();
-puts("Content-Type:text/javascript\n");
-
-// Initialize response JSON object:
-jsonWriteObjectStart(cj->jw, NULL);
-// Always send back hgsid:
-jsonWriteString(cj->jw, cartSessionVarName(), cartSessionId(cj->cart));
-
-char *commandJson = cgiOptionalString(CARTJSON_COMMAND);
-if (commandJson)
-    {
-    struct errCatch *errCatch = errCatchNew();
-    if (errCatchStart(errCatch))
-        {
-        struct jsonElement *commandObj = jsonParse(commandJson);
-        struct hash *commandHash = jsonObjectVal(commandObj, "commandObj");
-        // change* commands need to go first!  Really we need an ordered map type here...
-        // for now, just make a list and sort to put change commands at the front.
-        struct slPair *commandList = NULL, *cmd;
-        struct hashCookie cookie = hashFirst(commandHash);
-        struct hashEl *hel;
-        while ((hel = hashNext(&cookie)) != NULL)
-            slAddHead(&commandList, slPairNew(hel->name, hel->val));
-        slSort(&commandList, commandCmp);
-        for (cmd = commandList;  cmd != NULL;  cmd = cmd->next)
-            doOneCommand(cj, cmd->name, (struct jsonElement *)cmd->val);
-        }
-    errCatchEnd(errCatch);
-    if (errCatch->gotError)
-        {
-        jsonWritePopToLevel(cj->jw, 1);
-        jsonWriteString(cj->jw, "error", errCatch->message->string);
-        }
-    errCatchFree(&errCatch);
-    }
-
-cartJsonPrintWarnings(cj->jw);
-jsonWriteObjectEnd(cj->jw);
-puts(cj->jw->dy->string);
-cartJsonPopErrHandlers();
-}