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);