4c389c37d5a42eafb281c81ca6d12271a58f1499
jcasper
  Thu Jun 22 13:18:03 2017 -0700
hgHubConnect now expands on single click, show results in correct order,
and has more intuitive logic for the search/filter text boxes, refs #13625

diff --git src/hg/hgHubConnect/hgHubConnect.c src/hg/hgHubConnect/hgHubConnect.c
index 6a7c49c..4a1fabe 100644
--- src/hg/hgHubConnect/hgHubConnect.c
+++ src/hg/hgHubConnect/hgHubConnect.c
@@ -398,74 +398,84 @@
             }
         else
             {
             // no db in the hubSearchText means this is a top-level hub hit.
             // filter by the db list associated with the hub instead
             struct hubEntry *hubInfo = hashFindVal(hubLookup, hubUrl);
             char *dbList = cloneString(hubInfo->dbList);
             tolowers(dbList);
             if (stringIn(dbFilter, dbList) == NULL)
                 continue;
             }
         }
     // Add hst to the list to be returned
     slAddHead(&hubSearchResultsList, hst);
     }
+slReverse(&hubSearchResultsList);
 return hubSearchResultsList;
 }
 
 
-void printSearchBox(char *hubSearchTerms, char *dbFilter)
+void printSearchAndFilterBoxes(int searchEnabled, char *hubSearchTerms, char *dbFilter)
 /* Create the text boxes for search and database filtering along with the required
  * javscript */
 {
+char event[4096];
+if (searchEnabled)
+    {
+    safef(event, sizeof(event), 
+            "document.searchHubForm.elements['hubSearchTerms'].value=$('#hubSearchTerms').val();"
+            "document.searchHubForm.elements['hubDbFilter'].value=$('#hubDbFilter').val();"
+            "document.searchHubForm.submit();return true;");
     printf("Enter search terms to find in public track hub description pages:<BR>"
             "<input name=\"hubSearchTerms\" id=\"hubSearchTerms\" class=\"hubField\" "
-        "type=\"text\" size=\"65\" value=\"%s\"> \n"
-        "<input name=\"hubSearchButton\" id='hubSearchButton' "
-        "class=\"hubField\" type=\"button\" value=\"Search Public Hubs\">\n",
+            "type=\"text\" size=\"65\" value=\"%s\"> \n",
             hubSearchTerms!=NULL?hubSearchTerms:"");
-jsOnEventById("click", "hubSearchButton",
-        "document.searchHubForm.elements['hubSearchTerms'].value=$('#hubSearchTerms').val();"
-        "document.searchHubForm.submit();return true;");
     printf("<br>\n");
+    }
+else
+    {
+    safef(event, sizeof(event), 
+            "document.searchHubForm.elements['hubDbFilter'].value=$('#hubDbFilter').val();"
+            "document.searchHubForm.submit();return true;");
+    }
+
 printf("Filter hubs by assembly: "
         "<input name=\"%s\" id=\"hubDbFilter\" class=\"hubField\" "
         "type=\"text\" size=\"10\" value=\"%s\"> \n"
-        "<input name=\"dbFilterButton\" id='dbFilterButton' "
-        "class=\"hubField\" type=\"button\" value=\"Filter assemblies\">\n",
+        "<input name=\"hubSearchButton\" id='hubSearchButton' "
+        "class=\"hubField\" type=\"button\" value=\"Search Public Hubs\">\n",
         hgHubDbFilter, dbFilter!=NULL?dbFilter:"");
-jsOnEventById("click", "dbFilterButton",
-        "document.dbFilterHubForm.elements['hubDbFilter'].value=$('#hubDbFilter').val();"
-        "document.dbFilterHubForm.submit();return true;");
-puts("<BR><BR>\n");
+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");
+        "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>");
 printf("<table id=\"publicHubsTable\" class=\"hubList\"> "
         "<thead><tr> "
             "<th>Display</th> "
             "<th>Hub Name</th> "
             "<th>Description</th> "
             "<th>Assemblies</th> "
         "</tr></thead></table>\n");
@@ -950,32 +960,32 @@
 char *dbFilter = cartOptionalString(cart, hgHubDbFilter);
 char *lcDbFilter = cloneString(dbFilter);
 if (isNotEmpty(lcDbFilter))
     tolowers(lcDbFilter);
 
 // make sure all the public hubs are in the hubStatus table.
 addPublicHubsToHubStatus(conn, publicTable, statusTable);
 
 // build full public hub lookup hash, taking each URL to struct hubEntry * for that hub
 struct hash *hubLookup = buildPublicLookupHash(conn, publicTable, statusTable, pHash);
 
 printf("<div id=\"publicHubs\" class=\"hubList\"> \n");
 
 char *hubSearchTableName = cfgOptionDefault("hubSearchTextTable", "hubSearchText");
 int searchEnabled = sqlTableExists(conn, hubSearchTableName);
-if (searchEnabled)
-    printSearchBox(hubSearchTerms, dbFilter);
+
+printSearchAndFilterBoxes(searchEnabled, hubSearchTerms, dbFilter);
 
 struct hash *searchResultHash = NULL;
 struct slName *hubsToPrint = NULL;
 if (searchEnabled && !isEmpty(hubSearchTerms))
     {
     printSearchTerms(hubSearchTerms);
     if (isNotEmpty(cleanSearchTerms))
         tolowers(cleanSearchTerms);
     // Forcing checkDescriptions to TRUE right now, but we might want to add this as a
     // checkbox option for users in the near future.
     bool checkDescriptions = TRUE;
     struct hubSearchText *hubSearchResults = getHubSearchResults(conn, hubSearchTableName,
             cleanSearchTerms, checkDescriptions, lcDbFilter, hubLookup);
     searchResultHash = newHash(5);
     struct hubSearchText *hst = hubSearchResults;
@@ -1259,100 +1269,100 @@
 cgiMakeHiddenVar(hgHubDoDisconnect, "on");
 cgiMakeHiddenVar(hgHubConnectRemakeTrackHub, "on");
 cartSaveSession(cart);
 puts("</FORM>");
 
 // 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");
-cartSaveSession(cart);
-puts("</FORM>");
-
-// this is the form for the search hub button
-printf("<FORM ACTION=\"%s\" NAME=\"dbFilterHubForm\">\n",  "../cgi-bin/hgHubConnect");
 cgiMakeHiddenVar(hgHubDbFilter, "");
-cgiMakeHiddenVar(hgHubDoFilter, "on");
 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> "
        "</ul> ");
 
 struct hash *publicHash = hgHubConnectPublic();
 hgHubConnectUnlisted(hubList, publicHash);
 printf("</div>");
 
 printf("<div class=\"tabFooter\">");
 
-char *emailAddress = cfgOptionDefault("hub.emailAddress","genome@soe.ucsc.edu");
 printf("<span class=\"small\">"
-    "Contact <A HREF=\"mailto:%s\">%s</A> to add a public hub."
-    "</span>\n", emailAddress,emailAddress);
+    "Contact <a href=\"../contacts.html\">us</A> to add a public hub."
+    "</span>\n");
 printf("</div>");
 
 cgiMakeHiddenVar(hgHubConnectRemakeTrackHub, "on");
 
 puts("</FORM>");
 printf("</div>\n");
 
 jsInline(
 "var hubSearchTree = (function() {\n"
 "    // Effectively global vars set by init\n"
 "    var treeDiv;        // Points to div we live in\n"
 "\n"
 "    function hubSearchTreeContextMenuHandler (node, callback) {\n"
 "        var nodeType = node.li_attr.nodetype;\n"
 "        if (nodeType == 'track') {\n"
 "            callback({\n"
 "               'openConfig': {\n"
 "                   'label' : 'Configure this track',\n"
 "                   'action' : function () {window.open(node.li_attr.configlink, '_blank'); }\n"
 "               }\n"
 "            });\n"
 "        }\n"
 "        else if (nodeType == 'assembly') {\n"
 "            callback({\n"
 "               'openConfig': {\n"
 "                   'label' : 'Open this assembly',\n"
 "                   'action' : function () {window.open(node.li_attr.assemblylink, '_blank'); }\n"
 "               }\n"
 "            });\n"
 "        }\n"
-"    }"
+"    }\n"
+"    function toggleExpansion(node, event) {\n"
+"       var ident = '#' + node.id;\n"
+"       if (event.type != 'contextmenu')\n"
+"           $(ident).jstree(true).toggle_node(node);\n"
+"       return false;\n"
+"    }\n" 
 "    function init() {\n"
 "       $.jstree.defaults.core.themes.icons = false;\n"
 "       $.jstree.defaults.core.themes.dots = true;\n"
 "       $.jstree.defaults.contextmenu.show_at_node = false;\n"
 "       $.jstree.defaults.contextmenu.items = hubSearchTreeContextMenuHandler\n"
 "       treeDiv=$('.hubTdbTree');\n"
 "       treeDiv.jstree({\n"
-"               'conditionalselect' : function (node, event) { return false; },\n"
-"               'plugins' : ['conditionalselect', 'contextmenu']\n"
+"               'conditionalselect' : function (node, event) { toggleExpansion(node, event); },\n"
+"               'plugins' : ['conditionalselect', 'contextmenu'],\n"
+"               'core' : { dblclick_toggle: false }\n"
 "               });\n"
 "    }\n\n"
 "    return { init: init};\n\n"
 "}());\n"
 "\n"
 "$(function () {\n"
 "    hubSearchTree.init();\n"
 "});\n"
 );
 
 cartWebEnd();
 }
 
 char *excludeVars[] = {"Submit", "submit", "hc_one_url", 
     hgHubCheckUrl, hgHubDoClear, hgHubDoDisconnect,hgHubDoRedirect, hgHubDataText,