62222a5e02c38a059c7e13af320d894e7cef8370
braney
  Wed Aug 2 15:30:18 2017 -0700
fixes in response to code review.  Allow creation of generic views

diff --git src/hg/hgCollection/hgCollection.c src/hg/hgCollection/hgCollection.c
index ac765cf..bc69c30 100644
--- src/hg/hgCollection/hgCollection.c
+++ src/hg/hgCollection/hgCollection.c
@@ -1,83 +1,63 @@
-/* hgVai - Variant Annotation Integrator. */
+/* hgCollection - hub builder */
 
-/* Copyright (C) 2014 The Regents of the University of California 
+/* Copyright (C) 2017 The Regents of the University of California 
  * See README in this or parent directory for licensing information. */
 #include "common.h"
-#include "linefile.h"
-#include "hash.h"
-#include "options.h"
-#include "jksql.h"
-#include "htmshell.h"
-#include "web.h"
-#include "cheapcgi.h"
-#include "cart.h"
 #include "cartTrackDb.h"
-#include "genbank.h"
-#include "hgConfig.h"
-#include "hgHgvs.h"
+#include "trackHub.h"
+#include "trashDir.h"
+#include "hubConnect.h"
 #include "hui.h"
 #include "grp.h"
-#include "hCommon.h"
-#include "hgFind.h"
-#include "hPrint.h"
+#include "cheapcgi.h"
 #include "jsHelper.h"
-#include "memalloc.h"
-#include "textOut.h"
-#include "trackHub.h"
-#include "hubConnect.h"
-#include "twoBit.h"
-#include "gpFx.h"
-#include "bigGenePred.h"
-#include "udc.h"
+#include "web.h"
 #include "knetUdc.h"
-#include "md5.h"
-#include "regexHelper.h"
-#include "hAnno.h"
-#include "trashDir.h"
 #include "api.h"
+#include "genbank.h"
+#include "htmshell.h"
+#include "jsonParse.h"
 #include "customComposite.h"
 
-//#include "libifyMe.h"
-
-
 /* Global Variables */
 struct cart *cart;		/* CGI and other variables */
 struct hash *oldVars = NULL;	/* The cart before new cgi stuff added. */
 char *genome = NULL;		/* Name of genome - mouse, human, etc. */
 char *database = NULL;		/* Current genome database - hg17, mm5, etc. */
 char *regionType = NULL;	/* genome, ENCODE pilot regions, or specific position range. */
 struct grp *fullGroupList = NULL;	/* List of all groups. */
 struct trackDb *fullTrackList = NULL;	/* List of all tracks in database. */
  struct pipeline *compressPipeline = (struct pipeline *)NULL;
 
 
 // Null terminated list of CGI Variables we don't want to save permanently:
 char *excludeVars[] = {"Submit", "submit", "hgva_startQuery", NULL,};
 
 struct track
 {
 struct track *next;
 struct track *trackList;
 struct trackDb *tdb;
 char *name;
 char *shortLabel;
 char *longLabel;
 char *visibility;
 };
 
 static char *getString(char **input)
+// grab a quoted string out of text blob
 {
 char *ptr = *input;
 
 if (*ptr != '"')
     errAbort("string must start with \"");
 ptr++;
 char *ret = ptr;
 for(; *ptr != '"'; ptr++)
     ;
 *ptr = 0;
 ptr++;
 
 if (*ptr == ',')
     ptr++;
 
@@ -101,65 +81,80 @@
 
 for(;; count++)
     {
     safef(buffer, sizeof buffer, "%s%d", skipHub, count);
     if (hashLookup(nameHash, buffer) == NULL)
         {
         hashStore(nameHash, buffer);
         return cloneString(buffer);
         }
     }
 
 return NULL;
 }
 
 static boolean trackCanBeAdded(struct trackDb *tdb)
+// are we allowing this track into a custom composite
 {
 return  (tdb->subtracks == NULL) && !startsWith("wigMaf",tdb->type) &&  (startsWith("wig",tdb->type) || startsWith("bigWig",tdb->type)) ;
 }
 
 static void printGroup(char *parent, struct trackDb *tdb, boolean folder, boolean user)
+// output the table rows for a group
+{
+char *userString = "";
+
+if (user)
     {
-printf("<tr data-tt-parent-id='%s' data-tt-id='%s' %s><td><span class='%s'>%s</span></td>",  parent, trackHubSkipHubName(tdb->track),   user ? "class='user'" : "", folder ? "folder" : "file", tdb->shortLabel );
+    if (tdb->parent && tdb->subtracks) 
+        userString = "class='user view'";
+    else
+        userString = "class='user'";
+    }
+    
+
+printf("<tr data-tt-parent-id='%s' data-tt-id='%s' %s><td><span class='%s'>%s</span></td>",  parent, trackHubSkipHubName(tdb->track),   userString, folder ? "folder" : "file", tdb->shortLabel );
 printf("<td>%s</td></tr>\n", tdb->longLabel);
 
 
 if (tdb->subtracks)
     {
     struct trackDb *subTdb;
 
     for(subTdb = tdb->subtracks; subTdb; subTdb = subTdb->next)
-        printGroup(trackHubSkipHubName(tdb->track), subTdb, FALSE, user);
+        printGroup(trackHubSkipHubName(tdb->track), subTdb, user && (subTdb->subtracks != NULL), user);
     }
 }
 
 static void outHubHeader(FILE *f, char *db, char *hubName)
+// output a track hub header
 {
 char *hubFile = strrchr(hubName, '/') + 1;
 
 fprintf(f,"hub hub1\n\
 shortLabel User Composite\n\
 longLabel User Composite\n\
 genomesFile %s\n\
 email braney@soe.ucsc.edu\n\
 descriptionUrl hub.html\n\n", hubFile);
 fprintf(f,"genome %s\n\
 trackDb %s\n\n", db, hubFile);  
 }
 
 
 static char *getHubName(char *db)
+// get the name of the hub to use for user collections
 {
 struct tempName hubTn;
 char buffer[4096];
 safef(buffer, sizeof buffer, "%s-%s", customCompositeCartName, db);
 char *hubName = cartOptionalString(cart, buffer);
 
 if (hubName == NULL)
     {
     trashDirDateFile(&hubTn, "hgComposite", "hub", ".txt");
     hubName = cloneString(hubTn.forCgi);
     cartSetString(cart, buffer, hubName);
     FILE *f = mustOpen(hubName, "a");
     outHubHeader(f, db, hubName);
     fclose(f);
     cartSetString(cart, "hubUrl", hubName);
@@ -212,43 +207,45 @@
 
 char *cartVis = cartOptionalString(cart, tdb->parent->track);
 boolean vis;
 if (cartVis != NULL) 
     vis =  differentString(cartVis, "hide");
 else if (tdbIsSuperTrack(tdb->parent))
     vis = tdb->parent->isShow;
 else
     vis = tdb->parent->visibility != tvHide;
 
 return vis;
 }
 
 
 void addVisibleTracks()
+// add the visible tracks table rows
 {
 printf("<tr data-tt-id='visible' ><td><span class='file'>All Visible</td><td>All the tracks visible in hgTracks</td></tr>\n");
 struct trackDb *tdb;
 for(tdb = fullTrackList; tdb; tdb = tdb->next)
     {
     if (isParentVisible(tdb) &&  isSubtrackVisible(tdb))
         {
         printGroup("visible", tdb, FALSE, FALSE);
         }
     }
 }
 
 static void doHeader()
+// build the header for the main page
 {
 puts(  
 "<a name='TRACK_TOP'></a>\n"  
 "    <div class='row gbTrackTitleBanner'>\n"  
 "       <div class='col-md-10'>\n"  
 );  
 printf(  
 "           <span class='gbTrackName'>\n"  
 "               My Collections \n"  
 "           </span>"  
 "           <span class='gbTrackTitle'> Build Custom Collections of Tracks </span>\n"  );
 puts(  
 "<!-- Info icon built from stacked fa icons -->\n"  
 "           <a href='#INFO_SECTION' title='Jump to the track description'>\n"  
 "               <span class='gbIconSmall fa-stack'>\n"  
@@ -260,30 +257,31 @@
 
 "       </div>\n"  
 "       <div class='col-md-2 text-right'>\n"  
 "           <div class='gbButtonGoContainer text-right' title='Go to the Genome Browser'>\n"
 
 "               <div class='gbButtonGo' id='saveCollections' >Save</div>\n"
 "               <div class='gbButtonGo' id='discardChanges'>Discard Changes</div>\n"
 "       </div>\n"  );
 puts(  
 "       </div>\n"  
 "   </div>\n"  
 );  
 }
 
 static void doTable()
+// output the tree table
 {
 puts(
 "        <!-- Configuration panel -->\n"
 "        <div class='row gbSectionBanner'>\n"
 "            <div class='col-md-8'>All Tracks</div>\n"
 "            <div class='col-md-4 text-right'>\n");
 
 puts(
 "           <div class='gbButtonGoContainer text-right' title='Make New Collection'>\n"
 "               <div id='newCollection' class='gbButton'>Make New Collection </div>\n"
 "           </div>\n"
 );
 puts(
 "            </div>\n"
 "        </div>\n");
@@ -315,127 +313,140 @@
         continue;
     printf("<tr data-tt-id='%s'><td><span class='file'>%s</span></td><td></td></tr>\n", curGroup->name, curGroup->label );
     struct trackDb *tdb;
     for(tdb = fullTrackList; tdb;  tdb = tdb->next)
         {
         if ( sameString(tdb->grp, curGroup->name))
             {
             printGroup(curGroup->name, tdb, FALSE, FALSE);
             }
         }
     }
 printf("</tbody></table>\n");
 }
 
 static void doAttributes()
+// output the attribute pane
 {
 puts(
 "        <!-- Configuration panel -->\n"
 "        <div class='row gbSectionBanner'>\n"
 "            <div class='col-md-8'>Set Attributes</div>\n"
 "            <div class='col-md-4 text-right'>\n");
 puts(
 "           <div class='gbButtonGoContainer text-right' title='Save Props'>\n"
 "               <div id='propsSave' class='gbButton'>Save</div>\n"
 "           </div>\n"
 );
 puts(
 "           <div class='gbButtonGoContainer text-right' title='Discard Changes'>\n"
 "               <div id='propsDiscard' class='gbButton'>Discard Changes</div>\n"
 "           </div>\n"
 );
 puts(
 "</div></div>\n");
 // trackDb track options (none at the moment)
 puts(
 "<div id='TrackDbOptions' style=\"display: none;\">"
 "Drag to custom composite to copy to Collections"
 "</div>"
 );
 
-// mathwig options
+// view options
 puts(
-"<div class='jwInputLabel'  id='MathWigOptions' style=\"display: none;\">"
+"<div class='jwInputLabel'  id='viewOptions' style=\"display: none;\">"
 "<label for='name'>Name:</label>"
-"<input type='text' name='mathWigName' id='mathWigName' value='' class='text ui-widget-content ui-corner-all'>\n"
+"<input type='text' name='viewName' id='viewName' value='' class='text ui-widget-content ui-corner-all'>\n"
+"<br>"
 "<label for='description'>Description:</label>\n"
-"<input type='text' name='mathWigDescription' id='mathWigDescription' value='' class='text ui-widget-content ui-corner-all'>\n"
-"<label for='mathWigVis'>Visibility:</label>"
-"<SELECT ID='mathWigVis' >\n"
+"<input type='text' name='viewDescription' id='viewDescription' value='' class='text ui-widget-content ui-corner-all'>\n"
+"<br>"
+"<label for='viewVis'>Visibility:</label>"
+"<SELECT ID='viewVis' >\n"
 "<OPTION>hide</OPTION>\n"
 "<OPTION SELECTED>dense</OPTION>\n"
 "<OPTION>squish</OPTION>\n"
 "<OPTION>pack</OPTION>\n"
 "<OPTION>full</OPTION>\n"
 "</SELECT>\n"
-"<label for='mathWigFunction'>Function:</label>"
-"<SELECT ID='mathWigFunction' >\n"
-"<OPTION SELECTED>add</OPTION>\n"
-"<OPTION >subtract</OPTION>\n"
+"<br>"
+"<label for='viewFunc'>Function:</label>"
+"<SELECT ID='viewFunc' >\n"
+"<OPTION SELECTED>show all</OPTION>\n"
+"<OPTION >add all</OPTION>\n"
+"<OPTION >subtract from the first</OPTION>\n"
 "</SELECT>\n"
-" <p>Highlight color: <input type='text' id='mathColorInput' value='0xffffff'>&nbsp;&nbsp;<input id='mathColorPicker'>"
+"<br>"
+" <p>Color: <input type='text' id='viewInput' value='0xffffff'>&nbsp;&nbsp;<input id='viewPicker'>"
 "</div>\n"
 );
 
 // custom composite options
 puts(
 "<div class='jwInputLabel'  id='CustomCompositeOptions' style=\"display: none;\">"
 "<label for='name'>Name:</label>"
 "<input type='text' name='collectionName' id='collectionName' value='' class='text ui-widget-content ui-corner-all'>\n"
+"<br>"
 "<label for='description'>Description:</label>\n"
 "<input type='text' name='collectionDescription' id='collectionDescription' value='' class='text ui-widget-content ui-corner-all'>\n"
+"<br>"
 "<label for='collectionVis'>Visibility:</label>"
 "<SELECT ID='collectionVis' style='width: 70px'>\n"
 "<OPTION>hide</OPTION>\n"
 "<OPTION SELECTED>dense</OPTION>\n"
 "<OPTION>squish</OPTION>\n"
 "<OPTION>pack</OPTION>\n"
 "<OPTION>full</OPTION>\n"
 "</SELECT>\n"
-"<input type='button' value='Create MathWig' name='createMathWig' id='createMathWig'>\n"
+"<br>"
+"<input type='button' value='Create View' name='createView' id='createView'>\n"
 "</div>\n"
 );
 
 // custom track  options
 puts(
 "<div class='jwInputLabel'  id='CustomTrackOptions' style=\"display: none;\">"
 "<label for='name'>Name:</label>"
 "<input type='text' name='customName' id='customName' value='' class='text ui-widget-content ui-corner-all'>\n"
+"<br>"
 "<label for='description'>Description:</label>\n"
 "<input type='text' name='customDescription' id='customDescription' value='' class='text ui-widget-content ui-corner-all'>\n"
+"<br>"
 "<label for='customVis'>Visibility:</label>"
 "<SELECT ID='customVis' style='width: 70px'>\n"
 "<OPTION>hide</OPTION>\n"
 "<OPTION SELECTED>dense</OPTION>\n"
 "<OPTION>squish</OPTION>\n"
 "<OPTION>pack</OPTION>\n"
 "<OPTION>full</OPTION>\n"
 "</SELECT>\n"
-" <p>Highlight color: <input type='text' id='trackColorInput' value='0xffffff'>&nbsp;&nbsp;<input id='trackColorPicker'>"
+"<br>"
+" <p>Color: <input type='text' id='trackColorInput' value='0xffffff'>&nbsp;&nbsp;<input id='trackColorPicker'>"
 "</div>\n"
 
 );
 
 }
 
 static void onclickJumpToTop(char *id)
 /* CSP-safe click handler arrows that cause scroll to top */
 {
 jsOnEventById("click", id, "$('html,body').scrollTop(0);");
 }
 
 static void printHelp()
+// print out the help page
 {
 puts(
 "<a name='INFO_SECTION'></a>\n"
 "    <div class='row gbSectionBanner'>\n"
 "        <div class='col-md-11'>Help</div>\n"
 "        <div class='col-md-1'>\n"
 );
 #define DATA_INFO_JUMP_ARROW_ID    "hgGtexDataInfo_jumpArrow"
 printf(
 "            <i id='%s' title='Jump to top of page' \n"
 "               class='gbIconArrow fa fa-lg fa-arrow-circle-up'></i>\n",
 DATA_INFO_JUMP_ARROW_ID
 );
 onclickJumpToTop(DATA_INFO_JUMP_ARROW_ID);
 puts(
@@ -445,233 +456,211 @@
 puts(
 "    <div class='row gbTrackDescriptionPanel'>\n"
 "       <div class='gbTrackDescription'>\n");
 puts("<div class='dataInfo'>");
 puts("</div>");
 webIncludeHelpFileSubst("hgCompositeHelp", NULL, FALSE);
 
 puts("<div class='dataInfo'>");
 puts("</div>");
 
 puts(
 "     </div>\n"
 "   </div>\n");
 
 
-puts("<script src=\"//code.jquery.com/jquery-1.9.1.min.js\"></script>");
-puts("<script src=\"//code.jquery.com/ui/1.10.3/jquery-ui.min.js\"></script>");
-jsIncludeFile("jquery.treetable.js", NULL);
-jsIncludeFile("utils.js", NULL);
-jsIncludeFile("ajax.js", NULL);
-jsIncludeFile("hgTracks.js", NULL);
-jsIncludeFile("spectrum.min.js", NULL);
-jsIncludeFile("hgCollection.js", NULL);
 }
 
 void doMainPage()
 /* Print out initial HTML of control page. */
 {
 webStartGbNoBanner(cart, database, "Collections");
 webIncludeResourceFile("jquery.treetable.css");
 webIncludeResourceFile("jquery.treetable.theme.default.css");
 webIncludeResourceFile("gb.css");
 webIncludeResourceFile("jWest.css");
 webIncludeResourceFile("spectrum.min.css");
 webIncludeResourceFile("hgGtexTrackSettings.css");
 
-//webIncludeFile("inc/hgCollection.html");
-
-printf(
-"<form action='%s' name='MAIN_FORM' method=%s>\n\n",
-                hgTracksName(), cartUsualString(cart, "formMethod", "POST"));
-
 doHeader();
 puts(
 "<!-- Track Configuration Panels -->\n"
 "    <div class='row'>\n"
 "        <div class='col-md-6'>\n");
 doTable();
 puts(
 "        </div>\n"
 "        <div class='col-md-6'>\n");
 doAttributes();
 puts(
 "        </div>\n"
 "    </div>\n"
 );
-puts(
-"</form>");
 printHelp();
 
-
+puts("<script src=\"//code.jquery.com/jquery-1.9.1.min.js\"></script>");
+puts("<script src=\"//code.jquery.com/ui/1.10.3/jquery-ui.min.js\"></script>");
+jsIncludeFile("jquery.treetable.js", NULL);
+jsIncludeFile("utils.js", NULL);
+jsIncludeFile("ajax.js", NULL);
+jsIncludeFile("spectrum.min.js", NULL);
+jsIncludeFile("hgCollection.js", NULL);
+webEndGb();
 }
 
 static char *getSqlBigWig(struct sqlConnection *conn, char *db, struct trackDb *tdb)
+// figure out the bigWig for native tables
 {
 char buffer[4096];
 
 safef(buffer, sizeof buffer, "NOSQLINJ select fileName from %s", tdb->table);
 return sqlQuickString(conn, buffer);
 }
 
 char *getUrl(struct sqlConnection *conn, char *db,  struct track *track, struct hash *nameHash)
+// get the bigDataUrl for a track
 {
 struct trackDb *tdb = hashMustFindVal(nameHash, track->name);
 
 if (tdb == NULL)
     errAbort("cannot find trackDb for %s\n", track->name);
 
 char *bigDataUrl = trackDbSetting(tdb, "bigDataUrl");
 if (bigDataUrl == NULL)
     {
     if (startsWith("bigWig", tdb->type))
         bigDataUrl = getSqlBigWig(conn, db, tdb);
     }
 return bigDataUrl;
 }
 
 void outTdb(struct sqlConnection *conn, char *db, FILE *f, char *name,  struct trackDb *tdb, char *parent, unsigned int color, struct track *track, struct hash *nameHash, struct hash *collectionNameHash)
+// out the trackDb for one track
 {
 char *dataUrl = NULL;
 char *bigDataUrl = trackDbSetting(tdb, "bigDataUrl");
 
 if (bigDataUrl == NULL)
     {
     if (startsWith("bigWig", tdb->type))
         dataUrl = getSqlBigWig(conn, db, tdb);
     }
 struct hashCookie cookie = hashFirst(tdb->settingsHash);
 struct hashEl *hel;
 fprintf(f, "\ttrack %s\n", makeUnique(collectionNameHash, name));
 while ((hel = hashNext(&cookie)) != NULL)
     {
-    if (sameString(hel->name, "mathDataUrl"))
-        {
-/*
-        fprintf(f, "\ttrackNames ");
-        struct mathTrack *mt = (struct mathTrack *)track;
-        struct track *tr = mt->trackList;
-        for(;  tr; tr = tr->next)
-            {
-            fprintf(f, "%s ", tr->name);
-            }
-        fprintf(f, "\n");
-
-        fprintf(f, "\tmathDataUrl ");
-        tr = mt->trackList;
-        if ((mt->function == NULL) || sameString(mt->function, "add"))
-            fprintf(f, "+  ");
-        else
-            fprintf(f, "-  ");
-        for(;  tr; tr = tr->next)
-            {
-            fprintf(f, "%s ", getUrl(conn, db, wigTracks, tr, nameHash));
-            }
-        fprintf(f, "\n");
-*/
-        }
-    else if (differentString(hel->name, "parent") && differentString(hel->name, "polished")&& differentString(hel->name, "color")&& differentString(hel->name, "track")&& differentString(hel->name, "trackNames")&& differentString(hel->name, "superTrack"))
+    if (differentString(hel->name, "parent") && differentString(hel->name, "polished")&& differentString(hel->name, "color")&& differentString(hel->name, "track")&& differentString(hel->name, "trackNames")&& differentString(hel->name, "superTrack"))
         fprintf(f, "\t%s %s\n", hel->name, (char *)hel->val);
     }
 if (bigDataUrl == NULL)
     {
     if (dataUrl != NULL)
         fprintf(f, "\tbigDataUrl %s\n", dataUrl);
     }
 fprintf(f, "\tparent %s\n",parent);
 fprintf(f, "\tcolor %d,%d,%d\n", (color >> 16) & 0xff,(color >> 8) & 0xff,color & 0xff);
 fprintf(f, "\n");
 }
 
 static void outComposite(FILE *f, struct track *collection)
+// output a composite header for user composite
 {
 char *parent = collection->name;
 char *shortLabel = collection->shortLabel;
 char *longLabel = collection->longLabel;
 fprintf(f,"track %s\n\
 shortLabel %s\n\
 compositeTrack on\n\
 aggregate none\n\
 longLabel %s\n\
 %s on\n\
 type wig \n\
 visibility full\n\n", parent, &shortLabel[2], longLabel, CUSTOM_COMPOSITE_SETTING);
 }
 
 static int snakePalette2[] =
 {
 0x1f77b4, 0xaec7e8, 0xff7f0e, 0xffbb78, 0x2ca02c, 0x98df8a, 0xd62728, 0xff9896, 0x9467bd, 0xc5b0d5, 0x8c564b, 0xc49c94, 0xe377c2, 0xf7b6d2, 0x7f7f7f, 0xc7c7c7, 0xbcbd22, 0xdbdb8d, 0x17becf, 0x9edae5
 };
 
 
-static void outMathWig(FILE *f, struct sqlConnection *conn, char *db, struct track *mathWig, struct hash *nameHash)
+static void outView(FILE *f, struct sqlConnection *conn, char *db, struct track *view, char *parent, struct hash *nameHash, struct hash *collectionNameHash)
+// output a view to a trackhub
 {
 fprintf(f,"\ttrack %s\n\
 \tshortLabel %s\n\
 \tlongLabel %s\n\
-\ttype mathWig \n\
-\tvisibility full\n", mathWig->name, &mathWig->shortLabel[2], mathWig->longLabel);
-fprintf(f,"\tmathDataUrl + ");
-struct track *track = mathWig->trackList;
+\tview %s \n\
+\tparent %s \n\
+\tvisibility full\n", view->name, &view->shortLabel[2], view->longLabel, view->name, parent);
+//fprintf(f,"\tequation +\n");
+fprintf(f, "\n");
+
+int useColor = 0;
+struct track *track = view->trackList;
 for(; track; track = track->next)
     {
-    fprintf(f, "%s ",  getUrl(conn, db,  track, nameHash));
-    }
+    struct trackDb *tdb = hashMustFindVal(nameHash, track->name);
 
-fprintf(f, "\n");
+    outTdb(conn, db, f, track->name,tdb, view->name, snakePalette2[useColor], track,  nameHash, collectionNameHash);
+    useColor++;
+    }
 
 }
 
 void updateHub(char *db, struct track *collectionList, struct hash *nameHash)
+// save our state to the track hub
 {
 char *hubName = getHubName(db);
 
 chmod(hubName, 0666);
 FILE *f = mustOpen(hubName, "w");
 struct hash *collectionNameHash = newHash(6);
 
 outHubHeader(f, db, hubName);
 int useColor = 0;
 struct track *collection;
 struct sqlConnection *conn = hAllocConn(db);
 for(collection = collectionList; collection; collection = collection->next)
     {
     outComposite(f, collection);
     struct trackDb *tdb;
     struct track *track;
     for (track = collection->trackList; track; track = track->next)
         {
         if (track->trackList != NULL)
             {
-            outMathWig(f, conn, db, track, nameHash);
+            outView(f, conn, db, track, collection->name,  nameHash, collectionNameHash);
             }
         else
             {
             tdb = hashMustFindVal(nameHash, track->name);
 
             outTdb(conn, db, f, track->name,tdb, collection->name, snakePalette2[useColor], track,  nameHash, collectionNameHash);
             useColor++;
             if (useColor == (sizeof snakePalette2 / sizeof(int)))
                 useColor = 0;
             }
         }
     }
 fclose(f);
 hFreeConn(&conn);
 }
 
 static struct track *parseJson(char *jsonText)
+// parse the JSON of the treetable from the Javascript
 {
 struct hash *trackHash = newHash(5);
 struct track *collectionList = NULL;
 struct track *track;
 char *ptr = jsonText;
 if (*ptr != '[')
     errAbort("element didn't start with [");
 ptr++;
 
 do
     {
     if (*ptr != '[')
         errAbort("element didn't start with [");
     ptr++;
 
@@ -689,54 +678,56 @@
     track->longLabel = getString(&ptr);
     track->name = getString(&ptr);
     track->visibility = getString(&ptr);
     hashAdd(trackHash, track->name, track);
     if (*ptr != ']')
         errAbort("element didn't end with ]");
     ptr++;
     if (*ptr == ',')
         ptr++;
     } while (*ptr != ']');
 
 return collectionList;
 }
 
 void doAjax(char *db, char *jsonText, struct hash *nameHash)
+// Save our state
 {
 struct track *collectionList = parseJson(jsonText);
 
 updateHub(db, collectionList, nameHash);
 }
 
 static struct hash *buildNameHash(struct trackDb *list)
 // TODO;  needs to go down one more layer
 {
 struct hash *nameHash = newHash(8);
 struct trackDb *tdb;
 for(tdb = list; tdb;  tdb = tdb->next)
     {
     hashAdd(nameHash, trackHubSkipHubName(tdb->track), tdb);
     struct trackDb *subTdb = tdb->subtracks;
     for(; subTdb; subTdb = subTdb->next)
         {
         hashAdd(nameHash, trackHubSkipHubName(subTdb->track), subTdb);
         }
     }
 return nameHash;
 }
 
 static struct trackDb *traverseTree(struct trackDb *oldList, struct hash *groupHash)
+// add acceptable tracks to our tree
 {
 struct trackDb *newList = NULL, *tdb, *tdbNext;
 
 for(tdb = oldList;  tdb ; tdb = tdbNext)
     {
     tdbNext = tdb->next;
     if (tdb->subtracks)
         {
         tdb->subtracks = traverseTree(tdb->subtracks, groupHash);
 
         if (tdb->subtracks)
             {
             hashStore(groupHash, tdb->grp);
             slAddHead(&newList, tdb);
             }
@@ -744,30 +735,31 @@
     else
         {
         if (trackCanBeAdded(tdb))
             {
             hashStore(groupHash, tdb->grp);
             slAddHead(&newList, tdb);
             }
         }
     }
 slReverse(&newList);
 
 return newList;
 }
 
 static void pruneTrackList(struct trackDb **fullTrackList, struct grp **fullGroupList)
+// drop track types we don't grok yet
 {
 struct hash *groupHash = newHash(5);
 
 *fullTrackList = traverseTree(*fullTrackList, groupHash);
 struct grp *newGroupList = NULL, *grp, *nextGrp;
 
 for (grp = *fullGroupList; grp; grp = nextGrp)
     {
     nextGrp = grp->next;
 
     if (hashLookup(groupHash, grp->name))
         slAddHead(&newGroupList, grp);
     }
 slReverse(&newGroupList);
 *fullGroupList = newGroupList;