845dfcdd54bdf5cbda67a70b7b3946f2892104ea
max
Fri Jun 24 08:25:12 2022 -0700
changing copy icon as per QA, refs #29583
diff --git src/hg/hgHubConnect/hgHubConnect.c src/hg/hgHubConnect/hgHubConnect.c
index c79d6c8..f0f3c0f 100644
--- src/hg/hgHubConnect/hgHubConnect.c
+++ src/hg/hgHubConnect/hgHubConnect.c
@@ -1,1692 +1,1692 @@
/* hgHubConnect - the CGI web-based program to select track data hubs to connect with. */
/* Copyright (C) 2014 The Regents of the University of California
* See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */
#include "common.h"
#include "hash.h"
#include "linefile.h"
#include "errAbort.h"
#include "errCatch.h"
#include "hCommon.h"
#include "dystring.h"
#include "jksql.h"
#include "cheapcgi.h"
#include "htmshell.h"
#include "hdb.h"
#include "hui.h"
#include "cart.h"
#include "dbDb.h"
#include "web.h"
#include "trackHub.h"
#include "hubConnect.h"
#include "dystring.h"
#include "hPrint.h"
#include "jsHelper.h"
#include "obscure.h"
#include "hgConfig.h"
#include "trix.h"
#include "net.h"
#include "hubSearchText.h"
#include "pipeline.h"
#include "hubPublic.h"
static boolean measureTiming;
struct cart *cart; /* The user's ui state. */
struct hash *oldVars = NULL;
static char *pageTitle = "Track Data Hubs";
char *database = NULL;
char *organism = NULL;
struct hubOutputStructure
{
struct hubOutputStructure *next;
struct dyString *metaTags;
struct dyString *descriptionMatch;
struct genomeOutputStructure *genomes;
int genomeCount;
struct hash *genomeOutHash;
};
struct genomeOutputStructure
{
struct genomeOutputStructure *next;
struct dyString *shortLabel;
struct dyString *metaTags;
struct dyString *descriptionMatch;
struct tdbOutputStructure *tracks;
struct dyString *assemblyLink;
char *genomeName;
char *positionString;
int trackCount;
struct hash *tdbOutHash;
int hitCount;
};
struct tdbOutputStructure
{
struct tdbOutputStructure *next;
struct dyString *shortLabel;
struct dyString *metaTags;
struct dyString *descriptionMatch;
struct dyString *configUrl;
struct tdbOutputStructure *children;
int childCount;
};
static void ourCellStart()
{
fputs("
", stdout); // do not add a newline
}
static void ourCellEnd()
{
puts("
");
}
static void ourPrintCellLink(char *str, char *url)
{
ourCellStart();
printf("\n", url);
if (str != NULL)
fputs(str, stdout); // do not add a newline -- was causing trailing blanks get copied in cut and paste
puts("");
ourCellEnd();
}
static void ourPrintCell(char *str)
{
ourCellStart();
if (str != NULL)
fputs(str, stdout); // do not add a newline -- was causing trailing blanks get copied in cut and paste
ourCellEnd();
}
static char *removeLastComma(char *string)
{
if (string != NULL)
{
int len = strlen(string);
if ( string[len - 1] == ',')
string[len - 1] = 0;
else if (len > 2 && endsWith(string,", "))
string[len - 2] = 0;
}
return string;
}
#define GENLISTWIDTH 40
static void printGenomeList(char *hubUrl, struct slName *genomes, int row, boolean withLink, boolean withPaste)
/* print supported assembly names from sl list */
{
struct dyString *dyHtml = dyStringNew(1024);
struct dyString *dyShortHtml = dyStringNew(1024);
// create two strings: one shortened to GENLISTWIDTH characters
// and another one with all genomes
int charCount = 0;
struct slName *genome = genomes;
for(; genome; genome = genome->next)
{
char *trimmedName = trackHubSkipHubName(genome->name);
char *shortName = cloneString(trimmedName);
// If even the first element is too long, truncate its short name.
if (genome==genomes && strlen(trimmedName) > GENLISTWIDTH)
shortName[GENLISTWIDTH] = 0;
// append to dyShortHtml if necessary
if (charCount == 0 || (charCount+strlen(trimmedName)<=GENLISTWIDTH))
{
if (withLink)
{
dyStringPrintf(dyShortHtml,"%s" , genome->name, hubUrl, genome->name, shortName);
// https://hgdownload-test.gi.ucsc.edu/hubs/GCA/009/914/755/GCA_009914755.4/hub.txt
if (withPaste)
{
dyStringPrintf(dyShortHtml,"" , hubUrl);
- dyStringAppend(dyShortHtml, "");
+ dyStringAppend(dyShortHtml, "");
}
}
else
dyStringPrintf(dyShortHtml,"%s" , shortName);
dyStringPrintf(dyShortHtml,", ");
}
freeMem(shortName);
charCount += strlen(trimmedName);
// always append to dyHtml
if (withLink)
dyStringPrintf(dyHtml,"%s" , genome->name, hubUrl, genome->name, trimmedName);
else
dyStringPrintf(dyHtml,"%s" , trimmedName);
if (genome->next)
{
dyStringPrintf(dyHtml,", ");
}
}
char *longHtml = dyStringCannibalize(&dyHtml);
char *shortHtml = dyStringCannibalize(&dyShortHtml);
shortHtml = removeLastComma(shortHtml);
if (charCount < GENLISTWIDTH)
ourPrintCell(shortHtml);
else
{
char id[256];
char tempHtml[1024+strlen(longHtml)+strlen(shortHtml)];
safef(tempHtml, sizeof tempHtml,
"[+] %s..."
"[-] %s"
, row, row, shortHtml
, row, row, longHtml);
safef(id, sizeof id, "Short%dPlus", row);
jsOnEventByIdF("click", id,
"document.getElementById('Short%d').style.display='none';"
"document.getElementById('Full%d').style.display='inline';"
"return false;"
, row, row);
safef(id, sizeof id, "Full%dMinus", row);
jsOnEventByIdF("click", id,
"document.getElementById('Full%d').style.display='none';"
"document.getElementById('Short%d').style.display='inline';"
"return false;"
, row, row);
ourPrintCell(tempHtml);
}
freeMem(longHtml);
freeMem(shortHtml);
}
static void printGenomes(struct trackHub *thub, int row, boolean withLink)
/* print supported assembly names from trackHub */
{
/* List of associated genomes. */
struct trackHubGenome *genomes = thub->genomeList;
struct slName *list = NULL, *el;
for(; genomes; genomes = genomes->next)
{
el = slNameNew(genomes->name);
slAddHead(&list, el);
}
slReverse(&list);
printGenomeList(thub->url, list, row, withLink, TRUE);
}
static void hgHubConnectUnlisted(struct hubConnectStatus *hubList,
struct hash *publicHash)
/* Put up the list of unlisted hubs and other controls for the page. */
/* uses publicHash to distingusih public hubs from unlisted ones */
/* NOTE: Destroys hubList */
{
// put out the top of our page
puts("
\n"
"
");
printf(""); // return from within DIV and FROM is probably not a good idea
puts("
"); // tabSection and .unlistedHubs
return;
}
// time to output the big table. First the header
puts(
"
"
"
Display
"
"
Hub Name
"
"
Description
"
"
AssembliesClick to connect and browse directly. Click copy icon to copy URL to clipboard for sharing.
"
"
\n"
"\n");
// start first row
printf("");
char id[256];
int count = 0;
for(hub = unlistedHubList; hub; hub = hub->next)
{
char hubName[64];
safef(hubName, sizeof(hubName), "%s%u", hgHubConnectHubVarPrefix, hub->id);
if (!cartUsualBoolean(cart, hubName, FALSE))
continue;
if (count)
webPrintLinkTableNewRow(); // ends last row and starts a new one
count++;
puts("
");
ourCellStart();
safef(id, sizeof id, "hubDisconnectButtonU%d", count);
printf("\n", id);
jsOnEventByIdF("click", id,
"document.disconnectHubForm.elements['hubId'].value='%d';"
"document.disconnectHubForm.submit(); return true;", hub->id);
ourCellEnd();
if (hub->trackHub != NULL)
{
ourPrintCellLink(hub->trackHub->shortLabel, hub->hubUrl);
}
else
ourPrintCell("");
boolean hubHasError = (!isEmpty(hub->errorMessage));
if (hubHasError)
{
ourCellStart();
printf("ERROR: %s "
"Debug Help\n",
hub->errorMessage);
safef(id, sizeof id, "hubClearButton%d", count);
// give people a chance to clear the error
printf(""
, id);
jsOnEventByIdF("click", id,
"document.resetHubForm.elements['hubCheckUrl'].value='%s';"
"document.resetHubForm.submit(); return true;", hub->hubUrl);
ourCellEnd();
}
else if (hub->trackHub != NULL)
{
if (hub->trackHub->descriptionUrl != NULL)
ourPrintCellLink(hub->trackHub->longLabel, hub->trackHub->descriptionUrl);
else
ourPrintCell(hub->trackHub->longLabel);
}
else
ourPrintCell("");
if (hub->trackHub != NULL)
printGenomes(hub->trackHub, count, !hubHasError);
else
ourPrintCell("");
puts("
");
}
printf("\n");
puts("");
printf(""); // .tabSection
printf(""); // #unlistedHubs
}
int doValidateNewHub(char *hubUrl)
/* Run hubCheck on a hub. */
{
int ret = 0;
udcSetCacheTimeout(1);
char *expireTime = cfgOptionDefault("browser.cgiExpireMinutes", "20");
unsigned expireMinutes = sqlUnsigned(expireTime);
int hubCheckTimeout = (expireMinutes - 1) * 60 > 0 ? (expireMinutes - 1) * 60 : 60;
printf("");
char *cmd[] = {"loader/hubCheck", "-htmlOut", "-noTracks", hubUrl, NULL};
struct errCatch *errCatch = errCatchNew();
if (errCatchStart(errCatch))
{
struct pipeline *pl = pipelineOpen1(cmd, pipelineRead | pipelineNoAbort, NULL, NULL, hubCheckTimeout);
struct lineFile *lf = pipelineLineFile(pl);
char *line;
while (lineFileNext(lf, &line, NULL))
jsInlineF("%s", line);
pipelineClose(&pl);
// the 'false' below prevents a few hub-search specific jstree configuration options
jsInline("hubSearchTree.init(false);");
}
errCatchEnd(errCatch);
if (errCatch->gotError || errCatch->gotWarning)
{
printf("hubCheck timed out after running for %d minute%s. Please try on a Unix command line", hubCheckTimeout / 60, hubCheckTimeout/60 > 1 ? "s" : "");
ret = 1;
}
errCatchFree(&errCatch);
return ret;
}
void hgHubConnectDeveloperMode()
/* Put up the controls for the "Hub Development" Tab, which includes a button to run the
* hubCheck utility on a hub and load a hub with the udcTimeout and measureTiming
* variables turned on */
{
// put out the top of our page
char *hubUrl = cartOptionalString(cart, "validateHubUrl");
// the outer div for all the elements in the tab
puts("
\n"
" You may also contact us if you have any "
"issues or questions on hub development.");
puts("
"); // .tabSection
puts("
");
puts("
Check a hub for errors
");
printf("");
printf("\n", hubUrlVal);
printf("\n");
printf("  Load Example URL\n");
puts("
Use the URL bar above to check a hub for errors. This will "
"validate the hub's configuration files, including hub.txt, "
"genomes.txt and trackDb.txt. "
"It will also present a hierarchical tree of tracks with any errors in red. A hub "
"with no errors still shows the tree which can be used to explore the track hierarchy. "
"Hub error checking will always refresh the files and never use our remote file cache (see below)."
"
\n "
);
puts("
"); // .tabSection
puts("
");
puts("
Enable Genome Browser debugging modes
");
puts("
These apply to all connected hubs. By default, caching is activated and track load times are not shown, but you can change these settings while developing your hub:
");
puts("
");
puts("
"); // margin-left
puts("
"); // tabSection
puts("
"); // #hubDeveloper
jsOnEventById("click", "hubValidateButton", "makeIframe(event)");
}
void printSearchAndFilterBoxes(int searchEnabled, char *hubSearchTerms, char *dbFilter)
/* Create the text boxes for search and database filtering along with the required
* javscript */
{
printf("");
}
void printSearchTerms(char *hubSearchTerms)
/* Write out a reminder about the current search terms and a note about
* how to navigate detailed search results */
{
printf("Displayed list restricted by search terms: %s\n", hubSearchTerms);
puts("\n");
jsOnEventById("click", "hubDeleteSearchButton",
"document.searchHubForm.elements['hubSearchTerms'].value='';"
"document.searchHubForm.elements['hubDbFilter'].value='';"
"document.searchHubForm.submit();return true;");
puts("
\n");
printf("When exploring the detailed search results for a hub, you may right-click "
"on an assembly or track line to open it in a new window.\n");
puts("
\n");
}
void printHubListHeader()
/* Write out the header for a list of hubs in its own table */
{
puts("
"
"
"
"
Display
"
"
Hub Name
"
"
Description
"
//"
Assemblies
"
"
AssembliesClick to connect and browse directly
"
"
");
}
void outputPublicTableRow(struct hubEntry *hubInfo, int count)
/* Prints out a table row with basic information about a hub and a button
* to connect to that hub */
{
int id = hubInfo->id;
char jsId[256];
struct slName *dbListNames = slNameListFromComma(hubInfo->dbList);
printf("
\n");
if (id != 0)
{
ourCellStart();
char hubName[32];
safef(hubName, sizeof(hubName), "%s%u", hgHubConnectHubVarPrefix, id);
if (cartUsualBoolean(cart, hubName, FALSE))
{
safef(jsId, sizeof jsId, "hubDisconnectButtonP%d", count);
printf("\n", jsId);
jsOnEventByIdF("click", jsId,
"document.disconnectHubForm.elements['hubId'].value= '%d';"
"document.disconnectHubForm.submit();return true;", id);
}
else
{
// get first name off of list of supported databases
char * name = dbListNames->name;
// if the name isn't currently loaded, we assume it's a hub
if (!hDbExists(name))
{
char buffer[512];
safef(buffer, sizeof buffer, "hub_%d_%s", id, name);
name = cloneString(buffer);
}
safef(jsId, sizeof jsId, "hubConnectButton%d", count);
printf("\n", jsId);
jsOnEventByIdF("click", jsId,
"document.connectHubForm.elements['hubUrl'].value= '%s';"
"document.connectHubForm.elements['db'].value= '%s';"
"document.connectHubForm.submit();return true;", hubInfo->hubUrl,name);
}
ourCellEnd();
}
else
errAbort("cannot get id for hub with url %s\n", hubInfo->hubUrl);
ourPrintCellLink(hubInfo->shortLabel, hubInfo->hubUrl);
boolean hubHasNoError = isEmpty(hubInfo->errorMessage);
if (hubHasNoError)
{
if (hubInfo->tableHasDescriptionField && !isEmpty(hubInfo->descriptionUrl))
ourPrintCellLink(hubInfo->longLabel, hubInfo->descriptionUrl);
else
ourPrintCell(hubInfo->longLabel);
}
else
{
ourCellStart();
printf("ERROR: %s "
"Debug Help",
hubInfo->errorMessage);
safef(jsId, sizeof jsId, "hubClearButton%d", count);
printf(
""
, jsId);
jsOnEventByIdF("click", jsId,
"document.resetHubForm.elements['hubCheckUrl'].value='%s';"
"document.resetHubForm.submit();return true;", hubInfo->hubUrl);
ourCellEnd();
}
printGenomeList(hubInfo->hubUrl, dbListNames, count, hubHasNoError, FALSE);
printf("
\n");
}
struct trackHub *fetchTrackHub(struct hubEntry *hubInfo)
/* Fetch the hub structure for a public hub, trapping the error
* if the hub cannot be reached */
{
struct errCatch *errCatch = errCatchNew();
struct trackHub *hub = NULL;
if (errCatchStart(errCatch))
{
char hubName[4096];
safef(hubName, sizeof(hubName), "hub_%d", hubInfo->id);
hub = trackHubOpen(hubInfo->hubUrl, hubName);
}
errCatchEnd(errCatch);
if (errCatch->gotError)
{
fprintf(stderr, "%s\n", errCatch->message->string);
}
errCatchFree(&errCatch);
return hub;
}
char *getPositionStringForDb(struct trackHubGenome *genome)
{
char positionVar[1024];
safef(positionVar, sizeof(positionVar), "position.%s", genome->name);
char *position = cartOptionalString(cart, positionVar);
if (position == NULL)
{
struct dyString *tmp = dyStringCreate("position=");
if (genome->defaultPos != NULL)
dyStringAppend(tmp, genome->defaultPos);
else
dyStringAppend(tmp, hDefaultPos(genome->name)); // memory leak from hDefaultPos return value
position = dyStringCannibalize(&tmp);
}
return position;
}
struct tdbOutputStructure *hstToTdbOutput(struct hubSearchText *hst, struct genomeOutputStructure *genomeOut, struct trackHub *hub)
/* Convert a hubSearchText entry to a (list of) tdbOutputStructure(s) */
{
struct tdbOutputStructure *tdbOut = hashFindVal(genomeOut->tdbOutHash, hst->track);
if (tdbOut == NULL)
{
genomeOut->trackCount++;
AllocVar(tdbOut);
tdbOut->shortLabel = dyStringNew(0);
tdbOut->metaTags = dyStringNew(0);
tdbOut->descriptionMatch = dyStringNew(0);
tdbOut->configUrl = dyStringNew(0);
dyStringPrintf(tdbOut->shortLabel, "%s", hst->label);
char *hubId = hubNameFromUrl(hub->url);
if (isNotEmpty(hst->parents))
{
// hst->parents is a comma-sep list like "track1","track1Label","track2","track2Label"
int i;
int parentCount;
int parentTypesCount;
char *parentTrack = NULL;
char *parentLabel = NULL;
char *parentTrackLabels[16]; // 2 slots per parent, can tracks nest more than 8 deep?
char *parentTypes[16]; // the types of parents, "comp", "super", "view", "other" for each track in parentTrackLabels
struct tdbOutputStructure *parentTdbOut = NULL;
struct tdbOutputStructure *savedParent = NULL;
parentCount = chopByCharRespectDoubleQuotes(cloneString(hst->parents), ',', parentTrackLabels, sizeof(parentTrackLabels));
parentTypesCount = chopCommas(cloneString(hst->parentTypes), parentTypes);
if (parentCount == 0 || parentCount % 2 != 0)
{
errAbort("error parsing hubSearchText->parents for %s.%s in hub: '%s'",
genomeOut->genomeName, hst->track, hub->url);
}
if (parentTypesCount != (parentCount / 2))
{
errAbort("error parsing hubSearchText->parentTypes: '%s' for %s.%s in hub: '%s'",
hst->parentTypes, genomeOut->genomeName, hst->track, hub->url);
}
boolean foundParent = FALSE;
boolean doAddSaveParent = FALSE;
for (i = 0; i < parentCount; i += 2)
{
parentTrack = stripEnclosingDoubleQuotes(cloneString(parentTrackLabels[i]));
parentLabel = stripEnclosingDoubleQuotes(cloneString(parentTrackLabels[i+1]));
// wait until the first valid trackui page for the track hit
if (isEmpty(dyStringContents(tdbOut->configUrl)) && sameString(parentTypes[i/2], "comp"))
{
dyStringPrintf(tdbOut->configUrl, "../cgi-bin/hgTrackUi?hubUrl=%s&db=%s&g=%s_%s&hgsid=%s&%s",
hub->url, genomeOut->genomeName, hubId, parentTrack, cartSessionId(cart),
genomeOut->positionString);
}
else if (isEmpty(dyStringContents(tdbOut->configUrl)) && sameString(parentTypes[i/2], "super"))
{
dyStringPrintf(tdbOut->configUrl, "../cgi-bin/hgTrackUi?hubUrl=%s&db=%s&g=%s_%s&hgsid=%s&%s",
hub->url, genomeOut->genomeName, hubId, hst->track, cartSessionId(cart),
genomeOut->positionString);
}
parentTdbOut = hashFindVal(genomeOut->tdbOutHash, parentTrack);
if (parentTdbOut == NULL)
{
AllocVar(parentTdbOut);
parentTdbOut->shortLabel = dyStringNew(0);
parentTdbOut->metaTags = dyStringNew(0);
parentTdbOut->descriptionMatch = dyStringNew(0);
parentTdbOut->configUrl = dyStringNew(0);
// views will be in the parent list, but the &g parameter to trackUi should be the view's parent
if (sameString(parentTypes[(i/2)], "view"))
dyStringPrintf(parentTdbOut->configUrl,
"../cgi-bin/hgTrackUi?hubUrl=%s&db=%s&g=%s_%s&hgsid=%s&%s",
hub->url, genomeOut->genomeName, hubId, stripEnclosingDoubleQuotes(parentTrackLabels[(i/2)+2]), cartSessionId(cart), genomeOut->positionString);
else // everything else has the correct &g param
dyStringPrintf(parentTdbOut->configUrl,
"../cgi-bin/hgTrackUi?hubUrl=%s&db=%s&g=%s_%s&hgsid=%s&%s",
hub->url, genomeOut->genomeName, hubId, parentTrack, cartSessionId(cart), genomeOut->positionString);
dyStringPrintf(parentTdbOut->shortLabel, "%s", parentLabel);
parentTdbOut->childCount += 1;
if (savedParent)
slAddHead(&(parentTdbOut->children), savedParent);
else
slAddHead(&(parentTdbOut->children), tdbOut);
savedParent = parentTdbOut;
doAddSaveParent = TRUE;
hashAdd(genomeOut->tdbOutHash, parentTrack, parentTdbOut);
}
else
{
foundParent = TRUE; // don't add this track to the genomeOut->tracks hash again
if (savedParent && doAddSaveParent)
{
parentTdbOut->childCount += 1;
slAddHead(&(parentTdbOut->children), savedParent);
}
else if (!savedParent)
{
parentTdbOut->childCount += 1;
slAddHead(&(parentTdbOut->children), tdbOut);
}
savedParent = parentTdbOut;
doAddSaveParent = FALSE;
}
}
if (!foundParent)
{
slAddHead(&(genomeOut->tracks), parentTdbOut);
}
}
else
{
dyStringPrintf(tdbOut->configUrl, "../cgi-bin/hgTrackUi?hubUrl=%s&db=%s&g=%s_%s&hgsid=%s&%s",
hub->url, genomeOut->genomeName, hubId, hst->track, cartSessionId(cart),
genomeOut->positionString);
slAddHead(&(genomeOut->tracks), tdbOut);
}
hashAdd(genomeOut->tdbOutHash, hst->track, tdbOut);
}
return tdbOut;
}
struct hubOutputStructure *buildHubSearchOutputStructure(struct trackHub *hub,
struct hubSearchText *searchResults)
/* Build a structure that contains the data for writing out the hub search results for this hub */
{
struct hash *missingGenomes = hashNew(0);
struct hubOutputStructure *hubOut = NULL;
AllocVar(hubOut);
hubOut->metaTags = dyStringNew(0);
hubOut->descriptionMatch = dyStringNew(0);
hubOut->genomeOutHash = newHash(5);
struct hubSearchText *hst = NULL;
for (hst = searchResults; hst != NULL; hst = hst->next)
{
if (isEmpty(hst->db))
{
// must be a hit to the hub itself, not an assembly or track within it
if (hst->textLength == hubSearchTextLong)
{
dyStringPrintf(hubOut->descriptionMatch, "%s", hst->text);
}
else if (hst->textLength == hubSearchTextMeta)
{
if (isNotEmpty(dyStringContents(hubOut->metaTags)))
dyStringPrintf(hubOut->metaTags, ", %s", hst->text);
else
dyStringPrintf(hubOut->metaTags, "%s", hst->text);
}
continue;
}
char *db = cloneString(hst->db);
if (hashLookup(missingGenomes, db) != NULL)
continue;
struct trackHubGenome *genome = hashFindVal(hub->genomeHash, db);
if (genome == NULL)
{
// assembly hub genomes are stored with a prefix; try that
char withHubName[4096];
safef(withHubName, sizeof(withHubName), "%s_%s", hub->name, db);
genome = hashFindVal(hub->genomeHash, withHubName);
if (genome == NULL)
{
hashStoreName(missingGenomes, db);
warn("Error: Unable to find info for matching assembly '%s'. Skipping ...\n", withHubName);
continue;
}
}
struct genomeOutputStructure *genomeOut = hashFindVal(hubOut->genomeOutHash, db);
if (genomeOut == NULL)
{
AllocVar(genomeOut);
genomeOut->tdbOutHash = newHash(5);
genomeOut->metaTags = dyStringNew(0);
genomeOut->descriptionMatch = dyStringNew(0);
genomeOut->shortLabel = dyStringNew(0);
genomeOut->assemblyLink = dyStringNew(0);
genomeOut->positionString = getPositionStringForDb(genome);
dyStringPrintf(genomeOut->assemblyLink, "../cgi-bin/hgTracks?hubUrl=%s&db=%s&hgsid=%s&%s",
hub->url, genome->name, cartSessionId(cart), genomeOut->positionString);
char *name = trackHubSkipHubName(genome->name);
if (isNotEmpty(genome->description))
dyStringPrintf(genomeOut->shortLabel, "%s (%s)", genome->description, name);
else if (isNotEmpty(genome->organism))
dyStringPrintf(genomeOut->shortLabel, "%s %s", genome->organism, name);
else
dyStringPrintf(genomeOut->shortLabel, "%s", name);
genomeOut->genomeName = cloneString(genome->name);
hashAdd(hubOut->genomeOutHash, db, genomeOut);
slAddTail(&(hubOut->genomes), genomeOut);
hubOut->genomeCount++;
}
if (isEmpty(hst->track))
{
if (hst->textLength == hubSearchTextLong) // Genome description match
dyStringPrintf(genomeOut->descriptionMatch, "%s", hst->text);
else if (hst->textLength == hubSearchTextMeta)
{
if (isNotEmpty(dyStringContents(genomeOut->metaTags)))
dyStringPrintf(genomeOut->metaTags, ", %s", hst->text);
else
dyStringPrintf(genomeOut->metaTags, "%s", hst->text);
}
}
if (isNotEmpty(hst->track))
{
// Time to add a track! (or add info to one, maybe)
struct tdbOutputStructure *tdbOut = hstToTdbOutput(hst, genomeOut, hub);
if (tdbOut != NULL)
{
if (hst->textLength == hubSearchTextLong)
dyStringPrintf(tdbOut->descriptionMatch, "%s", hst->text);
else if (hst->textLength == hubSearchTextMeta)
{
if (isNotEmpty(dyStringContents(tdbOut->metaTags)))
dyStringPrintf(tdbOut->metaTags, ", %s", hst->text);
else
dyStringPrintf(tdbOut->metaTags, "%s", hst->text);
}
}
}
}
return hubOut;
}
static char *tdbOutputStructureLabelToId(struct tdbOutputStructure *tdbOut)
/* Make an array name out of a tdbOutputStruct */
{
struct dyString *id = dyStringNew(0);
dyStringPrintf(id, "%s", htmlEncode(dyStringContents(tdbOut->shortLabel)));
if (tdbOut->childCount > 0)
{
dyStringPrintf(id, " (%d subtrack%s)", tdbOut->childCount,
tdbOut->childCount == 1 ? "" : "s");
}
return dyStringCannibalize(&id);
}
static void printTdbOutputStructureToDyString(struct tdbOutputStructure *tdbOut, struct dyString *dy, char *arrayName)
/* Print a tdbOutputStructure to a dyString, recursive for subtracks. */
{
dyStringPrintf(dy, "trackData['%s'] = [", arrayName);
if (tdbOut->childCount > 0)
{
struct dyString *subtrackDy = dyStringNew(0);
struct tdbOutputStructure *child = tdbOut->children;
while (child != NULL)
{
char *childId = tdbOutputStructureLabelToId(child);
dyStringPrintf(dy, "\n\t{\n\tid: '%s',\n\tparent: '%s',\n\t"
"li_attr: {nodetype:'track', configlink:'%s'},\n\ttext: \'%s ",
childId, arrayName, dyStringContents(child->configUrl), childId);
if (isNotEmpty(dyStringContents(child->metaTags)))
{
dyStringPrintf(dy, " Metadata: %s",
htmlEncode(dyStringContents(child->metaTags)));
}
if (isNotEmpty(dyStringContents(child->descriptionMatch)))
{
dyStringPrintf(dy, " Description: %s",
htmlEncode(dyStringContents(child->descriptionMatch)));
}
dyStringPrintf(dy, "\'");
if (child->childCount > 0)
{
dyStringPrintf(dy, ",\n\tchildren: true");
printTdbOutputStructureToDyString(child, subtrackDy, childId);
}
dyStringPrintf(dy, "\n\t},");
child = child->next;
}
dyStringPrintf(dy, "];\n");
if (isNotEmpty(dyStringContents(subtrackDy)))
dyStringPrintf(dy, "%s", subtrackDy->string);
}
else
dyStringPrintf(dy, "];\n");
}
void printGenomeOutputStructureToDyString(struct genomeOutputStructure *genomeOut, struct dyString *dy, char *genomeNameId)
/* Print a genomeOutputStructure to a dyString. The structure here is:
* trackData[genome] = [{track 1 obj}, {track2 obj}, {track3 obj}, ... ]
* trackData[track1] = [{search hit text}, {subtrack1 obj}, {subtrack2 obj}, ... ]
*
* if track1, track2, track3 are container tracks, then the recursive function
* tdbOutputStructureToDystring creates the above trackData[track1] = [{}] for
* each of the containers, otherwise a single child of the genome is sufficient */
{
struct tdbOutputStructure *tdbOut = NULL;
static struct dyString *tdbArrayDy = NULL; // the dyString for all of the tdb objects
static struct dyString *idString = NULL; // the special id of this track
if (tdbArrayDy == NULL)
tdbArrayDy = dyStringNew(0);
if (idString == NULL)
idString = dyStringNew(0);
dyStringPrintf(dy, "trackData['%s'] = [", genomeNameId);
if (genomeOut->tracks != NULL)
{
tdbOut = genomeOut->tracks;
slReverse(&tdbOut);
while (tdbOut != NULL)
{
dyStringPrintf(idString, "%s", tdbOutputStructureLabelToId(tdbOut));
dyStringPrintf(dy, "\n\t{\n\t'id': '%s',\n\t'parent': '%s',\n\t"
"'li_attr': {'nodetype':'track', configlink: '%s'},\n\t'text': \'%s ",
idString->string, genomeNameId, dyStringContents(tdbOut->configUrl), idString->string);
if (isNotEmpty(dyStringContents(tdbOut->metaTags)))
{
dyStringPrintf(dy, " Metadata: %s",
htmlEncode(dyStringContents(tdbOut->metaTags)));
}
if (isNotEmpty(dyStringContents(tdbOut->descriptionMatch)))
{
dyStringPrintf(dy, " Description: %s",
htmlEncode(dyStringContents(tdbOut->descriptionMatch)));
}
dyStringPrintf(dy, "\'");
// above we took care of both non-heirarchical tracks and the top-level containers,
// now do container children, which also takes care of any deeper heirarchies
if (tdbOut->childCount > 0)
dyStringPrintf(dy, ",\n\t'children': true");
dyStringPrintf(dy, "\n\t},\n");
if (tdbOut->childCount > 0)
printTdbOutputStructureToDyString(tdbOut, tdbArrayDy, idString->string);
tdbOut = tdbOut->next;
dyStringClear(idString);
}
}
dyStringPrintf(dy, "];\n"); // close off genome node
dyStringPrintf(dy, "%s\n", tdbArrayDy->string);
dyStringClear(tdbArrayDy);
}
void printHubOutputStructure(struct hubOutputStructure *hubOut, struct hubEntry *hubInfo)
/* Convert a hubOutputStructure to a jstree-readable string. This function forms the root
* node for each hub search tree, whose children are the hub description match and each individual
* genome node. A simplified structure is:
* trackData['#_hubId'] = [{id:'descriptionMatch',...},{id:'assembly1',...},...]
*
* The id's become new "trackData[id]" entries with their own arrays later if they have
* sub-trees (via printGenomeOutputStructureToDyString() and printTdbOutputStructureToDyString(). */
{
struct dyString *dy = dyStringNew(0);
// The leading '#' tells the javascript this is a 'root' node
dyStringPrintf(dy, "trackData['#_%d'] = [", hubInfo->id);
if (isNotEmpty(dyStringContents(hubOut->descriptionMatch)))
{
dyStringPrintf(dy, "{'id':'%d_descriptionMatchText','parent':'#_%d',"
"'state':{'opened': true},'text': 'Hub Description: "
"%s'},",
hubInfo->id, hubInfo->id, htmlEncode(dyStringContents(hubOut->descriptionMatch)));
}
struct genomeOutputStructure *genomeOut = hubOut->genomes;
struct dyString *genomeDy = dyStringNew(0);
if (genomeOut != NULL)
{
dyStringPrintf(dy, "{'id':'%d_assemblies', 'text':'%d Matching Assembl%s', 'parent':'#_%d', "
"'children':true, 'li_attr': {'state':{'opened': 'false'}}}];\n",
hubInfo->id, hubOut->genomeCount, hubOut->genomeCount == 1 ? "y" : "ies", hubInfo->id);
dyStringPrintf(dy, "trackData['%d_assemblies'] = [", hubInfo->id);
while (genomeOut != NULL)
{
char *assemblyName = htmlEncode(dyStringContents(genomeOut->shortLabel));
char genomeNameId[512];
safef(genomeNameId, sizeof(genomeNameId), "%d_%s", hubInfo->id, assemblyName);
dyStringPrintf(dy, "{'id': '%s', 'parent': '%d_assemblies', 'children': true, "
"'li_attr': {'assemblylink': '%s','nodetype': 'assembly'},"
"'text': \"%s",
genomeNameId, hubInfo->id, dyStringContents(genomeOut->assemblyLink), assemblyName);
if (genomeOut->trackCount > 0)
{
dyStringPrintf(dy, " (%d track%s) ", genomeOut->trackCount,
genomeOut->trackCount == 1 ? "" : "s");
}
if (isNotEmpty(dyStringContents(genomeOut->metaTags)))
{
dyStringPrintf(dy, " %s",
htmlEncode(dyStringContents(genomeOut->metaTags)));
}
if (isNotEmpty(dyStringContents(genomeOut->descriptionMatch)))
{
dyStringPrintf(dy, " Assembly Description: %s",
htmlEncode(dyStringContents(genomeOut->descriptionMatch)));
}
dyStringPrintf(dy, "\"},");
printGenomeOutputStructureToDyString(genomeOut, genomeDy, genomeNameId);
genomeOut = genomeOut->next;
}
}
dyStringPrintf(dy, "];\n");
dyStringPrintf(dy, "%s", genomeDy->string);
jsInline(dy->string);
dyStringClear(dy);
}
static void printOutputForHub(struct hubEntry *hubInfo, struct hubSearchText *hubSearchResult, int count)
/* Given a hub's info and a structure listing the search hits within the hub, first print
* a basic line of hub information with a "connect" button. Then, if the search results
* are non-NULL, write out information about the genomes and tracks from the search hits that
* match the db filter.
* If there are no search results to print, the basic hub lines are combined into a single HTML table
* that is defined outside this function.
* Otherwise, each hub line is printed in its own table followed by a
containing details
* about the search results. */
{
if (hubSearchResult != NULL)
printf("
\n");
outputPublicTableRow(hubInfo, count);
if (hubSearchResult != NULL)
{
printf("
\n");
struct trackHub *hub = fetchTrackHub(hubInfo);
if (hub != NULL)
{
struct hubOutputStructure *hubOut = buildHubSearchOutputStructure(hub, hubSearchResult);
if (dyStringIsEmpty(hubOut->descriptionMatch) && (hubOut->genomes == NULL))
return; // no detailed search results; hit must have been to hub short label or something
printf("
\n");
printf("", hubInfo->id); // div for the jstree for this hub's search result(s)
printf("
\n");
printHubOutputStructure(hubOut, hubInfo);
}
}
}
int hubEntryCmp(const void *va, const void *vb)
/* Compare to sort based on shortLabel */
{
const struct hubEntry *a = *((struct hubEntry **)va);
const struct hubEntry *b = *((struct hubEntry **)vb);
return strcasecmp(a->shortLabel, b->shortLabel);
}
void printHubList(struct slName *hubsToPrint, struct hash *hubLookup, struct hash *searchResultHash)
/* Print out a list of hubs, possibly along with search hits to those hubs.
* hubLookup takes hub URLs to struct hubEntry
* searchResultHash takes hub URLs to struct hubSearchText * (list of hits on that hub)
*/
{
printf("