8fca283c4625a81c19eb71e87582e831771f03f6 chmalee Tue Mar 28 15:49:39 2023 -0700 Make single position search coming from hgGateway go directly to hgTracks before drawing any html, refs Max email diff --git src/hg/hgSearch/hgSearch.c src/hg/hgSearch/hgSearch.c index ec7655c..6996ed1 100644 --- src/hg/hgSearch/hgSearch.c +++ src/hg/hgSearch/hgSearch.c @@ -1,688 +1,685 @@ /* hgSearch - User interface to explore hgFind search results */ #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 "hui.h" #include "udc.h" #include "knetUdc.h" #include "hgConfig.h" #include "jsHelper.h" #include "errCatch.h" #include "hgFind.h" #include "cartJson.h" #include "trackHub.h" #include "hubConnect.h" #include "jsonWrite.h" #include "hgFind.h" #include "trix.h" #include "genbank.h" #include "cartTrackDb.h" #include "chromAlias.h" // name of the searchBar form variable from the HTML #define SEARCH_TERM_VAR "search" #define SEARCH_LIMIT 100 /* Standard CGI Global Variables */ struct cart *cart; /* CGI and other variables */ struct hash *oldVars = NULL; boolean measureTiming = FALSE; /* These globals are for creating the category tree interface */ struct trackDb *hgFindTdbList = NULL; struct grp *hgFindGrpList = NULL; struct dbDbHelper /* A struct dbDb with an extra field for whether the assembly is the default */ { struct dbDbHelper *next; struct dbDb *dbDb; boolean isDefault; boolean isCurated;; }; /* Helper functions for cart handlers */ static struct dbDbHelper *getDbDbWithDefault() /* Grab rows from dbDb along with whether assembly is the default or not */ { struct dbDbHelper *ret = NULL; struct sqlConnection *conn = hConnectCentral(); struct sqlResult *sr; char **row; struct dbDb *db; struct hash *hash = sqlHashOfDatabases(); char query[1024]; sqlSafef(query, sizeof query, "select dbDb.*,defaultDb.name from %s " "join %s on %s.genome=%s.genome where active=1 order by orderKey,dbDb.name desc", dbDbTable(), defaultDbTable(), dbDbTable(), defaultDbTable()); sr = sqlGetResult(conn, query); while ((row = sqlNextRow(sr)) != NULL) { struct dbDbHelper *this; db = dbDbLoad(row); boolean isGenarkHub = sameOk(db->nibPath, "genark"); boolean isCuratedHub = startsWith("hub:", db->nibPath); if (isGenarkHub || isCuratedHub || hashLookup(hash, db->name)) { AllocVar(this); this->dbDb = db; this->isDefault = sameString(row[14],row[0]); this->isCurated = isCuratedHub; slAddTail(&ret, this); } else dbDbFree(&db); } sqlFreeResult(&sr); hashFree(&hash); hDisconnectCentral(&conn); slReverse(&ret); return ret; } static struct trackDb *addSupers(struct trackDb *trackList) /* Insert supertracks into the hierarchy. */ { struct trackDb *newList = NULL; struct trackDb *tdb, *nextTdb; struct hash *superHash = newHash(5); for(tdb = trackList; tdb; tdb = nextTdb) { nextTdb = tdb->next; if (tdb->parent) { // part of a super track if (hashLookup(superHash, tdb->parent->track) == NULL) { hashStore(superHash, tdb->parent->track); slAddHead(&newList, tdb->parent); } slAddTail(&tdb->parent->subtracks, tdb); } else slAddHead(&newList, tdb); } slReverse(&newList); return newList; } static void hashTdbNames(struct trackDb *tdb, struct hash *trackHash) /* Store the track names for lookup, except for knownGene which gets its own special * category in the UI */ { struct trackDb *tmp; for (tmp = tdb; tmp != NULL; tmp = tmp->next) { if (tmp->subtracks) hashTdbNames(tmp->subtracks, trackHash); if (!sameString(tmp->table, tmp->track)) hashAdd(trackHash, tmp->track, tmp); hashAdd(trackHash, tmp->table, tmp); } } void hashTracksAndGroups(struct cart *cart, char *db) /* get the list of tracks available for this assembly, along with their group names * and visibility-ness. Note that this implicitly makes connected hubs show up * in the trackList struct, which means we get item search for connected * hubs for free */ { if (hgFindTdbList != NULL && hgFindGrpList != NULL) return; if (!hgFindTrackHash) hgFindTrackHash = hashNew(0); if (!hgFindGroupHash) hgFindGroupHash = hashNew(0); cartTrackDbInit(cart, &hgFindTdbList, &hgFindGrpList, FALSE); if (!hgFindTdbList) errAbort("Error getting tracks for %s", db); if (!hgFindGrpList) errAbort("Error getting groups for %s", db); struct trackDb *superList = addSupers(hgFindTdbList); hgFindTdbList = superList; hashTdbNames(superList, hgFindTrackHash); struct grp *g; for (g = hgFindGrpList; g != NULL; g = g->next) if (!sameString(g->name, "allTracks") && !sameString(g->name, "allTables")) hashStore(hgFindGroupHash, g->name); } struct hgPositions *doQuery(struct jsonWrite *jw, char *db, struct searchCategory *categories, boolean doRedirect, char *searchTerms, boolean measureTiming) /* Fire off a query. If the query results in a single position and we came from another CGI, * inform the client that we can redirect right to hgTracks, otherwise write the * list as JSON. Return the results if we want to handle the redirect server * side later. */ { struct hgPositions *hgp = NULL; char *chrom; int retWinStart = 0, retWinEnd = 0; boolean categorySearch = TRUE; hgp = genomePosCJ(jw, db, searchTerms, &chrom, &retWinStart, &retWinEnd, cart, categories, categorySearch); // at this point, if the search term wasn't a singlePos, we have written // out the JSON already. So now take care of the singlePos case. if (hgp && hgp->singlePos) { // if we got an hgvs match to chromInfo (example: chrX:g.31500000_31600000del), // or just a plain position range was searched, we have to create the json // manually, cause the tdb lookups in hgPositionsJson() won't work struct hgPosTable *table = hgp->tableList; jsonWriteListStart(jw, "positionMatches"); jsonWriteObjectStart(jw, NULL); jsonWriteString(jw, "name", table->name); jsonWriteString(jw, "description", table->description); if (table->searchTime >= 0) jsonWriteNumber(jw, "searchTime", table->searchTime); jsonWriteListStart(jw, "matches"); jsonWriteObjectStart(jw, NULL); char position[512]; retWinStart = hgp->singlePos->chromStart; retWinEnd = hgp->singlePos->chromEnd; if (hgp->singlePos->chromStart > hgp->singlePos->chromEnd) { retWinStart = retWinEnd; retWinEnd = hgp->singlePos->chromStart; } safef(position, sizeof(position), "%s:%d-%d", hgp->singlePos->chrom, retWinStart+1, retWinEnd); jsonWriteString(jw, "position", position); jsonWriteString(jw, "posName", hgp->query); if (doRedirect) { // If we are coming from hgTracks or hgGateway, then we can just return right // back to hgTracks, the client will handle this: jsonWriteBoolean(jw, "doRedirect", doRedirect); } jsonWriteObjectEnd(jw); jsonWriteListEnd(jw); // end matches jsonWriteObjectEnd(jw); // end one table jsonWriteListEnd(jw); // end positionMatches } return hgp; } static void addCategoryFieldsToHash(struct hash *elementHash, struct searchCategory *category) { hashAdd(elementHash, "name", newJsonString(category->name)); hashAdd(elementHash, "id", newJsonString(category->id)); hashAdd(elementHash, "visibility", newJsonNumber((long)category->visibility)); hashAdd(elementHash, "group", newJsonString(category->groupName)); hashAdd(elementHash, "label", newJsonString(category->label)); hashAdd(elementHash, "description", newJsonString(category->description)); hashAdd(elementHash, "parents", newJsonString(slNameListToString(category->parents, ','))); hashAdd(elementHash, "priority", newJsonDouble(category->priority)); } static boolean nonTrackCateg(struct searchCategory *categ) { if (sameString("publicHubs", categ->id) || sameString("helpDocs", categ->id) || startsWith("trackDb", categ->id) || sameString("knownGene", categ->id)) return TRUE; return FALSE; } static struct jsonElement *jsonElementFromVisibleCategs(struct searchCategory *categs) { struct searchCategory *categ = NULL; struct jsonElement *ret = newJsonList(NULL); for (categ = categs; categ != NULL; categ = categ->next) { if (categ->visibility > 0) jsonListAdd(ret, newJsonString(categ->id)); } return ret; } struct searchCategory *getVisibleCategories(struct searchCategory *categories) /* Return a list of only the visible categories. Use CloneVar so our original list * stays alive */ { struct searchCategory *ret = NULL, *categ = NULL; for (categ = categories; categ != NULL; categ = categ->next) { if (categ->visibility > 0) { struct searchCategory *temp = (struct searchCategory *)CloneVar(categ); slAddHead(&ret, temp); } } return ret; } struct jsonElement *makeTrackGroupsJson(char *db) /* Turn the available track groups for a database into JSON. Needed for * sorting the categories */ { hashTracksAndGroups(cart, db); struct jsonElement *retObj = newJsonObject(hashNew(0)); struct grp *grp; for (grp = hgFindGrpList; grp != NULL; grp = grp->next) { if (!sameString(grp->name, "allTracks") && !sameString(grp->name, "allTables")) { struct jsonElement *grpObj = newJsonObject(hashNew(0)); jsonObjectAdd(grpObj, "name", newJsonString(grp->name)); jsonObjectAdd(grpObj, "label", newJsonString(grp->label)); jsonObjectAdd(grpObj, "priority", newJsonDouble(grp->priority)); jsonObjectAdd(retObj, grp->name, grpObj); } } return retObj; } static struct jsonElement *jsonElementFromSearchCategory(struct searchCategory *categories, char *db) /* Turn struct searchCategory into jsonElement */ { struct jsonElement *trackList = newJsonList(NULL); struct jsonElement *nonTrackList = newJsonList(NULL); struct searchCategory *categ = NULL; for (categ = categories; categ != NULL; categ = categ->next) { struct hash *eleHash = hashNew(0); struct jsonElement *categJson = newJsonObject(eleHash); addCategoryFieldsToHash(eleHash, categ); // now add to one of our final lists if (nonTrackCateg(categ)) { jsonListAdd(nonTrackList, categJson); } else { if (categ->id) jsonListAdd(trackList, categJson); } } // we need to enclose the track list categories in a parent category // for the javascript to function: struct jsonElement *trackContainerEle = newJsonObject(hashNew(0)); jsonObjectAdd(trackContainerEle, "id", newJsonString("trackData")); char name[2048]; safef(name, sizeof(name), "%s Track Data", db); jsonObjectAdd(trackContainerEle, "name", newJsonString(name)); jsonObjectAdd(trackContainerEle, "tracks", trackList); jsonObjectAdd(trackContainerEle, "label", newJsonString(name)); jsonObjectAdd(trackContainerEle, "priority", newJsonDouble(2.0)); jsonObjectAdd(trackContainerEle, "description", newJsonString("Search for track data items")); jsonListAdd(nonTrackList, trackContainerEle); return nonTrackList; } struct searchCategory *makeCategsFromJson(struct jsonElement *searchCategs, char *db) /* User has selected some categories, parse the JSON into a struct searchCategory */ { if (searchCategs == NULL) return getAllCategories(cart, db, hgFindGroupHash); struct searchCategory *ret = NULL; struct slRef *jsonVal = NULL; for (jsonVal= searchCategs->val.jeList; jsonVal != NULL; jsonVal = jsonVal->next) { struct jsonElement *val = jsonVal->val; if (val->type != jsonString) errAbort("makeCategsFromJson: non-string argument value for 'categs'"); char *categName = val->val.jeString; struct searchCategory *category = NULL; if (!sameString(categName, "allTracks")) category = makeCategory(cart, categName, NULL, db, hgFindGroupHash); else { struct hashEl *hel, *helList = hashElListHash(hgFindTrackHash); for (hel = helList; hel != NULL; hel = hel->next) { struct trackDb *tdb = hel->val; if (!sameString(tdb->track, "knownGene") && !sameString(tdb->table, "knownGene")) slAddHead(&category, makeCategory(cart, tdb->track, NULL, db, hgFindGroupHash)); } // the hgFindTrackHash can contain both a composite track (where makeCategory would make categories // for each of the subtracks, and a subtrack, where makeCategory just makes a single // category, which means our final list can contain duplicate categories, so do a // uniqify here so we only have one category for each category slUniqify(&category, cmpCategories, searchCategoryFree); } if (category != NULL) { if (ret) slCat(&ret, category); else ret = category; } } return ret; } void jsonElementSaveCategoriesToCart(char *db, struct jsonElement *jel) /* Write the currently selected categories to the cart so on back button navigation * we only search the selected categories. jel must be a jeList of strings */ { char cartSetting[512]; safef(cartSetting, sizeof(cartSetting), "hgSearch_categs_%s", db); struct dyString *cartVal = dyStringNew(0); struct slRef *categ; for (categ = jel->val.jeList; categ != NULL; categ = categ->next) { struct jsonElement *val = categ->val; if (val->type != jsonString) errAbort("saveCategoriesToCart: category is not a string"); else dyStringPrintf(cartVal, "%s,", val->val.jeString); } cartSetString(cart, cartSetting, dyStringCannibalize(&cartVal)); } static void writeDefaultForDb(struct jsonWrite *jw, char *database) /* Put up the default view when entering this page for the first time, which basically * means return the list of searchable stuff. */ { struct searchCategory *defaultCategories = getAllCategories(cart, database, hgFindGroupHash); struct jsonElement *categsJsonElement = jsonElementFromSearchCategory(defaultCategories, database); struct jsonElement *selectedCategsJsonList = jsonElementFromVisibleCategs(defaultCategories); jsonElementSaveCategoriesToCart(database,selectedCategsJsonList); dyStringPrintf(jw->dy, ", \"db\": \"%s\"", database); dyStringPrintf(jw->dy, ", \"categs\": "); jsonDyStringPrint(jw->dy, categsJsonElement, NULL, -1); dyStringPrintf(jw->dy, ", \"trackGroups\": "); jsonDyStringPrint(jw->dy, makeTrackGroupsJson(database), NULL, -1); } void printMainPageIncludes() { webIncludeResourceFile("gb.css"); webIncludeResourceFile("gbStatic.css"); webIncludeResourceFile("spectrum.min.css"); webIncludeResourceFile("hgGtexTrackSettings.css"); puts(""); puts(""); puts(""); puts(""); puts("\n"); jsIncludeFile("utils.js", NULL); jsIncludeFile("ajax.js", NULL); jsIncludeFile("lodash.3.10.0.compat.min.js", NULL); jsIncludeFile("cart.js", NULL); jsIncludeFile("hgSearch.js", NULL); // Write the skeleton HTML, which will get filled out by the javascript webIncludeFile("inc/hgSearch.html"); webIncludeFile("inc/gbFooter.html"); } /* End handler helper functions */ /* Handlers for returning JSON to client */ static void getSearchResults(struct cartJson *cj, struct hash *paramHash) /* User has entered a term to search, search this term against the selected categories */ { char *db = cartJsonRequiredParam(paramHash, "db", cj->jw, "getSearchResults"); cartSetString(cj->cart, "db", db); initGenbankTableNames(db); hashTracksAndGroups(cj->cart, db); char *searchTerms = cartJsonRequiredParam(paramHash, SEARCH_TERM_VAR, cj->jw, "getSearchResults"); measureTiming = cartUsualBoolean(cj->cart, "measureTiming", FALSE); struct jsonElement *searchCategs = hashFindVal(paramHash, "categs"); struct searchCategory *searchCategoryList = makeCategsFromJson(searchCategs, db); boolean doRedirect = FALSE; (void)doQuery(cj->jw, db, searchCategoryList, doRedirect, searchTerms, measureTiming); fprintf(stderr, "performed query on %s\n", searchTerms); } static void getUiState(struct cartJson *cj, struct hash *paramHash) /* We haven't seen this database before, return list of all searchable stuff */ { char *db = cartJsonRequiredParam(paramHash, "db", cj->jw, "getUiState"); cartSetString(cj->cart, "db", db); initGenbankTableNames(db); hashTracksAndGroups(cj->cart, db); writeDefaultForDb(cj->jw, db); } static struct jsonElement *getGenomes() /* Return a string that the javascript can use to put up a species and db select. */ { struct jsonElement *genomesObj = newJsonObject(hashNew(0)); struct dbDbHelper *localDbs = getDbDbWithDefault(); struct dbDbHelper *temp; struct hash *curatedHubs = hashNew(0); for (temp = localDbs; temp != NULL; temp = temp->next) { // fill out the dbDb fields into a struct struct jsonElement *genomeObj = newJsonObject(hashNew(0)); jsonObjectAdd(genomeObj, "organism", newJsonString(temp->dbDb->organism)); jsonObjectAdd(genomeObj, "name", newJsonString(temp->dbDb->name)); jsonObjectAdd(genomeObj, "genome", newJsonString(temp->dbDb->genome)); jsonObjectAdd(genomeObj, "description", newJsonString(temp->dbDb->description)); jsonObjectAdd(genomeObj, "orderKey", newJsonNumber(temp->dbDb->orderKey)); jsonObjectAdd(genomeObj, "isDefault", newJsonBoolean(temp->isDefault)); jsonObjectAdd(genomeObj, "isCurated", newJsonBoolean(temp->isCurated)); if (temp->isCurated) hashStore(curatedHubs, temp->dbDb->name); // finally add the dbDb object to the hash on species, either create a new // list if this is the first time seeing this species or append to the end // of the list to keep things in the right order struct jsonElement *genomeList = NULL; if ( (genomeList = jsonFindNamedField(genomesObj, temp->dbDb->genome, temp->dbDb->genome)) != NULL) jsonListAdd(genomeList, genomeObj); else { genomeList = newJsonList(NULL); jsonListAdd(genomeList, genomeObj); jsonObjectAdd(genomesObj, temp->dbDb->genome, genomeList); } } struct dbDb *trackHubDbs = trackHubGetDbDbs(NULL); struct dbDb *dbDb; for (dbDb = trackHubDbs; dbDb != NULL; dbDb = dbDb->next) { // if this is a curated hub, don't treat it like other track hubs if (hashLookup(curatedHubs, trackHubSkipHubName(dbDb->name)) != NULL) continue; // fill out the dbDb fields into a struct struct jsonElement *genomeObj = newJsonObject(hashNew(0)); jsonObjectAdd(genomeObj, "organism", newJsonString(dbDb->organism)); jsonObjectAdd(genomeObj, "name", newJsonString(dbDb->name)); jsonObjectAdd(genomeObj, "genome", newJsonString(dbDb->genome)); jsonObjectAdd(genomeObj, "description", newJsonString(dbDb->description)); // finally add the dbDb object to the hash on species, either create a new // list if this is the first time seeing this species or append to the end // of the list to keep things in the right order struct jsonElement *genomeList = NULL; if ( (genomeList = jsonFindNamedField(genomesObj, dbDb->genome, dbDb->genome)) != NULL) jsonListAdd(genomeList, genomeObj); else { genomeList = newJsonList(NULL); jsonListAdd(genomeList, genomeObj); jsonObjectAdd(genomesObj, dbDb->genome, genomeList); } } return genomesObj; } static void getChromName(struct cartJson *cj, struct hash *paramHash) /* Check if search term is a valid chromosome name, if so return the * UCSC approved chromsome name for easy redirect to hgTracks/CGI. If * no chromosome name return chromName: null and let the user handle it */ { // we need to connect to database explicitly to handle hub chromosomes (hs1): char *database = NULL, *genome = NULL; getDbAndGenome(cj->cart, &database, &genome, oldVars); char *term = cartJsonRequiredParam(paramHash, "searchTerm", cj->jw, "getChromName"); chromAliasSetup(database); char *chromName = NULL; // wrap this lookup in an errCatch so we can silently return // a null chromosome name if there was a problem. This is meant // for quick look ups so if a term is not a chrom name it's not // a big deal struct errCatch *errCatch = errCatchNew(); if (errCatchStart(errCatch)) chromName = hgOfficialChromName(database, term); errCatchEnd(errCatch); // whoever consumes this json can deal with a NULL chromName jsonWriteString(cj->jw, "chromName", chromName); } /* End Handlers */ /* Start do commands, functions that dispatch */ void doSaveCategoriesToCart(struct cartJson *cj, struct hash *paramHash) /* Save any selected categories to the cart so if the user clicks the back button * from hgTracks/hgTrackUi/etc we know what they were last looking at */ { char *db = cartUsualString(cj->cart, "db", hDefaultDb()); struct jsonElement *jel = hashFindVal(paramHash, "categs"); if (jel == NULL) { jsonWriteStringf(cj->jw, "error", "saveCategoriesToCart: required param 'categs' is missing"); return; } jsonElementSaveCategoriesToCart(db,jel); } void doCartJson() /* Register functions that return JSON to client */ { struct cartJson *cj = cartJsonNew(cart); cartJsonRegisterHandler(cj, "getSearchResults", getSearchResults); cartJsonRegisterHandler(cj, "getUiState", getUiState); cartJsonRegisterHandler(cj, "saveCategoriesToCart", doSaveCategoriesToCart); cartJsonRegisterHandler(cj, "getChromName", getChromName); cartJsonExecute(cj); } void doMainPage() /* Print the basic HTML page and include any necessary Javascript. AJAX calls * will fill out the page later */ { char *database = NULL; char *genome = NULL; getDbAndGenome(cart, &database, &genome, oldVars); webStartGbNoBanner(cart, database, "Search Disambiguation"); printMainPageIncludes(); jsInlineF("var hgsid='%s';\n", cartSessionId(cart)); struct jsonElement *cartJson = newJsonObject(hashNew(0)); jsonObjectAdd(cartJson, "db", newJsonString(database)); jsonObjectAdd(cartJson, "genomes", getGenomes()); struct dyString *cartJsonDy = dyStringNew(0); jsonDyStringPrint(cartJsonDy, cartJson, "cartJson", -1); jsInlineF("%s;\n", dyStringCannibalize(&cartJsonDy)); // Call our init function to fill out the page jsInline("hgSearch.init();\n"); webEndGb(); } void doSearchOnly() /* Send back search results along with whatever we need to make the UI */ { char *db = NULL; char *genome = NULL; getDbAndGenome(cart, &db, &genome, oldVars); char *userSearch = cartCgiUsualString(cart, "search", NULL); if (userSearch == NULL || isEmpty(userSearch)) { doMainPage(); return; } initGenbankTableNames(db); hashTracksAndGroups(cart, db); struct searchCategory *allCategories = getAllCategories(cart, db, hgFindGroupHash); struct jsonElement *categsJsonElement = jsonElementFromSearchCategory(allCategories, db); struct cartJson *cj = cartJsonNew(cart); // since we are coming directly from hgTracks/hgGateway (or a URL manipulation), // if the search would normally be a singlePos, like a chromosome name or HGVS search, // we can just go directly there. But if we aren't a singlePos, we need to show // the results page boolean doRedirect = TRUE; measureTiming = cartUsualBoolean(cart, "measureTiming", FALSE); -(void)doQuery(cj->jw, db, allCategories, doRedirect, userSearch, measureTiming); -/* This is an option to do an automatic redirect but I'm commenting it - * out for now, as this page suggests the refresh option is not helpful - * for visually impaired users: - * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta - * For now the client javascript can handle the redirect +struct hgPositions *hgp = doQuery(cj->jw, db, allCategories, doRedirect, userSearch, measureTiming); +// Since we are coming from another CGI or a URL manip, go directly to hgTracks +// if we resolve to a single position if (hgp && hgp->singlePos) { char newPosBuf[128]; safef(newPosBuf, sizeof(newPosBuf), "%s:%d-%d", hgp->singlePos->chrom, hgp->singlePos->chromStart+1, hgp->singlePos->chromEnd); cartSetString(cj->cart, "position", newPosBuf); if (hgp->singlePos->highlight) cartSetString(cj->cart, "addHighlight", hgp->singlePos->highlight); puts("Content-type:text/html\n"); puts("\n\n"); - hPrintf("\n", db, newPosBuf); + printf("\n", db, newPosBuf); puts("\n"); } else -*/ + { dyStringPrintf(cj->jw->dy, ", \"db\": '%s'", db); dyStringPrintf(cj->jw->dy, ", \"categs\": "); jsonDyStringPrint(cj->jw->dy, categsJsonElement, NULL,-1); dyStringPrintf(cj->jw->dy, ", \"trackGroups\": "); jsonDyStringPrint(cj->jw->dy, makeTrackGroupsJson(db), NULL, -1); dyStringPrintf(cj->jw->dy, ", \"genomes\": "); jsonDyStringPrint(cj->jw->dy, getGenomes(), NULL, -1); // Now we need to actually spit out the page + json webStartGbNoBanner(cart, db, "Search Disambiguation"); printMainPageIncludes(); jsInlineF("var hgsid='%s';\n", cartSessionId(cart)); jsInline("var cartJson = {"); jsInline(cj->jw->dy->string); jsInline("};\n"); jsInline("hgSearch.init();\n"); webEndGb(); } +} /* End do commands */ void doMiddle(struct cart *theCart) /* Set up globals and make web page */ { cart = theCart; if (cgiOptionalString(CARTJSON_COMMAND)) doCartJson(); else if (cgiOptionalString("search")) // we got here from: // 1. changing the URL arguments // 2. a back button reload // regardless, we can just put up the whole page with search results already // included in the returned json doSearchOnly(); else doMainPage(); } /* Null terminated list of CGI Variables we don't want to save * permanently. */ char *excludeVars[] = {"Submit", "submit", NULL,}; int main(int argc, char *argv[]) /* Process command line. */ { cgiSpoof(&argc, argv); cartEmptyShellNoContent(doMiddle, hUserCookie(), excludeVars, oldVars); return 0; }