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;'> %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);