6eacdcdb29afdeefa7ba7a5ec1e52e13e86680f9
hiram
  Wed Apr 10 14:45:45 2019 -0700
better layout for selection menus and now returning errors properly refs #18869

diff --git src/hg/hubApi/hubApi.c src/hg/hubApi/hubApi.c
index 40b1708..a81f253 100644
--- src/hg/hubApi/hubApi.c
+++ src/hg/hubApi/hubApi.c
@@ -27,42 +27,42 @@
 #define delayFraction	0.03
 
 /* default is to list all trackDb entries, composite containers too.
  * This option will limit to only the actual track entries with data
  */
 boolean trackLeavesOnly = FALSE;  /* set by CGI parameter 'trackLeavesOnly' */
 
 /* Global only to this one source file */
 static struct cart *cart;             /* CGI and other variables */
 static struct hash *oldVars = NULL;
 static struct hash *trackCounter = NULL;
 static long totalTracks = 0;
 static boolean measureTiming = FALSE;	/* set by CGI parameters */
 static boolean allTrackSettings = FALSE;	/* checkbox setting */
 static char **shortLabels = NULL;	/* public hub short labels in array */
-// struct hubPublic *publicHubList = NULL;
 static int publicHubCount = 0;
 static char *defaultHub = "Plants";
-// static char *defaultHubUrl = " http://genome-test.gi.ucsc.edu/~hiram/hubs/Plants/hub.txt";
 static char *defaultDb = "ce11";
 static long enteredMainTime = 0;	/* will become = clock1000() on entry */
 		/* to allow calculation of when to bail out, taking too long */
 static long timeOutSeconds = 100;
 static boolean timedOut = FALSE;
 static char *urlPrefix = "";	/* initalized to support self references */
+
+	/* supportedTypes will be initialized to a known supported set */
 static struct slName *supportedTypes = NULL;
-	/* will be initialized to a known supported set */
+
 
 static void initSupportedTypes()
 /* initalize the list of supported track types */
 {
 struct slName *el = newSlName("bed");
 slAddHead(&supportedTypes, el);
 el = newSlName("wig");
 slAddHead(&supportedTypes, el);
 el = newSlName("broadPeak");
 slAddHead(&supportedTypes, el);
 el = newSlName("narrowPeak");
 slAddHead(&supportedTypes, el);
 el = newSlName("bigBed");
 slAddHead(&supportedTypes, el);
 el = newSlName("bigWig");
@@ -217,54 +217,54 @@
 
 boolean superChild = tdbIsSuperTrackChild(tdb);
 unsigned start = chromSize / 4;
 unsigned end = start + 10000;
 if (end > chromSize)
     end = chromSize;
 char *genome = NULL;
 if (hub)
     genome = hub->genomeList->name;
 
 if (db)
     {
     if (hub)
 	{
 	char urlReference[2048];
-	safef(urlReference, sizeof(urlReference), " <a href='%s/getData/track?hubUrl=%s&amp;genome=%s&amp;track=%s&amp;chrom=%s&amp;start=%u&amp;end=%u' target=_blank>(sample getData)%s</a>\n", urlPrefix, hub->url, genome, tdb->track, chrom, start, end, errorPrint);
+	safef(urlReference, sizeof(urlReference), " <a href='%s/getData/track?hubUrl=%s&amp;genome=%s&amp;track=%s&amp;chrom=%s&amp;start=%u&amp;end=%u' target=_blank>(sample data)%s</a>\n", urlPrefix, hub->url, genome, tdb->track, chrom, start, end, errorPrint);
 
 	if (tdb->parent)
 	    hPrintf("<li><b>%s</b>: %s subtrack of parent: %s%s</li>\n", tdb->track, tdb->type, tdb->parent->track, urlReference);
 	else
 	    hPrintf("<li><b>%s</b>: %s%s</li>\n", tdb->track, tdb->type, urlReference);
 	}
     else
 	{
 	char urlReference[2048];
-	safef(urlReference, sizeof(urlReference), " <a href='%s/getData/track?db=%s&amp;chrom=%s&amp;track=%s&amp;start=%u&amp;end=%u' target=_blank>(sample getData)%s</a>\n", urlPrefix, db, chrom, tdb->track, start, end, errorPrint);
+	safef(urlReference, sizeof(urlReference), " <a href='%s/getData/track?db=%s&amp;chrom=%s&amp;track=%s&amp;start=%u&amp;end=%u' target=_blank>(sample data)%s</a>\n", urlPrefix, db, chrom, tdb->track, start, end, errorPrint);
 
 	if (superChild)
 	    hPrintf("<li><b>%s</b>: %s superTrack child of parent: %s%s</li>\n", tdb->track, tdb->type, tdb->parent->track, urlReference);
 	else if (tdb->parent)
 	    hPrintf("<li><b>%s</b>: %s subtrack of parent: %s%s</li>\n", tdb->track, tdb->type, tdb->parent->track, urlReference);
 	else
 	    hPrintf("<li><b>%s</b>: %s%s</li>\n", tdb->track, tdb->type, urlReference );
 	}
     }
 else if (hub)
     {
     char urlReference[2048];
-    safef(urlReference, sizeof(urlReference), " <a href='%s/getData/track?hubUrl=%s&amp;genome=%s&amp;track=%s&amp;chrom=%s&amp;start=%u&amp;end=%u' target=_blank>(sample getData)%s</a>\n", urlPrefix, hub->url, genome, tdb->track, chrom, start, end, errorPrint);
+    safef(urlReference, sizeof(urlReference), " <a href='%s/getData/track?hubUrl=%s&amp;genome=%s&amp;track=%s&amp;chrom=%s&amp;start=%u&amp;end=%u' target=_blank>(sample data)%s</a>\n", urlPrefix, hub->url, genome, tdb->track, chrom, start, end, errorPrint);
 
     if (tdb->parent)
 	hPrintf("<li><b>%s</b>: %s subtrack of parent: %s%s</li>\n", tdb->track, tdb->type, tdb->parent->track, urlReference);
     else
 	hPrintf("<li><b>%s</b>: %s%s</li>\n", tdb->track, tdb->type, urlReference);
     }
 else
     hPrintf("<li>%s : %s not db hub track ?</li>\n", tdb->track, tdb->type);
 }
 
 static void hubSampleUrl(struct trackHub *hub, struct trackDb *tdb,
     long chromCount, long itemCount, char *chromName, unsigned chromSize,
       char *genome, char *errorString)
 {
 unsigned start = chromSize / 4;
@@ -283,31 +283,31 @@
 char countsMessage[512];
 countsMessage[0] = 0;
 if (chromCount > 0 || itemCount > 0)
     {
     if (allowedBigBedType(tdb->type))
         safef(countsMessage, sizeof(countsMessage), " : %ld chroms : %ld item count ", chromCount, itemCount);
     else if (startsWithWord("bigWig", tdb->type))
         safef(countsMessage, sizeof(countsMessage), " : %ld chroms : %ld bases covered ", chromCount, itemCount);
     else
         safef(countsMessage, sizeof(countsMessage), " : %ld chroms : %ld count ", chromCount, itemCount);
     }
 
 if (isSupportedType(tdb->type))
     {
 	char urlReference[2048];
-	safef(urlReference, sizeof(urlReference), "<a href='%s/getData/track?hubUrl=%s&amp;genome=%s&amp;track=%s&amp;chrom=%s&amp;start=%u&amp;end=%u' target=_blank>(sample getData)%s</a>\n", urlPrefix, hub->url, genome, tdb->track, chromName, start, end, errorPrint);
+	safef(urlReference, sizeof(urlReference), "<a href='%s/getData/track?hubUrl=%s&amp;genome=%s&amp;track=%s&amp;chrom=%s&amp;start=%u&amp;end=%u' target=_blank>(sample data)%s</a>\n", urlPrefix, hub->url, genome, tdb->track, chromName, start, end, errorPrint);
 
 	if (allowedBigBedType(tdb->type))
             hPrintf("    <li><b>%s</b>: %s%s%s</li>\n", tdb->track, tdb->type, countsMessage, urlReference);
         else if (startsWithWord("bigWig", tdb->type))
             hPrintf("    <li><b>%s</b>: %s%s%s</li>\n", tdb->track, tdb->type, countsMessage, urlReference);
         else
             hPrintf("    <li><b>%s</b>: %s%s%s</li>\n", tdb->track, tdb->type, countsMessage, urlReference);
     }
 else
     {
         if (allowedBigBedType(tdb->type))
             hPrintf("    <li><b>%s</b>: %s%s</li>\n", tdb->track, tdb->type, countsMessage);
         else if (startsWithWord("bigWig", tdb->type))
             hPrintf("    <li><b>%s</b>: %s%s</li>\n", tdb->track, tdb->type, countsMessage);
         else
@@ -549,34 +549,32 @@
     for (tdbEl = tdb->subtracks; tdbEl; tdbEl = tdbEl->next)
 	{
         hPrintf("<li>subtrack: %s of parent: %s : type: '%s'</li>\n", tdbEl->track, tdbEl->parent->track, tdbEl->type);
 	hashCountTrack(tdbEl, countTracks);
 	trackSettings(tdbEl, countTracks);
 	}
     }
 hPrintf("    </ul></li>\n");
 }
 
 static void hubCountOneTdb(struct trackHub *hub, char *db, struct trackDb *tdb,
     char *bigDataIndex, struct hash *countTracks, char *chromName,
     unsigned chromSize, char *genome)
 {
 char *bigDataUrl = trackDbSetting(tdb, "bigDataUrl");
-// char *compositeTrack = trackDbSetting(tdb, "compositeTrack");
 boolean compositeContainer = tdbIsComposite(tdb);
 boolean compositeView = tdbIsCompositeView(tdb);
-// char *superTrack = trackDbSetting(tdb, "superTrack");
 boolean superChild = tdbIsSuperTrackChild(tdb);
 boolean depthSearch = cartUsualBoolean(cart, "depthSearch", FALSE);
 hashCountTrack(tdb, countTracks);
 
 long chromCount = 0;
 long itemCount = 0;
 
 struct dyString *errors = newDyString(1024);
 
 /* if given a chromSize, it belongs to a UCSC db and this is *not* an
  *   assembly hub, otherwise, look up a chrom and size in the bbi file
  */
 if (! (compositeContainer || compositeView) )
     {
     if (chromSize < 1 || depthSearch)
@@ -835,45 +833,52 @@
 /* follow the pointers from the trackHub to trackHubGenome and around
  * in a circle from one to the other to find all hub resources
  */
 {
 long totalAssemblyCount = 0;
 struct trackHubGenome *genome = hubTop->genomeList;
 
 hPrintf("<h4>genome sequences (and tracks) present in this track hub</h4>\n");
 
 if (NULL == genome)
     {
     hPrintf("<h4>odd error, can not find a gnomeList ? at url: '%s'</h4>\n", hubTop->url);
     return;
     }
 
+
+// testing /list/tracks?db=ce11
+// testing /list/tracks? hubUrl genome=_araTha1
+
 hPrintf("<ul>\n");
 long lastTime = clock1000();
 for ( ; genome; genome = genome->next )
     {
     ++totalAssemblyCount;
     if (isNotEmpty(genome->twoBitPath))
 	{
 	hPrintf("<li><b>Assembly genome</b> '%s' <b>twoBitPath</b>: '%s'</li>\n", genome->name, genome->twoBitPath);
 	char *chromName = NULL;
 	struct chromInfo *ci = trackHubAllChromInfo(genome->name);
         unsigned chromSize = largestChromInfo(ci, &chromName);
 	char sizeString[64];
 	sprintLongWithCommas(sizeString, chromSize);
 	hPrintf("<li><b>Sequence count</b> %d, <b>largest</b>: %s at %s bases</li>\n", slCount(ci), chromName, sizeString);
 	}
+    char urlReference[2048];
+    safef(urlReference, sizeof(urlReference), " <a href='%s/list/tracks?hubUrl=%s&amp;genome=%s' target=_blank>JSON output: list tracks</a>", urlPrefix, hubTop->url, genome->name);
+    hPrintf("<li>%s</li>\n", urlReference);
     hubInfo("organism", genome->organism);
     hubInfo("name", genome->name);
     hubInfo("description", genome->description);
     hubInfo("groups", genome->groups);
     hubInfo("defaultPos", genome->defaultPos);
     hubInfo("trackDbFile", genome->trackDbFile);
     hubAssemblySettings(hubTop, genome);
     if (measureTiming || debug)
 	{
 	long thisTime = clock1000();
 	hPrintf("<li><em>processing time %s: %ld millis</em></li>\n", genome->name, thisTime - lastTime);
 	hPrintf("<hr>\n");
         }
     if (timeOutReached())
 	break;
@@ -970,30 +975,35 @@
 return hel;
 }
 
 static void tracksForUcscDb(char *db)
 /* scan the specified database for all tracks */
 {
 struct hash *countTracks = hashNew(0);
 char *chromName = NULL;
 int chromCount = 0;
 unsigned chromSize = largestChrom(db, &chromName, &chromCount);
 char countString[64];
 sprintLongWithCommas(countString, chromCount);
 char sizeString[64];
 sprintLongWithCommas(sizeString, chromSize);
 hPrintf("<h4>Tracks in UCSC genome: '%s', chrom count: %s, longest chrom: %s : %s</h4>\n", db, countString, chromName, sizeString);
+
+char urlReference[2048];
+safef(urlReference, sizeof(urlReference), " <a href='%s/list/tracks?db=%s' target=_blank>JSON output: list tracks</a>", urlPrefix, db);
+hPrintf("<h4>%s</h4>\n", urlReference);
+
 struct trackDb *tdbList = obtainTdb(NULL, db);
 struct trackDb *tdb;
 hPrintf("<ul>\n");
 for (tdb = tdbList; tdb != NULL; tdb = tdb->next )
     {
     countOneTdb(db, tdb, countTracks, chromName, chromSize, NULL);
     if (timeOutReached())
 	break;
     }
 int trackCount = ptToInt(hashFindVal(countTracks, "track count"));
 /* elCount - 1 since the 'track count' element isn't a track */
 hPrintf("    <li>%d total tracks counted, %d different track types</li>\n", trackCount, countTracks->elCount - 1);
 if (countTracks->elCount)
     {
     hPrintf("        <ul>\n");
@@ -1032,81 +1042,187 @@
 	}
     }
 }
 
 static void showCartDump()
 /* for information purposes only during development, will become obsolete */
 {
 hPrintf("<h4>cart dump</h4>");
 hPrintf("<pre>\n");
 cartDump(cart);
 hPrintf("</pre>\n");
 }
 
 static void sendJsonHogMessage(char *hogHost)
 {
-apiErrAbort("Your host, %s, has been sending too many requests lately and is "
+apiErrAbort(429, "Too Many Requests", "Your host, %s, has been sending too many requests lately and is "
        "unfairly loading our site, impacting performance for other users. "
        "Please contact genome@soe.ucsc.edu to ask that your site "
        "be reenabled.  Also, please consider downloading sequence and/or "
        "annotations in bulk -- see http://genome.ucsc.edu/downloads.html.",
        hogHost);
 }
 
 static void sendHogMessage(char *hogHost)
 {
+puts("Content-Type:text/html");
+puts("Status: 429 Too Many Requests");
+puts("Retry-After: 30");
+puts("\n");
+
 hPrintf("<!DOCTYPE HTML>\n");
 hPrintf("<html lang='en'>\n");
 hPrintf("<head>\n");
 hPrintf("<meta charset=\"utf-8\">\n");
 hPrintf("<title>Status 459 Too Many Requests</title></head>\n");
 
 hPrintf("<body><h1>Status 459 Too many Requests</h1><p>\n");
 hPrintf("Your host, %s, has been sending too many requests lately and is "
        "unfairly loading our site, impacting performance for other users. "
        "Please contact genome@soe.ucsc.edu to ask that your site "
        "be reenabled.  Also, please consider downloading sequence and/or "
        "annotations in bulk -- see http://genome.ucsc.edu/downloads.html.",
        hogHost);
 hPrintf("</p></body></html>\n");
 exit(0);
 }
 
 static void hogExit()
 /* bottleneck server requests exit */
 {
 char *hogHost = getenv("REMOTE_ADDR");
 char *pathInfo = getenv("PATH_INFO");
 /* nothing on incoming path, then display the WEB page instead */
 if (sameOk("/",pathInfo))
     pathInfo = NULL;
 if (isNotEmpty(pathInfo))
     {
-    puts("Content-Type:application/json");
-    puts("Status: 459 Too Many Requests");
-    /* maybe a Retry-After: 3600 statement here ? */
-    puts("\n");
     sendJsonHogMessage(hogHost);
     }
 else
     {
-    puts("Content-Type:text/html");
-    puts("Status: 459 Too Many Requests");
-    /* maybe a Retry-After: 3600 statement here ? */
-    puts("\n");
     sendHogMessage(hogHost);
     }
+}	/*	static void hogExit()	*/
+
+/* name of button group */
+#define RADIO_GROUP	"selectRadio"
+/* button functions */
+#define RADIO_PUBHUB	"pubHub"
+#define RADIO_OTHERHUB	"otherHub"
+#define RADIO_UCSCDB	"ucscDb"
+
+static void selectionForm()
+/* setup the selection pull-downs for source */
+{
+char *hubDropDown = cartUsualString(cart, "publicHubs", defaultHub);
+char *urlDropDown = urlFromShortLabel(hubDropDown);
+char *otherHubUrl = cartUsualString(cart, "urlHub", "");
+char *ucscDb = cartUsualString(cart, "ucscGenome", defaultDb);
+
+if (isEmpty(otherHubUrl))
+    otherHubUrl = urlDropDown;
+
+char *radioOn = cartUsualString(cart, RADIO_GROUP, RADIO_PUBHUB);
+
+/* create border around table, but not inside the table with the data */
+hPrintf("<table border=4>\n");
+hPrintf("<tr><td><table border=0>\n");
+
+int maxDbNameWidth = 0;
+struct dbDb *dbList = ucscDbDb();
+char **ucscDbList = NULL;
+int listSize = slCount(dbList);
+AllocArray(ucscDbList, listSize);
+struct dbDb *el = dbList;
+int ucscDataBaseCount = 0;
+for ( ; el != NULL; el = el->next )
+    {
+    ucscDbList[ucscDataBaseCount++] = el->name;
+    if (strlen(el->name) > maxDbNameWidth)
+	maxDbNameWidth = strlen(el->name);
+    }
+maxDbNameWidth += 1;
+
+hPrintf("<form action='%s' name='hubApiUrl' id='hubApiUrl' method='GET'>\n\n", urlPrefix);
+
+hWrites("<tr><td>");
+jsMakeTrackingRadioButton(RADIO_GROUP, "typeOneJs", RADIO_PUBHUB, radioOn);
+hWrites("</td><th>");
+hWrites("Select public hub:");
+hWrites("</th><td>");
+
+#define JBUFSIZE 2048
+#define SMALLBUF 256
+char javascript[JBUFSIZE];
+struct slPair *events = NULL;
+safef(javascript, sizeof(javascript), "this.lastIndex=this.selectedIndex;");
+slPairAdd(&events, "focus", cloneString(javascript));
+
+cgiMakeDropListClassWithIdStyleAndJavascript("publicHubs", "publicHubs",
+    shortLabels, publicHubCount, hubDropDown, NULL, "width: 400px", events);
+
+jsOnEventById("change", "publicHubs", "document.getElementById('"RADIO_GROUP"_"RADIO_PUBHUB"').checked=true;");
+hWrites("</td></tr>\n");
+
+hWrites("<tr><td>");
+jsMakeTrackingRadioButton(RADIO_GROUP, "typeOneJs", RADIO_OTHERHUB, radioOn);
+hWrites("</td><th>");
+hWrites("enter a hub URL:");
+hWrites("</th><td>");
+hPrintf("<input type='text' name='urlHub' id='urlHub' size='60' value='%s'>\n", otherHubUrl);
+jsOnEventById("change", "urlHub", "document.getElementById('"RADIO_GROUP"_"RADIO_OTHERHUB"').checked=true;");
+hWrites("</td></tr>\n");
+
+hWrites("<tr><td>");
+jsMakeTrackingRadioButton(RADIO_GROUP, "typeOneJs", RADIO_UCSCDB, radioOn);
+hWrites("</td><th>");
+hWrites("select a UCSC database name:");
+hWrites("</th><td>");
+maxDbNameWidth *= 9;  // 9 should be font width here
+char widthPx[SMALLBUF];
+safef(widthPx, sizeof(widthPx), "width: %dpx", maxDbNameWidth);
+cgiMakeDropListClassWithIdStyleAndJavascript("ucscGenome", "ucscGenome",
+    ucscDbList, ucscDataBaseCount, ucscDb, NULL, widthPx, events);
+jsOnEventById("change", "ucscGenome", "document.getElementById('"RADIO_GROUP"_"RADIO_UCSCDB"').checked=true;");
+hWrites("</td></tr>\n");
+
+allTrackSettings = cartUsualBoolean(cart, "allTrackSettings", FALSE);
+hWrites("<tr><td>&nbsp;</td><th>display control:</th><td>");
+hCheckBox("allTrackSettings", allTrackSettings);
+hWrites("&nbsp;display all track settings for each track");
+hWrites("</td></tr>\n");
+
+
+trackLeavesOnly = cartUsualBoolean(cart, "trackLeavesOnly", trackLeavesOnly);
+hWrites("<tr><td>&nbsp;</td><th>JSON list output:</th><td>");
+hCheckBox("trackLeavesOnly", trackLeavesOnly);
+hWrites("&nbsp;show only data tracks, do not show composite container information");
+hWrites("</td></tr>\n");
+
+/* go button at the bottom of the table */
+hWrites("<tr><td>&nbsp;</td><td align=center>");
+hButton("sourceSelected", "go");
+hWrites("</td><td>press 'go' after selections made</td></tr>\n");
+
+hPrintf("</form>\n");
+hPrintf("</table>\n");
+hPrintf("</td></tr></table>\n");
+
+/* how does debug carry forward ? */
+// if (debug)
+//    cgiMakeHiddenVar("debug", "1");
 }
 
 static void doMiddle(struct cart *theCart)
 /* Set up globals and make web page */
 {
 cart = theCart;
 measureTiming = hPrintStatus() && isNotEmpty(cartOptionalString(cart, "measureTiming"));
 measureTiming = TRUE;
 char *database = NULL;
 char *genome = NULL;
 
 cgiVarSet("ignoreCookie", "1");
 
 getDbAndGenome(cart, &database, &genome, oldVars);
 initGenbankTableNames(database);
@@ -1139,62 +1255,40 @@
     pathInfo = NULL;
 
 boolean commandError = FALSE;
 /*expect no more than MAX_PATH_INFO number of words*/
 char *words[MAX_PATH_INFO];
 
 if (isNotEmpty(pathInfo))
     {
     setupFunctionHash();
     struct hashEl *hel = parsePathInfo(pathInfo, words);
     /* verify valid API command */
 
     if (hel)	/* have valid command */
 	{
         hPrintDisable();
-        /* could check botDelay here to see if 459 status is advised */
-	puts("Content-Type:application/json");
-	puts("\n");
         void (*apiFunction)(char **) = hel->val;
         (*apiFunction)(words);
 	return;
 	}
      else
 	commandError = TRUE;
     }
 
-/* could check botDelay here to see if 459 status is advised */
-// puts("Content-Type:text/html");
-// puts("\n");
-
 (void) hubPublicDbLoadAll();
 
-struct dbDb *dbList = ucscDbDb();
-char **ucscDbList = NULL;
-int listSize = slCount(dbList);
-AllocArray(ucscDbList, listSize);
-struct dbDb *el = dbList;
-int ucscDataBaseCount = 0;
-int maxDbNameWidth = 0;
-for ( ; el != NULL; el = el->next )
-    {
-    ucscDbList[ucscDataBaseCount++] = el->name;
-    if (strlen(el->name) > maxDbNameWidth)
-	maxDbNameWidth = strlen(el->name);
-    }
-maxDbNameWidth += 1;
-
 webStartJWest(cart, database, "UCSC JSON API interface");
 // webStartGbNoBanner(cart, database, "UCSC JSON API interface");
 // webStartGbOptionalBanner(cart, database, "UCSC JSON API interface", TRUE, FALSE);
 
 hPrintf("<div class='container-fluid gbPage'>\n");
 
 if (debug)
     {
     hPrintf("<ul>\n");
     hPrintf("<li>hgBotDelay: %d</li>\n", botDelay);
     char *envVar = getenv("BROWSER_HOST");
     hPrintf("<li>BROWSER_HOST:%s</li>\n", envVar);
     envVar = getenv("CONTEXT_DOCUMENT_ROOT");
     hPrintf("<li>CONTEXT_DOCUMENT_ROOT:%s</li>\n", envVar);
     envVar = getenv("CONTEXT_PREFIX");
@@ -1211,132 +1305,85 @@
     hPrintf("<li>SCRIPT_NAME:%s</li>\n", envVar);
     envVar = getenv("SCRIPT_URI");
     hPrintf("<li>SCRIPT_URI:%s</li>\n", envVar);
     envVar = getenv("SCRIPT_URL");
     hPrintf("<li>SCRIPT_URL:%s</li>\n", envVar);
     envVar = getenv("SERVER_NAME");
     hPrintf("<li>SERVER_NAME:%s</li>\n", envVar);
     envVar = getenv("PATH_INFO");
     if (isNotEmpty(envVar))
        hPrintf("<li>PATH_INFO:'%s'</li>\n", envVar);
     else
        hPrintf("<li>PATH_INFO:&lt;empty&gt;</li>\n");
     hPrintf("</ul>\n");
     }
 
-char *goOtherHub = cartUsualString(cart, "goOtherHub", "");
-char *goUcscDb = cartUsualString(cart, "goUcscDb", "");
 char *otherHubUrl = cartUsualString(cart, "urlHub", "");
-char *goPublicHub = cartUsualString(cart, "goPublicHub", "");
 char *hubDropDown = cartUsualString(cart, "publicHubs", defaultHub);
 char *urlDropDown = urlFromShortLabel(hubDropDown);
 char *ucscDb = cartUsualString(cart, "ucscGenome", defaultDb);
+char *selectRadio = cartUsualString(cart, RADIO_GROUP, RADIO_PUBHUB);
 char *urlInput = urlDropDown;	/* assume public hub */
 if (debug)
     {
     hPrintf("<ul>\n");
-    hPrintf("<li>goOtherHub: '%s'</li>\n", goOtherHub);
-    hPrintf("<li>goUcscDb: '%s'</li>\n", goUcscDb);
     hPrintf("<li>otherHubUrl: '%s'</li>\n", otherHubUrl);
-    hPrintf("<li>goPublicHub: '%s'</li>\n", goPublicHub);
     hPrintf("<li>hubDropDown: '%s'</li>\n", hubDropDown);
     hPrintf("<li>urlDropDown: '%s'</li>\n", urlDropDown);
     hPrintf("<li>ucscDb: '%s'</li>\n", ucscDb);
     hPrintf("<li>urlInput: '%s'</li>\n", urlInput);
     hPrintf("</ul>\n");
     }
-if (sameWord("go", goOtherHub))	/* requested other hub URL */
-    urlInput = otherHubUrl;
-else if (isEmpty(otherHubUrl))
+if (isEmpty(otherHubUrl))
     otherHubUrl = urlInput;
 
+if (sameWord(RADIO_OTHERHUB, selectRadio))	/* requested other hub URL */
+    urlInput = otherHubUrl;
+
 if (commandError)
   {
   hPrintf("<h3>ERROR: no such command: '%s/%s' for endpoint '%s'</h3>", words[0], words[1], pathInfo);
   }
 
 long lastTime = clock1000();
 struct trackHub *hub = errCatchTrackHubOpen(urlInput);
 if (measureTiming || debug)
     {
     long thisTime = clock1000();
     if (debug)
        hPrintf("<em>hub open time: %ld millis</em><br>\n", thisTime - lastTime);
     }
 
 hPrintf("<h3>Please refer to <a href='../../goldenPath/help/api.html'>API help</a> documentation for more information about the JSON data API operation.</h3>\n");
 hPrintf("<h3>See also: <a href='../../goldenPath/help/trackDb/trackDbHub.html' target=_blank>Track definition document</a> for definitions of track settings.</h3>\n");
 
 if (debug)
     showCartDump();
 
 hPrintf("<h2>Explore hub or database assemblies and tracks</h2>\n");
+hPrintf("<h3>Select one of these three sources, and display options:</h3>\n");
 
-hPrintf("<form action='%s' name='hubApiUrl' id='hubApiUrl' method='GET'>\n\n", urlPrefix);
-
-hPrintf("<b>Select public hub:&nbsp;</b>");
-#define JBUFSIZE 2048
-#define SMALLBUF 256
-char javascript[JBUFSIZE];
-struct slPair *events = NULL;
-safef(javascript, sizeof(javascript), "this.lastIndex=this.selectedIndex;");
-slPairAdd(&events, "focus", cloneString(javascript));
-
-cgiMakeDropListClassWithIdStyleAndJavascript("publicHubs", "publicHubs",
-    shortLabels, publicHubCount, hubDropDown, NULL, "width: 400px", events);
-
-hWrites("&nbsp;");
-hButton("goPublicHub", "go");
-
-hPrintf("<br>Or, enter a hub URL:&nbsp;");
-hPrintf("<input type='text' name='urlHub' id='urlHub' size='60' value='%s'>\n", otherHubUrl);
-hWrites("&nbsp;");
-hButton("goOtherHub", "go");
-
-hPrintf("<br>Or, select a UCSC database name:&nbsp;");
-maxDbNameWidth *= 9;  // 9 should be font width here
-char widthPx[SMALLBUF];
-safef(widthPx, sizeof(widthPx), "width: %dpx", maxDbNameWidth);
-cgiMakeDropListClassWithIdStyleAndJavascript("ucscGenome", "ucscGenome",
-    ucscDbList, ucscDataBaseCount, ucscDb, NULL, widthPx, events);
-hWrites("&nbsp;");
-hButton("goUcscDb", "go");
-
-// boolean depthSearch = cartUsualBoolean(cart, "depthSearch", FALSE);
-// hPrintf("<br>\n&nbsp;&nbsp;");
-// hCheckBox("depthSearch", cartUsualBoolean(cart, "depthSearch", FALSE));
-// hPrintf("&nbsp;perform full bbi file measurement : %s (will time out if taking longer than %ld seconds)<br>\n", depthSearch ? "TRUE" : "FALSE", timeOutSeconds);
-
-hPrintf("<br>\n&nbsp;&nbsp;");
-
-allTrackSettings = cartUsualBoolean(cart, "allTrackSettings", FALSE);
-hCheckBox("allTrackSettings", allTrackSettings);
-hPrintf("&nbsp;display all track settings for each track<br>\n");
-
-hPrintf("<br>\n");
-// if (debug)
-//    cgiMakeHiddenVar("debug", "1");
-hPrintf("</form>\n");
+selectionForm();
 
 hPrintf("<p>\n");
-if (sameWord("go", goUcscDb))	/* requested UCSC db track list */
+if (sameWord(RADIO_UCSCDB, selectRadio))  /* requested UCSC db track list */
     {
     tracksForUcscDb(ucscDb);
     }
 else
     {
-    hPrintf("<h3>%s url: <em>%s</em></h3>\n", sameWord("go",goPublicHub) ? "Public hub" : "Other hub", urlInput);
+    hPrintf("<h3>%s url: <em>%s</em></h3>\n", sameWord(RADIO_PUBHUB,selectRadio) ? "Public hub" : "Other hub", urlInput);
     hPrintf("<ul>\n");
     hubInfo("hub name", hub->name);
     hubInfo("short label", hub->shortLabel);
     hubInfo("long label", hub->longLabel);
     hubInfo("genomes file", hub->genomesFile);
     hubInfo("default db", hub->defaultDb);
     hubInfo("description url", hub->descriptionUrl);
     hubInfo("email", hub->email);
     if (debug)
 	{
 	hubInfo("version", hub->version);	/* UCSC internal info */
 	hubInfo("level", hub->level);		/* UCSC internal info */
 	}
     hPrintf("</ul>\n");
 
@@ -1346,31 +1393,31 @@
 if (timedOut)
     hPrintf("<h1>Reached time out %ld seconds</h1>", timeOutSeconds);
 if (measureTiming || debug)
     hPrintf("<em>Overall total time: %ld millis</em><br>\n", clock1000() - enteredMainTime);
 
 hPrintf("</p>\n");
 hPrintf("</div> <!-- end this page contents -->\n");
 
 webIncludeFile("inc/jWestFooter.html");
 webEndJWest();
 // cartWebEnd();
 }	/*	void doMiddle(struct cart *theCart)	*/
 
 /* Null terminated list of CGI Variables we don't want to save
  * permanently. */
-static char *excludeVars[] = {"Submit", "submit", "goOtherHub", "goPublicHub", "goUcscDb", "ucscGenome", "publicHubs", "clade", NULL,};
+static char *excludeVars[] = {"Submit", "submit", "sourceSelected", "selectRadio", "ucscGenome", "publicHubs", "clade", NULL,};
 
 int main(int argc, char *argv[])
 /* Process command line. */
 {
 enteredMainTime = clock1000();
 cgiSpoof(&argc, argv);
 measureTiming = TRUE;
 verboseTimeInit();
 /* similar delay system as in DAS server */
 botDelay = hgBotDelayTimeFrac(delayFraction);
 if (botDelay > 0)
     {
     if (botDelay > 2000)
         {
 	hogExit();