10a0062ff3d8f35b50b8944685bcc551d68ea26e
angie
  Fri Jun 7 07:04:27 2013 -0700
Valuable feedback from code review -- thanks Tim!  refs 10977
diff --git src/hg/hgVai/libifyMe.c src/hg/hgVai/libifyMe.c
index 31e918d..a3a4920 100644
--- src/hg/hgVai/libifyMe.c
+++ src/hg/hgVai/libifyMe.c
@@ -110,37 +110,39 @@
 tdb->longLabel = WIKI_TRACK_LONGLABEL;
 tdb->visibility = tvFull;
 tdb->priority = WIKI_TRACK_PRIORITY;
 
 tdb->html = hFileContentsOrWarning(hHelpFile(tdb->track));
 tdb->type = "none";
 tdb->grp = "map";
 tdb->canPack = FALSE;
 
 slAddHead(list, tdb);
 slSort(list, trackDbCmp);
 }
 
 static struct trackDb *getFullTrackList(struct cart *cart, struct hubConnectStatus *hubList,
 					struct grp **pHubGroups)
-/* Get all tracks including custom tracks if any. */
+/* Get all tracks including custom tracks if any. *pHubGroups is reversed. */
 {
 char *db = cartString(cart, "db");
 struct trackDb *list = hTrackDb(db);
 struct customTrack *ctList, *ct;
 
 /* exclude any track with a 'tableBrowser off' setting */
+//#*** NOTE: this only scans top-level tdb's, and would miss a setting applied
+//#*** only to lower levels.  So far we have not encountered such a track.
 struct trackDb *tdb, *nextTdb, *newList = NULL;
 for (tdb = list;  tdb != NULL;  tdb = nextTdb)
     {
     nextTdb = tdb->next;
     if (tdbIsDownloadsOnly(tdb) || tdb->table == NULL)
         {
         //freeMem(tdb);  // should not free tdb's.
         // While hdb.c should and says it does cache the tdbList, it doesn't.
         // The most notable reason that the tdbs are not cached is this hgTables CGI !!!
         // It needs to be rewritten to make tdbRef structures for the lists it creates here!
         continue;
         }
 
     char *tbOff = trackDbSetting(tdb, "tableBrowser");
     if (tbOff == NULL || !startsWithWord("off", tbOff))
@@ -167,55 +169,52 @@
 
 return list;
 }
 
 static struct grp *makeGroupList(char *db, struct trackDb *trackList,
 				 struct grp **pHubGrpList, boolean allTablesOk)
 /* Get list of groups that actually have something in them. */
 {
 struct grp *groupsAll, *groupList = NULL, *group;
 struct hash *groupsInTrackList = newHash(0);
 struct hash *groupsInDb = newHash(0);
 struct trackDb *track;
 
 /* Stream through track list building up hash of active groups. */
 for (track = trackList; track != NULL; track = track->next)
-    {
-    if (!hashLookup(groupsInTrackList,track->grp))
-        hashAdd(groupsInTrackList, track->grp, NULL);
-    }
+    hashStoreName(groupsInTrackList, track->grp);
 
 /* Scan through group table, putting in ones where we have data. */
 groupsAll = hLoadGrps(db);
 for (group = slPopHead(&groupsAll); group != NULL; group = slPopHead(&groupsAll))
     {
     if (hashLookup(groupsInTrackList, group->name))
 	{
 	slAddTail(&groupList, group);
 	hashAdd(groupsInDb, group->name, group);
 	}
     else
         grpFree(&group);
     }
 
 /* if we have custom tracks, we want to add the track hubs
  * after that group */
 struct grp *addAfter = NULL;
 if (sameString(groupList->name, "user"))
     addAfter = groupList;
 
-/* Add in groups from hubs. */
+/* Add in groups from hubs.  *pHubGrpList is reversed, so we add at head to restore order. */
 for (group = slPopHead(pHubGrpList); group != NULL; group = slPopHead(pHubGrpList))
     {
     /* check to see if we're inserting hubs rather than
      * adding them to the front of the list */
     if (addAfter != NULL)
 	{
 	group->next = addAfter->next;
 	addAfter->next = group;
 	}
     else
 	slAddHead(&groupList, group);
     hashAdd(groupsInDb, group->name, group);
     }
 
 /* Do some error checking for tracks with group names that are
@@ -373,31 +372,31 @@
     if (dbTable != NULL)
 	// This is really a database table, not a bigDataUrl CT.
 	dataDb = CUSTOM_TRASH;
     }
 if (startsWith("wig", tdb->type))
     streamer = annoStreamWigDbNew(dataDb, dbTable, assembly, maxOutRows);
 else if (sameString("vcfTabix", tdb->type))
     {
     char *fileOrUrl = getBigDataFileName(db, tdb, selTable, chrom);
     streamer = annoStreamVcfNew(fileOrUrl, TRUE, assembly, maxOutRows);
     }
 else if (sameString("bam", tdb->type))
     {
     warn("Sorry, BAM is not yet supported");
     }
-else if (sameString("bigBed", tdb->type))
+else if (startsWith("bigBed", tdb->type))
     {
     char *fileOrUrl = getBigDataFileName(db, tdb, selTable, chrom);
     streamer = annoStreamBigBedNew(fileOrUrl, assembly, maxOutRows);
     }
 else
     {
     struct sqlConnection *conn = hAllocConn(dataDb);
     char maybeSplitTable[1024];
     if (sqlTableExists(conn, dbTable))
 	safecpy(maybeSplitTable, sizeof(maybeSplitTable), dbTable);
     else
 	safef(maybeSplitTable, sizeof(maybeSplitTable), "%s_%s", chrom, dbTable);
     hFreeConn(&conn);
     struct asObject *asObj = getAutoSqlForTable(db, dataDb, maybeSplitTable, tdb);
     streamer = annoStreamDbNew(dataDb, maybeSplitTable, assembly, asObj, maxOutRows);
@@ -406,30 +405,31 @@
 }
 
 struct annoGrator *gratorFromTrack(struct annoAssembly *assembly, char *selTable,
 				   struct trackDb *tdb, char *chrom, int maxOutRows,
 				   struct asObject *primaryAsObj,
 				   enum annoGratorOverlap overlapRule)
 /* Figure out the source and type of data, make an annoStreamer & wrap in annoGrator.
  * If not NULL, primaryAsObj is used to determine whether we can make an annoGratorGpVar. */
 {
 struct annoGrator *grator = NULL;
 char *dataDb = assembly->name, *dbTable = selTable;
 if (isCustomTrack(selTable))
     {
     dataDb = CUSTOM_TRASH;
     dbTable = trackDbSetting(tdb, "dbTableName");
+    //#*** Catch custom bigBed & bigWig here.
     if (dbTable == NULL)
 	errAbort("Can't find dbTableName for custom track %s", selTable);
     }
 if (startsWith("wig", tdb->type))
     {
     grator = annoGrateWigDbNew(dataDb, dbTable, assembly, maxOutRows);
     }
 else
     {
     struct annoStreamer *streamer = streamerFromTrack(assembly, dbTable, tdb, chrom, maxOutRows);
     if (primaryAsObj != NULL &&
 	(asObjectsMatch(primaryAsObj, pgSnpAsObj()) || asObjectsMatch(primaryAsObj, vcfAsObj()))
 	&& asObjectsMatchFirstN(streamer->asObj, genePredAsObj(), 10))
 	grator = annoGratorGpVarNew(streamer);
     else