6cbe668f98ab263238542b8fd6bbf860db113930
angie
  Wed Jan 28 09:55:13 2015 -0800
Several major changes in the way data sources are represented:
1. Instead of the CGI sending menus for group, track and table, make it send
the whole tree of groups, tracks, views, subtracks -- "groupedTrackDb".
Now the client-side model can make menus that reflect the tree structure.
This also gives us flexibility to tweak labels easily.
2. Instead of hardcoding group, track and table into elements of
hgai_querySpec.dataSources, make each data source a proper object with
a trackPath (path through groupedTrackDb), and for the UI, other attributes
like label and URL for hgTables schema page.  When the CGI unpacks
hgai_querySpec.dataSources, it now uses trackPath and gets table from the
leaf track or subtrack (the last item in trackPath).
Note: this doesn't work for old Conservation tracks that don't have
views/subtracks -- need to figure out how to fudge subtracks for those.
3. Limit the number of data sources to 5, don't let the user add the same
data source twice, and remember which track the user most recently selected
in the "Add Data Source" section.
Also, in hgAi.jsx, I separated out a bunch of things that were rendered in
the top-level AppComponent into subcomponents to add some granularity to
"Loading..." sections (instead of making the whole page saying "Loading..."
until everything is in place).

diff --git src/lib/jsonWrite.c src/lib/jsonWrite.c
index e2fa0cf..458ba96 100644
--- src/lib/jsonWrite.c
+++ src/lib/jsonWrite.c
@@ -95,30 +95,38 @@
 struct tm tm;
 gmtime_r(&timeStamp, &tm);
 jsonWriteTag(jw, var);
 dyStringPrintf(dy, "\"%d:%02d:%02dT%02d:%02d:%02dZ\"",
     1900+tm.tm_year, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
 }
 
 void jsonWriteNumber(struct jsonWrite *jw, char *var, long long val)
 /* print out "var": val as number. Var may be NULL. */
 {
 struct dyString *dy = jw->dy;
 jsonWriteTag(jw, var);
 dyStringPrintf(dy, "%lld", val);
 }
 
+void jsonWriteDouble(struct jsonWrite *jw, char *var, double val)
+/* print out "var": val as number. Var may be NULL. */
+{
+struct dyString *dy = jw->dy;
+jsonWriteTag(jw, var);
+dyStringPrintf(dy, "%lf", val);
+}
+
 void jsonWriteLink(struct jsonWrite *jw, char *var, char *objRoot, char *name)
 /* Print out the jsony type link to another object.  objRoot will start and end with a '/'
  * and may have additional slashes in this usage. Var may be NULL. */
 {
 struct dyString *dy = jw->dy;
 jsonWriteTag(jw, var);
 dyStringPrintf(dy, "\"%s%s\"", objRoot, name);
 }
 
 void jsonWriteLinkNum(struct jsonWrite *jw, char *var, char *objRoot, long long id)
 /* Print out the jsony type link to another object with a numerical id.  objRoot will start 
  * and end with a '/' and may have additional slashes in this usage. Var may be NULL */
 {
 struct dyString *dy = jw->dy;
 jsonWriteTag(jw, var);