16fdc50e4e49b031f566911bf0371d4942282d75
larrym
  Fri Apr 27 16:22:37 2012 -0700
use a union in jsonElement to simplify code and eliminate typecasts (per suggestion of angie and tim); rename jsonHash to jsonObject; fix some obsolete comments
diff --git src/hg/lib/jsHelper.c src/hg/lib/jsHelper.c
index 14e87af..bd2a2e3 100644
--- src/hg/lib/jsHelper.c
+++ src/hg/lib/jsHelper.c
@@ -1,45 +1,45 @@
-/* javascript - some little helper routines  to manage our javascript.
- * We don't do much javascript - just occassionally use it so that
- * when they select something from a pull-down, it will go hit the server to
- * figure out how to reload other control options based on the choice.
- * (For instance if they change the group, which items in the track
- * drop-down need to change).
- *
- * We accomplish this by maintaining two forms - a mainForm and a
- * hiddenForm.  The hiddenForm maintains echo's of all the variables
- * in the main form, which get updated onChange of controls that need
- * to 'ripple' to other controls.  The onChange also submits the
- * control. */
+// jsHelper.c - helper routines for interface between CGIs and client-side javascript
 
 #include "common.h"
 #include <regex.h>
 #include "dystring.h"
 #include "cheapcgi.h"
 #include "cart.h"
 #include "hPrint.h"
 #include "hash.h"
 #include "jsHelper.h"
 #include "web.h"
 #include "hui.h"
 #include "hgConfig.h"
 #include "portable.h"
 
-
 static boolean jsInited = FALSE;
 
-struct jsonHashElement *jsonGlobalsHash = NULL;
+struct jsonElement *jsonGlobalsHash = NULL;
+
+/* mainForm/hiddenForm code supports the following: when the user selects
+ * something from a pull-down, it will go hit the server to
+ * figure out how to reload other control options based on the choice.
+ * (For instance if they change the group, which items in the track
+ * drop-down need to change).
+ *
+ * We accomplish this by maintaining two forms - a mainForm and a
+ * hiddenForm.  The hiddenForm maintains echo's of all the variables
+ * in the main form, which get updated onChange of controls that need
+ * to 'ripple' to other controls.  The onChange also submits the
+ * control. */
 
 void jsInit()
 /* If this is the first call, set window.onload to the operations
  * performed upon loading a page and print supporting javascript.
  * Currently this just sets the page vertical position if specified on
  * CGI, and also calls jsWriteFunctions.
  * Subsequent calls do nothing, so this can be called many times. */
 {
 if (! jsInited)
     {
     puts(
 
 "<INPUT TYPE=HIDDEN NAME=\"jsh_pageVertPos\" VALUE=0>\n"
 "<script language=\"javascript\">\n"
 "// f_scrollTop and f_filterResults taken from\n"
@@ -458,242 +458,192 @@
        "id='%s_button' src='%s' alt='%s' title='%s this section' class='bigBlue'"
        " style='cursor:pointer;'>\n",
        section, track,
        section, buttonImage, (isOpen ? "-" : "+"), (isOpen ? "Collapse": "Expand"));
 #endif///ndef BUTTONS_BY_CSS
 printf("<B style='font-size:larger;'>&nbsp;%s</B></TD></TR>\n", sectionTitle);
 printf("<TR %sid='%s-%d'><TD colspan=2>", isOpen ? "" : "style='display: none' ", section, 1);
 }
 
 void jsEndCollapsibleSection()
 /* End the collapsible <TR id=...>. */
 {
 puts("</TD></TR>");
 }
 
-struct jsonStringElement *newJsonString(char *str)
+static struct jsonElement *newJsonElement(jsonElementType type)
+// generic constructor for a jsonElement; callers fill in the appropriate value
 {
-struct jsonStringElement *ele;
+struct jsonElement *ele;
 AllocVar(ele);
-ele->str = cloneString(str);
-ele->type = jsonString;
+ele->type = type;
 return ele;
 }
 
-struct jsonBooleanElement *newJsonBoolean(boolean val)
+struct jsonElement *newJsonString(char *str)
 {
-struct jsonBooleanElement *ele;
-AllocVar(ele);
-ele->val = val;
-ele->type = jsonBoolean;
+struct jsonElement *ele = newJsonElement(jsonString);
+ele->val.jeString = cloneString(str);
 return ele;
 }
 
-struct jsonNumberElement *newJsonNumber(long val)
+struct jsonElement *newJsonBoolean(boolean val)
 {
-struct jsonNumberElement *ele;
-AllocVar(ele);
-ele->val = val;
-ele->type = jsonNumber;
+struct jsonElement *ele = newJsonElement(jsonBoolean);
+ele->val.jeBoolean = val;
 return ele;
 }
 
-struct jsonDoubleElement *newJsonDouble(double val)
+struct jsonElement *newJsonNumber(long val)
 {
-struct jsonDoubleElement *ele;
-AllocVar(ele);
-ele->val = val;
-ele->type = jsonDouble;
+struct jsonElement *ele = newJsonElement(jsonNumber);
+ele->val.jeNumber = val;
 return ele;
 }
 
-struct jsonHashElement *newJsonHash(struct hash *h)
+struct jsonElement *newJsonDouble(double val)
 {
-struct jsonHashElement *ele;
-AllocVar(ele);
-ele->type = jsonHash;
-ele->hash = h;
+struct jsonElement *ele = newJsonElement(jsonDouble);
+ele->val.jeDouble = val;
 return ele;
 }
 
-struct jsonListElement *newJsonList(struct slRef *list)
+struct jsonElement *newJsonObject(struct hash *h)
 {
-struct jsonListElement *ele;
-AllocVar(ele);
-ele->type = jsonList;
-ele->list = list;
+struct jsonElement *ele = newJsonElement(jsonObject);
+ele->val.jeHash = h;
 return ele;
 }
 
-void jsonHashAdd(struct jsonHashElement *h, char *name, struct jsonElement *ele)
+struct jsonElement *newJsonList(struct slRef *list)
 {
-if (h == NULL)  // If hash isn't provided, assume global
-    {
-    if (jsonGlobalsHash == NULL)
-        jsonGlobalsHash = newJsonHash(newHash(8));
-    h = jsonGlobalsHash;
-    }
-hashReplace(h->hash, name, ele);
-}
-
-void jsonHashAddString(struct jsonHashElement *h, char *name, char *val)
-{
-// Add a string to a hash which will be used to print a javascript object;
-// existing values are replaced.
-jsonHashAdd(h, name, (struct jsonElement *) newJsonString(val));
+struct jsonElement *ele = newJsonElement(jsonList);
+ele->val.jeList = list;
+return ele;
 }
 
-void jsonHashAddNumber(struct jsonHashElement *h, char *name, long val)
+void jsonObjectAdd(struct jsonElement *h, char *name, struct jsonElement *ele)
+// Add a new element to a jsonObject; existing values are replaced.
+// NOTE: Adding to a NULL hash will add to the global "common" hash printed with jsonPrintGlobals();
 {
-// Add a number to a hash which will be used to print a javascript object;
-// existing values are replaced.
-jsonHashAdd(h, name, (struct jsonElement *) newJsonNumber(val));
-}
-
-void jsonHashAddDouble(struct jsonHashElement *h, char *name, double val)
+if (h == NULL)  // If hash isn't provided, assume global
 {
-// Add a number to a hash which will be used to print a javascript object;
-// existing values are replaced.
-jsonHashAdd(h, name, (struct jsonElement *) newJsonDouble(val));
+    if (jsonGlobalsHash == NULL)
+        jsonGlobalsHash = newJsonObject(newHash(8));
+    h = jsonGlobalsHash;
 }
-
-void jsonHashAddBoolean(struct jsonHashElement *h, char *name, boolean val)
-{
-// Add a boolean to a hash which will be used to print a javascript object;
-// existing values are replaced.
-jsonHashAdd(h, name, (struct jsonElement *) newJsonBoolean(val));
+if(h->type != jsonObject)
+    errAbort("jsonObjectAdd called on element with incorrect type (%d)", h->type);
+hashReplace(h->val.jeHash, name, ele);
 }
 
-void jsonListAdd(struct slRef **list, struct jsonElement *ele)
+void jsonListAdd(struct jsonElement *list, struct jsonElement *ele)
 {
 struct slRef *e;
-AllocVar(e);
-e->val = ele;
-slAddHead(list, e);
-}
-
-void jsonListAddString(struct slRef **list, char *val)
-{
-jsonListAdd(list, (struct jsonElement *) newJsonString(val));
-}
-
-void jsonListAddNumber(struct slRef **list, long val)
-{
-jsonListAdd(list, (struct jsonElement *) newJsonNumber(val));
-}
-
-void jsonListAddDouble(struct slRef **list, double val)
-{
-jsonListAdd(list, (struct jsonElement *) newJsonDouble(val));
-}
-
-void jsonListAddBoolean(struct slRef **list, boolean val)
-{
-jsonListAdd(list, (struct jsonElement *) newJsonString(val ? "true" : "false"));
+if(list->type != jsonList)
+    errAbort("jsonListAdd called on element with incorrect type (%d)", list->type);
+slAddHead(&list->val.jeList, e);
 }
 
 static char *makeIndentBuf(int indentLevel)
 {
 if (indentLevel < 0)
     return "";
 char *indentBuf;
 indentBuf = needMem(indentLevel + 1);
 memset(indentBuf, '\t', indentLevel);
 indentBuf[indentLevel] = 0;
 return indentBuf;
 }
 
-static void jsonPrintRecurse(struct jsonElement *json, int indentLevel)
+static void jsonPrintRecurse(struct jsonElement *ele, int indentLevel)
 {
 if (indentLevel >= -1) // Note that < -1 will result in no indenting
     indentLevel++;
 char *tab = "\t";
 char *nl = "\n";
 if (indentLevel < 0)
     {
     tab = "";
     nl = "";
     }
 char *indentBuf = makeIndentBuf(indentLevel);
-switch (json->type)
+switch (ele->type)
     {
-    case jsonHash:
+    case jsonObject:
         {
-        struct jsonHashElement *ele = (struct jsonHashElement *) json;
         hPrintf("{%s",nl);
-        if(hashNumEntries(ele->hash))
+        if(hashNumEntries(ele->val.jeHash))
             {
-            struct hashEl *el, *list = hashElListHash(ele->hash);
+            struct hashEl *el, *list = hashElListHash(ele->val.jeHash);
             slSort(&list, hashElCmp);
             for (el = list; el != NULL; el = el->next)
                 {
-                struct jsonElement *val = (struct jsonElement *) el->val;
+                struct jsonElement *val = el->val;
                 hPrintf("%s%s\"%s\": ", indentBuf, tab, el->name);
                 jsonPrintRecurse(val, indentLevel);
                 hPrintf("%s%s", el->next == NULL ? "" : ",",nl);
                 }
             hashElFreeList(&list);
             }
         hPrintf("%s}", indentBuf);
         break;
         }
     case jsonList:
         {
-        struct jsonListElement *rec = (struct jsonListElement *) json;
         struct slRef *el;
         hPrintf("[%s",nl);
-        if(rec->list)
+        if(ele->val.jeList)
             {
-            for (el = rec->list; el != NULL; el = el->next)
+            for (el = ele->val.jeList; el != NULL; el = el->next)
                 {
-                struct jsonElement *val = (struct jsonElement *) el->val;
+                struct jsonElement *val = el->val;
                 hPrintf("%s%s", indentBuf,tab);
                 jsonPrintRecurse(val, indentLevel);
                 hPrintf("%s%s", el->next == NULL ? "" : ",",nl);
                 }
             }
         hPrintf("%s]", indentBuf);
         break;
         }
     case jsonString:
         {
-        char *val = javaScriptLiteralEncode(((struct jsonStringElement *) json)->str);
-        hPrintf("\"%s\"", val);
+        hPrintf("\"%s\"", javaScriptLiteralEncode(ele->val.jeString));
         break;
         }
     case jsonBoolean:
         {
-        hPrintf("%s", ((struct jsonBooleanElement *) json)->val ? "true" : "false");
+        hPrintf("%s", ele->val.jeBoolean ? "true" : "false");
         break;
         }
     case jsonNumber:
         {
         char buf[256];
-        safef(buf, sizeof(buf), "%ld", ((struct jsonNumberElement *) json)->val);
+        safef(buf, sizeof(buf), "%ld", ele->val.jeNumber);
         hPrintf("%s", buf);
         break;
         }
     case jsonDouble:
         {
         char buf[256];
-        safef(buf, sizeof(buf), "%.10f", ((struct jsonDoubleElement *) json)->val);
+        safef(buf, sizeof(buf), "%g", ele->val.jeDouble);
         hPrintf("%s", buf);
         break;
         }
     default:
         {
-        errAbort("jsonPrintRecurse; invalid type: %d", json->type);
+        errAbort("jsonPrintRecurse; invalid type: %d", ele->type);
         break;
         }
     }
 if (indentLevel >= 0)
     freez(&indentBuf);
 }
 
 void jsonPrint(struct jsonElement *json, char *name, int indentLevel)
 {
 // print out a jsonElement, indentLevel -1 means no indenting
 
 char *indentBuf = makeIndentBuf(indentLevel);
 if(name != NULL)
     {
     if (indentLevel >= 0 )
@@ -707,31 +657,31 @@
     if (indentLevel >= 0 )
         hPrintf("// END %s\n", name);
     }
 if (indentLevel >= 0)
     freez(&indentBuf);
 }
 
 void jsonPrintGlobals(boolean wrapWithScriptTags)
 // prints out the "common" globals json hash
 // This hash is the one utils.js and therefore all CGIs know about
 {
 if (jsonGlobalsHash != NULL)
     {
     if (wrapWithScriptTags)
         printf("<script type='text/javascript'>\n");
-    jsonPrint((struct jsonElement *) jsonGlobalsHash, "common", 0);
+    jsonPrint(jsonGlobalsHash, "common", 0);
     if (wrapWithScriptTags)
         printf("</script>\n");
     }
 }
 
 void jsonErrPrintf(struct dyString *ds, char *format, ...)
 //  Printf a json error to a dyString for communicating with ajax code; format is:
 //  {"error": error message here}
 {
 va_list args;
 va_start(args, format);
 dyStringPrintf(ds, "{\"error\": \"");
 struct dyString *buf = newDyString(1000);
 dyStringVaPrintf(buf, format, args);
 dyStringAppend(ds, javaScriptLiteralEncode(dyStringCannibalize(&buf)));
@@ -829,102 +779,102 @@
     // parse out a name : val pair
     skipLeadingSpacesWithPos(str, posPtr);
     char *name = getString(str, posPtr);
     skipLeadingSpacesWithPos(str, posPtr);
     getSpecificChar(':', str, posPtr);
     skipLeadingSpacesWithPos(str, posPtr);
     hashAdd(h, name, jsonParseExpression(str, posPtr));
     skipLeadingSpacesWithPos(str, posPtr);
     if(str[*posPtr] == ',')
         (*posPtr)++;
     else
         break;
     }
 skipLeadingSpacesWithPos(str, posPtr);
 getSpecificChar('}', str, posPtr);
-return (struct jsonElement *) newJsonHash(h);
+return newJsonObject(h);
 }
 
 static struct jsonElement *jsonParseList(char *str, int *posPtr)
 {
 struct slRef *list = NULL;
 getSpecificChar('[', str, posPtr);
 while(str[*posPtr] != ']')
     {
     struct slRef *e;
     AllocVar(e);
     skipLeadingSpacesWithPos(str, posPtr);
     e->val = jsonParseExpression(str, posPtr);
     slAddHead(&list, e);
     skipLeadingSpacesWithPos(str, posPtr);
     if(str[*posPtr] == ',')
         (*posPtr)++;
     else
         break;
     }
 skipLeadingSpacesWithPos(str, posPtr);
 getSpecificChar(']', str, posPtr);
 slReverse(&list);
-return (struct jsonElement *) newJsonList(list);
+return newJsonList(list);
 }
 
 static struct jsonElement *jsonParseString(char *str, int *posPtr)
 {
-return (struct jsonElement *) newJsonString(getString(str, posPtr));
+return newJsonString(getString(str, posPtr));
 }
 
 static struct jsonElement *jsonParseBoolean(char *str, int *posPtr)
 {
-struct jsonBooleanElement *ele = NULL;
+struct jsonElement *ele = NULL;
 int i;
 for(i = 0; str[*posPtr + i] && isalpha(str[*posPtr + i]); i++);
     ;
 char *val = cloneStringZ(str + *posPtr, i);
 if(sameString(val, "true"))
     ele = newJsonBoolean(TRUE);
 else if(sameString(val, "false"))
     ele =  newJsonBoolean(FALSE);
 else
     errAbort("Invalid boolean value '%s'; pos: %d", val, *posPtr);
 *posPtr += i;
 freez(&val);
-return (struct jsonElement *) ele;
+return ele;
 }
 
 static struct jsonElement *jsonParseNumber(char *str, int *posPtr)
 {
 int i;
 boolean integral = TRUE;
 struct jsonElement *retVal = NULL;
 
 for(i = 0;; i++)
     {
     char c = str[*posPtr + i];
     if(c == 'e' || c == 'E' || c == '.')
         integral = FALSE;
     else if(!c || (!isdigit(c) && c != '-'))
         break;
     }
 char *val = cloneStringZ(str + *posPtr, i);
 *posPtr += i;
 if(integral)
-    retVal = (struct jsonElement *) newJsonNumber(sqlLongLong(val));
+    retVal = newJsonNumber(sqlLongLong(val));
 else
     {
     double d;
     if(sscanf(val, "%lf", &d))
-        retVal = (struct jsonElement *) newJsonDouble(d);
+        retVal = newJsonDouble(d);
     else
         errAbort("Invalid JSON Double: %s", val);
     }
 freez(&val);
 return retVal;
 }
 
 static struct jsonElement *jsonParseExpression(char *str, int *posPtr)
 {
 skipLeadingSpacesWithPos(str, posPtr);
 char c = str[*posPtr];
 if(c == '{')
     return jsonParseObject(str, posPtr);
 else if (c == '[')
     return jsonParseList(str, posPtr);