14dbfe57d9b670fa554c64cd4604e37fdefe037d
chmalee
  Fri Jan 5 15:33:17 2024 -0800
Working version of saving last five user entered terms in the search bar, refs #29611

diff --git src/hg/js/hgSearch.js src/hg/js/hgSearch.js
index fe1d6f2..2a97818 100644
--- src/hg/js/hgSearch.js
+++ src/hg/js/hgSearch.js
@@ -499,33 +499,33 @@
                     }
                     if (match.highlight) {
                         url += url[url.length-1] !== '&' ? '&' : '';
                         url += "addHighlight=" + match.highlight;
                     }
                 } else {
                     url = "hgc?db=" + db + "&g=" + hgcTitle + "&i=" + match.position + "&c=0&o=0&l=0&r=0" ;
                 }
                 matchTitle = match.posName;
                 //if (match.canonical === true)
                 matchTitle = "<b>" + matchTitle + "</b>";
             }
             var newListObj;
             if (printCount < 500) {
                 if (printCount + 1 > 10) {
-                    newListObj = "<li class='" + title + "_hidden' style='display: none'><a href=\"" + url + "\">" + matchTitle + "</a> - ";
+                    newListObj = "<li class='searchResult " + title + "_hidden' style='display: none'><a href=\"" + url + "\">" + matchTitle + "</a> - ";
                 } else {
-                    newListObj = "<li><a href=\"" + url + "\">" + matchTitle + "</a> - ";
+                    newListObj = "<li class='searchResult'><a href=\"" + url + "\">" + matchTitle + "</a> - ";
                 }
                 printedPos = false;
                 if (!(["helpDocs", "publicHubs", "trackDb"].includes(title))) {
                     newListObj += match.position;
                     printedPos = true;
                 }
                 if (match.description) {
                     if (printedPos) {newListObj += " - ";}
                     newListObj += match.description;
                 }
                 newListObj += "</li>";
                 list.innerHTML += newListObj;
                 printCount += 1;
             }
         });
@@ -752,30 +752,31 @@
                 uiState.resultHash[match.name] = match;
             });
         } else {
             // no results for this search
             uiState.resultHash = {};
             uiState.positionMatches = [];
         }
         updateFilters(uiState);
         updateSearchResults(uiState);
         buildSpeciesDropdown();
         fillOutAssemblies();
         urlVars = {"db": db, "search": uiState.search, "showSearchResults": ""};
         // changing the url allows the history to be associated to a specific url
         var urlParts = changeUrl(urlVars);
         $("#searchCategories").jstree(true).refresh(false,true);
+        saveLinkClicks();
         if (doSaveHistory)
             saveHistory(uiState, urlParts);
         changeSearchResultsLabel();
     }
 
     function handleRefreshState(jsonData) {
         if (checkJsonData(jsonData, 'handleRefreshState')) {
             updateStateAndPage(jsonData, true);
         }
         $("#spinner").remove();
     }
 
     function handleErrorState(jqXHR, textStatus) {
         cart.defaultErrorCallback(jqXHR, textStatus);
         $("#spinner").remove();
@@ -823,30 +824,108 @@
                         }
                     },
                     handleRefreshState,
                     handleErrorState);
             // always update the results when a search has happened
             cart.flush();
         }
     }
 
     function switchAssemblies(newDb) {
         // reload the page to attach curated hub (if any)
         re = /db=[\w,\.]*/;
         window.location = window.location.href.replace(re,"db="+newDb);
     }
 
+    function saveLinkClicks() {
+        // attach the link handlers to save search history
+        document.querySelectorAll(".searchResult>a").forEach(function(i,j,k) {
+            i.addEventListener("click", function(e) {
+                // i is the <a> element, use parent elements and the href
+                // to construct a fake autocomplete option and save it
+                e.preventDefault(); // stops the page from redirecting until this function finishes
+                let callbackData = {};
+                let trackName = i.parentNode.parentNode.parentNode.id.replace(/Results$/,"");
+                let matchList = uiState.positionMatches.find((matches) => matches.name === trackName);
+                let id, match, matchStr = i.childNodes[0].textContent;
+                // switch lookup depending on the different search categories:
+                let decoder = function(str) {
+                    // helper decoder to change the html encoded entities in
+                    // uiState.positionMatches.posName to what is actually rendered
+                    // in matchStr
+                    return $("<textarea/>").html(str).text();
+                };
+                let geneSymbol; // a potentially fake geneSymbol for the autoComplete
+                if (trackName === "trackDb") {
+                    match = matchList.matches.find((elem) => {
+                        let posSplit = elem.posName.split(":");
+                        geneSymbol = decoder(posSplit[1] + " - " + posSplit[2]);
+                        id = "hgTrackUi?db=" + uiState.db + "&g=" + posSplit[0];
+                        return geneSymbol === matchStr;
+                    });
+                    callbackData.value = geneSymbol + " " + match.description;
+                    callbackData.id = id;
+                    callbackData.geneSymbol = geneSymbol;
+                    callbackData.internalId = "";
+                } else if (trackName === "publicHubs") {
+                    match = matchList.matches.find((elem) => {
+                        let posSplit = elem.posName.split(":");
+                        geneSymbol = decoder(posSplit[4] + " - " + trackHubSkipHubName(posSplit[3]));
+                        id = "hgTrackUi?hubUrl=" + posSplit[0] + ":" + posSplit[1] + "&g=" + posSplit[3] + "&db=" + posSplit[3];
+                        return decoder(posSplit[4]) === matchStr;
+                    });
+                    callbackData.value = geneSymbol + " " + match.description;
+                    callbackData.id = id;
+                    callbackData.geneSymbol = geneSymbol;
+                    callbackData.internalId = "";
+                } else if (trackName === "helpDocs") {
+                    match = matchList.matches.find((elem) => {
+                        let posSplit = elem.posName.split(":");
+                        geneSymbol = decoder(posSplit[1].replaceAll("_", " "));
+                        return geneSymbol === matchStr;
+                    });
+                    callbackData.value = geneSymbol + " " + match.description;
+                    callbackData.id = match.position.split(":")[0];
+                    callbackData.geneSymbol = geneSymbol;
+                    callbackData.internalId = "";
+                } else { // regular track item  search result click
+                    match = matchList.matches.find((elem) => {
+                        geneSymbol = elem.posName.replace(/ .*$/,"");
+                        return decoder(elem.posName) === matchStr;
+                    });
+                    callbackData.value = geneSymbol + " " + match.description;
+                    // special case the genbank searches that are supposed to go to hgc
+                    // and not hgTracks
+                    let parentTitle = i.parentNode.parentNode.parentNode.childNodes[2];
+                    if (["all_mrna", "all_est", "xenoMrna", "xenoEst", "intronEst"].includes(trackName) && parentTitle.textContent.includes("Unaligned")) {
+                        id = "hgc?db=" + db + "&g=" + trackName+ "&i=" + match.position + "&c=0&o=0&l=0&r=0" ;
+                    } else {
+                        id = match.position;
+                    }
+                    callbackData.id = id;
+                    callbackData.geneSymbol = geneSymbol;
+                    callbackData.internalId = match.hgFindMatches;
+                }
+                // type for autocomplete select to know where to navigate to
+                callbackData.type = trackName;
+                callbackData.label = callbackData.value;
+                addRecentSearch(db, callbackData.geneSymbol, callbackData);
+                window.location = i.href;
+            });
+        });
+    }
+
     function init() {
         cart.setCgi('hgSearch');
         cart.debug(debugCartJson);
         // If a user clicks search before the page has finished loading
         // start processing it now:
         $("#searchBarSearchButton").click(sendUserSearch);
         if (typeof cartJson !== "undefined") {
             if (typeof cartJson.db !== "undefined") {
                 db = cartJson.db;
             } else {
                 alert("Error no database from request");
             }
             if (typeof cartJson.warning !== "undefined") {
                 alert("Warning: " + cartJson.warning);
             }
@@ -889,30 +968,32 @@
                 cart.send({ getUiState: {db: db} }, handleRefreshState);
                 cart.flush();
             }
             $("#searchCategories").bind('ready.jstree', function(e, data) {
                 // wait for the category jstree to finish loading before showing the results
                 $("#searchBarSearchString").val(uiState.search);
                 updateSearchResults(uiState);
 
                 // when a category is checked/unchecked we show/hide that result
                 // from the result list
                 $("#searchCategories").on('check_node.jstree uncheck_node.jstree', function(e, data) {
                     if ($("#searchResults")[0].children.length > 0) {
                         showOrHideResults(e,data.node);
                     }
                 });
+                // Reattach event handlers as necessary:
+                saveLinkClicks();
             });
             saveHistory(cartJson, urlParts, true);
         } else {
             // no cartJson object means we are coming to the page for the first time:
             cart.send({ getUiState: {db: db} }, handleRefreshState);
             cart.flush();
         }
 
         buildSpeciesDropdown(); // make the list of available organisms
         fillOutAssemblies(); // call once to get the initial state
         $("#speciesSelect").change(fillOutAssemblies);
         $("#dbSelect").change(function(e) {
             e.preventDefault();
             db = e.currentTarget.value;
             switchAssemblies(db);