ea1508d83bb99583cb9ec1ff630b938187a44006
chmalee
  Tue Nov 26 08:13:06 2019 -0800
Change 'validate hub' to 'Hub Development' and display custom help
explaining that this tab is for running hubCheck. Also add a button
to load the hub on hgTracks, and a dropdown with extra checkboxes
for turning on udcTimeout and measureTiming. refs #24599

diff --git src/hg/hgHubConnect/hgHubConnect.c src/hg/hgHubConnect/hgHubConnect.c
index 0d8266f..318effe 100644
--- src/hg/hgHubConnect/hgHubConnect.c
+++ src/hg/hgHubConnect/hgHubConnect.c
@@ -375,84 +375,141 @@
 	ourPrintCell("");
 
     puts("</tr>");
     }
 
 printf("</tbody></TABLE>\n");
 printf("</div>");
 }
 
 void doValidateNewHub(char *hubUrl)
 /* Run hubCheck on a hub. */
 {
 struct dyString *cmd = dyStringNew(0);
 udcSetCacheTimeout(1);
 dyStringPrintf(cmd, "loader/hubCheck -htmlOut -noTracks %s", hubUrl);
-printf("</table>");
 printf("<div id=\"validateHubResult\" class=\"hubTdbTree\" style=\"overflow: auto\"></div>");
 FILE *f = popen(cmd->string, "r");
 if (f == NULL)
     errAbort("popen: error running command: \"%s\"", cmd->string);
 char buf[1024];
 while (fgets(buf, sizeof(buf), f))
     {
     jsInlineF("%s", buf);
     }
 if (pclose(f) == -1)
     errAbort("pclose: error for command \"%s\"", cmd->string);
 // the 'false' below prevents a few hub-search specific jstree configuration options
 jsInline("hubSearchTree.init(false);");
 dyStringFree(&cmd);
 }
 
-void hgHubConnectValidateNewHub()
+void hgHubConnectDeveloperMode()
 {
 // put out the top of our page
 char *hubUrl = cartOptionalString(cart, "validateHubUrl");
-printf("<div id=\"validateHub\" class=\"hubList\"> \n"
-    "<table id=\"validateHub\"> \n"
-    "<thead><tr> \n");
-printf("<th colspan=\"6\" id=\"addHubBar\"><label for=\"validateHubUrl\">URL:</label> \n");
+boolean doHubValidate = cartVarExists(cart, hgHubDoHubCheck);
+
+// the outer div for all the elements in the tab
+printf("\n<div id=\"hubDeveloper\" class=\"hubList\">\n");
+
+// the row to enter in the url and the button and settings to validate/load
+printf("<div class=\"addHubBar roundCorners\" id=\"validateHubUrlRow\">\n");
+printf("<label for=\"validateHubUrl\"><b>URL:</b></label>\n");
 if (hubUrl != NULL)
     {
     printf("<input name=\"validateText\" id=\"validateHubUrl\" class=\"hubField\" "
         "type=\"text\" size=\"65\" value=\"%s\"> \n", hubUrl);
     }
 else
     {
     printf("<input name=\"validateText\" id=\"validateHubUrl\" class=\"hubField\" "
         "type=\"text\" size=\"65\"> \n");
     }
 printf("<input name=\"hubValidateButton\" id='hubValidateButton' "
-    "class=\"hubField\" type=\"button\" value=\"Validate Hub\">\n"
-    "</th> \n"
-    "</tr> \n");
+    "class=\"hubField\" type=\"button\" value=\"Check Hub settings\">\n");
+printf(" or \n");
+printf("<input name=\"hubLoadMaybeTiming\" id='hubLoadMaybeTiming' "
+    "class=\"hubField\" type=\"button\" value=\"Load Hub\">\n");
+printf("</div>\n"); // validateHubUrlRow div
+
+printf("<div id=\"extraSettingsContainer\" class=\"addHubBar\">\n");
+printf("<img id=\"advancedSettingsButton\" src=\"../images/add_sm.gif\">\n");
+printf("Advanced Settings");
+
+char *measureTiming = cartCgiUsualString(cart, "measureTiming", NULL);
+char *udcTimeout = cartCgiUsualString(cart, "udcTimeout", NULL);
+
+printf("<div id=\"extraSettingsList\" style=\"display: none\">\n");
+printf("<ul style=\"list-style-type:none\">\n<li>\n");
+// measureTiming first
+printf("<input name=\"addMeasureTiming\" id=\"addMeasureTiming\" "
+    "class=\"hubField\" type=\"checkbox\" %s>", measureTiming != NULL ? "checked": "");
+printf("<label for=\"addMeasureTiming\">Display load times</label>\n");
+
+// and a tooltip explaining this checkbox
+printf("<div class=\"tooltip\"> (?)\n");
+printf("<span class=\"tooltiptext\">"
+    "Checking this box shows the timing measurements below the Genome Browser image. "
+    "Useful for determining slowdowns to loading or drawing tracks."
+    "</span>\n");
+printf("</div></li>\n"); // tooltip div
+
+printf("<li>\n");
+// udcTimeout enable/disable
+printf("<input name=\"disableUdcTimeout\" id=\"disableUdcTimeout\" "
+    "class=\"hubField\" type=\"checkbox\" %s >", udcTimeout != NULL ? "checked" : "");
+printf("<label for=\"disableUdcTimeout\">Enable hub refresh</label>\n");
+// add a tooltip explaining these checkboxes
+printf("<div class=\"tooltip\"> (?)\n");
+printf("<span class=\"tooltiptext\">"
+    "Checking this box changes the cache expiration time (default of 5 minutes) "
+    "and allows the Genome Browser to reload Hub configuration files with each refresh."
+    "</span>\n");
+printf("</div></li>\n"); // tooltip div
+printf("</ul>\n");
+printf("</div>\n"); // extraSettingsList div
+printf("</div>\n"); // extraSettingsContainer div
 
-if (hubUrl == NULL)
-    printf("<tr><td>Enter URL to hub to check settings</td></tr> \n");
-else
+if (hubUrl != NULL && doHubValidate)
     doValidateNewHub(hubUrl);
-printf("</table></div>");
+else
+    printf("<div id=\"hubDeveloperInstructions\">Enter URL to hub to check configuration settings "
+        "or load hub </div> \n");
+printf("</div>"); // hubDeveloper div
 
 jsOnEventById("click", "hubValidateButton",
     "var validateText = document.getElementById('validateHubUrl');"
     "validateText.value=$.trim(validateText.value);"
     "if(validateUrl($('#validateHubUrl').val())) { "
     " document.validateHubForm.elements['validateHubUrl'].value=validateText.value;"
+    " document.validateHubForm.elements['" hgHubDoHubCheck "'].value='on';"
     " document.validateHubForm.submit(); return true; }"
     "else { return false; }"
     );
+jsOnEventById("click", "hubLoadMaybeTiming",
+    "var validateText = document.getElementById('validateHubUrl');"
+    "var udcTimeout = document.getElementById('disableUdcTimeout').checked === true;"
+    "var doMeasureTiming = document.getElementById('addMeasureTiming').checked === true;"
+    "validateText.value=$.trim(validateText.value);"
+    "if(validateUrl($('#validateHubUrl').val())) {"
+    "   loc = \"../cgi-bin/hgTracks?&hgHub_do_firstDb=on\" + \"&hubUrl=\" + validateText.value;"
+    "   if (doMeasureTiming) { loc += \"&measureTiming=1\";} else { loc += \"&measureTiming=[]\"; }"
+    "   if (udcTimeout) { loc += \"&udcTimeout=5\"; } else { loc += \"&udcTimeout=[]\"; }"
+    "   window.location.href=loc; "
+    "} else { return false; }"
+    );
 }
 
 static void addPublicHubsToHubStatus(struct sqlConnection *conn, char *publicTable, char  *statusTable)
 /* Add urls in the hubPublic table to the hubStatus table if they aren't there already */
 {
 char query[1024];
 sqlSafef(query, sizeof(query),
         "select %s.hubUrl from %s left join %s on %s.hubUrl = %s.hubUrl where %s.hubUrl is NULL",
         publicTable, publicTable, statusTable, publicTable, statusTable, statusTable); 
 struct sqlResult *sr = sqlGetResult(conn, query);
 char **row;
 while ((row = sqlNextRow(sr)) != NULL)
     {
     char *errorMessage = NULL;
     char *url = row[0];
@@ -599,30 +656,31 @@
         "<input name=\"hubSearchButton\" id='hubSearchButton' "
         "class=\"hubField\" type=\"button\" value=\"Search Public Hubs\">\n",
         hgHubDbFilter, dbFilter!=NULL?dbFilter:"");
 jsOnEventById("click", "hubSearchButton", event);
 puts("<br><br>\n");
 }
 
 
 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 <B>restricted by search terms:</B> %s\n", hubSearchTerms);
 puts("<input name=\"hubDeleteSearchButton\" id='hubDeleteSearchButton' "
         "class=\"hubField\" type=\"button\" value=\"Show All Hubs\">\n");
+
 jsOnEventById("click", "hubDeleteSearchButton",
         "document.searchHubForm.elements['hubSearchTerms'].value='';"
         "document.searchHubForm.elements['hubDbFilter'].value='';"
         "document.searchHubForm.submit();return true;");
 puts("<BR><BR>\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("<BR><BR>\n");
 }
 
 
 void printHubListHeader()
 /* Write out the header for a list of hubs in its own table */
 {
 puts("<I>Clicking Connect redirects to the gateway page of the selected hub's default assembly.</I><BR>");
@@ -1595,43 +1653,63 @@
 printf(
 "<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jstree/3.3.7/themes/default/style.min.css\" />\n"
 "<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.1/jquery.min.js\"></script>\n"
 "<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jstree/3.3.7/jstree.min.js\"></script>\n"
 "<style>.jstree-default .jstree-anchor { height: initial; } </style>\n"
 );
 jsIncludeFile("utils.js", NULL);
 jsIncludeFile("jquery-ui.js", NULL);
 webIncludeResourceFile("jquery-ui.css");
 jsIncludeFile("ajax.js", NULL);
 jsIncludeFile("hgHubConnect.js", NULL);
 webIncludeResourceFile("hgHubConnect.css");
 jsIncludeFile("jquery.cookie.js", NULL);
 jsIncludeFile("spectrum.min.js", NULL);
 
+boolean doHubDevMode = cfgOptionBooleanDefault("hgHubConnect.validateHub", FALSE);
 printf("<div id=\"hgHubConnectUI\"> <div id=\"description\"> \n");
 printf(
     "<P>Track data hubs are collections of external tracks that can be imported into the UCSC Genome Browser. "
     "Hub tracks show up under the hub's own blue label bar on the main browser page, "
     "as well as on the configure page. For more information, including where to "
     "<A HREF=\"../goldenPath/help/hgTrackHubHelp.html#Hosting\">host</A> your track hub, see the "
     "<A HREF=\"../goldenPath/help/hgTrackHubHelp.html\" TARGET=_blank>"
     "User's Guide</A>."
     "To import a public hub click its \"Connect\" button below.</P>"
     "<P><B>NOTE: Because Track Hubs are created and maintained by external sources,"
     " UCSC is not responsible for their content.</B></P>"
    );
 printf("</div>\n");
+if (doHubDevMode)
+    {
+    printf("<div id=\"developerModeDescription\" style=\"display:none\"> \n");
+    printf("<p>Check the configuration settings for a hub, including the settings "
+        "in the hub.txt, genomes.txt, and trackDb.txt files. If there are errors in the hub "
+        "setup, a heirarchal tree of tracks is presented with the errors in red text. A hub "
+        "with no errors still has the tree present but needs to be clicked to be expanded to "
+        " explore the track heirarchy.</p>\n"
+        "<p>Also present are buttons to load the hub with track timing measuring turned on "
+        "in order to profile slow loading tracks, and an option to disable the track caching "
+        "mechanism so the Genome Browser picks up changes to the configuration files or "
+        "track data files. For more information on making track hubs, please see the following links: \n "
+        "<ul>\n"
+        "<li><a href=\"../goldenPath/help/trackDb/trackDbHub.html\">Track Hub Settings</a></li> \n"
+        "<li><a href=\"../goldenPath/help/hgTrackHubHelp#Hosting\">Where to host your track hub</a></li> \n"
+        "</ul> \n"
+        "</div>\n "
+        );
+    }
 
 // this variable is used by hub search and hub validate, initialize here so we don't
 // overwrite it unintentionally depending on which path the CGI takes
 jsInline("trackData = [];\n");
 
 getDbAndGenome(cart, &database, &organism, oldVars);
 
 char *survey = cfgOptionEnv("HGDB_HUB_SURVEY", "hubSurvey");
 char *surveyLabel = cfgOptionEnv("HGDB_HUB_SURVEY_LABEL", "hubSurveyLabel");
 
 if (survey && differentWord(survey, "off"))
     hPrintf("<span style='background-color:yellow;'><A HREF='%s' TARGET=_BLANK><EM><B>%s</EM></B></A></span>\n", survey, surveyLabel ? surveyLabel : "Take survey");
 hPutc('\n');
 
 // grab all the hubs that are listed in the cart
@@ -1670,72 +1748,103 @@
 // this is the form for the reset hub button
 printf("<FORM ACTION=\"%s\" NAME=\"resetHubForm\">\n",  "../cgi-bin/hgHubConnect");
 cgiMakeHiddenVar(hgHubCheckUrl, "");
 cartSaveSession(cart);
 puts("</FORM>");
 
 // this is the form for the search hub button
 printf("<FORM ACTION=\"%s\" NAME=\"searchHubForm\">\n",  "../cgi-bin/hgHubConnect");
 cgiMakeHiddenVar(hgHubSearchTerms, "");
 cgiMakeHiddenVar(hgHubDoSearch, "on");
 cgiMakeHiddenVar(hgHubDbFilter, "");
 cartSaveSession(cart);
 puts("</FORM>");
 
 // this is the form for the validate button
-boolean doValidate = cfgOptionBooleanDefault("hgHubConnect.validateHub", FALSE);
-if (doValidate)
+if (doHubDevMode)
     {
     printf("<FORM ACTION=\"%s\" NAME=\"validateHubForm\">\n",  "../cgi-bin/hgHubConnect");
     cgiMakeHiddenVar("validateHubUrl", "");
+    // allows saving the old url in the search bar but not run hubCheck on it right away
+    // mostly for when you come back to hgHubConnect after just looking at hgTracks
+    cgiMakeHiddenVar(hgHubDoHubCheck, "off");
     cartSaveSession(cart);
     puts("</FORM>");
     }
 
 // ... and now the main form
 printf("<FORM ACTION=\"%s\" METHOD=\"POST\" NAME=\"mainForm\">\n", "../cgi-bin/hgGateway");
 cartSaveSession(cart);
 
 // we have two tabs for the public and unlisted hubs
 printf("<div id=\"tabs\">"
-       "<ul> <li><a href=\"#publicHubs\">Public Hubs</a></li>"
-       "<li><a href=\"#unlistedHubs\">My Hubs</a></li> ");
-if (doValidate) // put up the validate tab if hg.conf statement present
-    printf("<li><a href=\"#validateHub\">Validate Hub</a></li>");
+       "<ul> <li><a class=\"defaultDesc\" href=\"#publicHubs\">Public Hubs</a></li>"
+       "<li><a class=\"defaultDesc\" href=\"#unlistedHubs\">My Hubs</a></li> ");
+if (doHubDevMode) // put up the validate tab if hg.conf statement present
+    printf("<li><a class=\"hubDeveloperDesc\" href=\"#hubDeveloper\">Hub Development</a></li>");
 printf("</ul> ");
 
 struct hash *publicHash = hgHubConnectPublic();
 hgHubConnectUnlisted(hubList, publicHash);
-if (doValidate)
-    hgHubConnectValidateNewHub();
+if (doHubDevMode)
+    hgHubConnectDeveloperMode();
+// add the event listener for toggling the right description text
+jsInline(
+    "var oldDesc = document.getElementById('description');\n"
+    "var newDesc = document.getElementById('developerModeDescription');\n"
+    "if (location.hash === \"#hubDeveloper\") {\n"
+    "   toggleTwoDivs(newDesc, oldDesc);\n"
+    "}\n"
+    "window.addEventListener('hashchange', function() {\n"
+    "   if (location.hash === \"#hubDeveloper\") {"
+    "       toggleTwoDivs(newDesc, oldDesc);\n"
+    "   } else {\n"
+    "       toggleTwoDivs(oldDesc, newDesc);\n"
+    "   }\n"
+    "});\n"
+    "var plusButton = document.getElementById('advancedSettingsButton');\n"
+    "plusButton.addEventListener('click', function() {\n"
+    "   var advancedSettingsDiv = document.getElementById('extraSettingsContainer');\n"
+    "   var extraSettingsList = document.getElementById('extraSettingsList');\n"
+    "   if (extraSettingsList.style.display != 'none') {\n"
+    "       extraSettingsList.style.display = 'none';\n"
+    "       imgTag = advancedSettingsDiv.getElementsByTagName('img')[0];\n"
+    "       imgTag.src = \"../images/add_sm.gif\";\n"
+    "   } else {\n"
+    "       extraSettingsList.style.display = 'block';\n"
+    "       imgTag = advancedSettingsDiv.getElementsByTagName('img')[0];\n"
+    "       imgTag.src = \"../images/remove_sm.gif\";\n"
+    "   }\n"
+    "});"
+    );
 printf("</div>");
 
 printf("<div class=\"tabFooter\">");
 
 printf("<span class=\"small\">"
     "Contact <a href=\"../contacts.html\">us</A> to add a public hub."
     "</span>\n");
 printf("</div>");
 
 cgiMakeHiddenVar(hgHubConnectRemakeTrackHub, "on");
 
 puts("</FORM>");
 printf("</div>\n");
 
 cartWebEnd();
 }
 
-char *excludeVars[] = {"Submit", "submit", "hc_one_url", "validateHubUrl",
+char *excludeVars[] = {"Submit", "submit", "hc_one_url", hgHubDoHubCheck,
     hgHubCheckUrl, hgHubDoClear, hgHubDoDisconnect,hgHubDoRedirect, hgHubDataText, 
     hgHubConnectRemakeTrackHub, NULL};
 
 int main(int argc, char *argv[])
 /* Process command line. */
 {
 long enteredMainTime = clock1000();
 oldVars = hashNew(10);
 cgiSpoof(&argc, argv);
 cartEmptyShell(doMiddle, hUserCookie(), excludeVars, oldVars);
 cgiExitTime("hgHubConnect", enteredMainTime);
 return 0;
 }