4a5e33685ee92a55d85200b621eb992d4c48cbfe
chmalee
Tue Nov 14 11:46:11 2023 -0800
Make the Show/Hide results text on hgSearch a link that triggers the showing or hiding of results. Also fix up incorrect comparisons to undefined and use the same linter settings as the rest of the javascript files, refs #31501
diff --git src/hg/js/hgSearch.js src/hg/js/hgSearch.js
index d5b6680..fcab8c4 100644
--- src/hg/js/hgSearch.js
+++ src/hg/js/hgSearch.js
@@ -1,15 +1,23 @@
+// Utility JavaScript
+
+// "use strict";
+
+// Don't complain about line break before '||' etc:
+/* jshint -W014 */
+/* jshint -W087 */
+/* jshint esnext: true */
var db; /* A global variable for the current assembly. Needed when we may not have
sent a cartJson request yet */
var hgSearch = (function() {
// this object contains everything needed to build current state of the page
var uiState = {
db: "", /* The assembly for which all this business belongs to */
categs: {}, /* all possible categories for this database, this includes all possible
* searchable tracks */
currentCategs: {}, /* the categories (filters) for the current search results */
positionMatches: [], /* an array of search result objects, one for each category,
* created by hgPositionsJson in cartJson.c */
search: "", /* what is currently in the search box */
trackGroups: {}, /* the track groups available for this assembly */
resultHash: {}, /* positionMatches but each objects' category name is a key */
@@ -97,31 +105,31 @@
function changeUrl(vars, oldVars) {
/* Save the users search string to the url so web browser can easily
* cache search results into the browser history
* vars: object of new key: val pairs like CGI arguments
* oldVars: arguments we want to keep between calls */
var myUrl = window.location.href;
myUrl = myUrl.replace('#','');
var urlParts = myUrl.split("?");
var baseUrl;
if (urlParts.length > 1)
baseUrl = urlParts[0];
else
baseUrl = myUrl;
var urlVars;
- if (oldVars === undefined) {
+ if (typeof oldVars === "undefined") {
var queryStr = urlParts[1];
urlVars = deparam(queryStr);
} else {
urlVars = oldVars;
}
for (var key in vars) {
var val = vars[key];
if (val === null || val === "") {
if (key in urlVars) {
delete urlVars[key];
}
} else {
urlVars[key] = val;
}
@@ -148,79 +156,79 @@
* followed by short label */
priorityA = trackA.priority;
priorityB = trackB.priority;
// if both priorities are undefined or equal to each other, sort
// on shortlabel alphabetically
if (priorityA === priorityB) {
if (trackA.name < trackB.name) {
return -1;
} else if (trackA.name > trackB.name) {
return 1;
} else {
return 0;
}
} else {
- if (priorityA === undefined) {
+ if (typeof priorityA === "undefined") {
return 1;
- } else if (priorityB === undefined) {
+ } else if (typeof priorityB === "undefined") {
return -1;
} else if (priorityA < priorityB) {
return -1;
} else if (priorityA > priorityA) {
return 1;
} else {
return 0;
}
}
}
function compareGroups(a, b) {
/* Compare function for track group sorting */
return uiState.trackGroups[a.name].priority - uiState.trackGroups[b.name].priority;
}
function sortByTrackGroups(groupList) {
return groupList.sort(compareGroups);
}
function sortTrackCategories(trackList) {
/* Sort the nested track list structure such that within each group
* the leaves of the tree are sorted by priority */
- if (trackList.children !== undefined) {
+ if (typeof trackList.children !== "undefined") {
trackList.children.sort(compareTrack);
for (var i = 0; i < trackList.children.length; i++) {
trackList.children[i] = sortTrackCategories(trackList.children[i]);
}
}
return trackList;
}
function categoryComp(category) {
- if (category.priority !== undefined)
+ if (typeof category.priority !== "undefined")
return category.priority;
return 1000.0;
}
function sortCategories(categList) {
return _.sortBy(categList, categoryComp);
}
function addCountAndTimeToLabel(categ) {
/* Change the text label of the node */
categ.text = categ.label + " (" + categ.numMatches + " results";
- if (categ.searchTime !== undefined && categ.searchTime >= 0) {
+ if (typeof categ.searchTime !== "undefined" && categ.searchTime >= 0) {
categ.text += ", " + categ.searchTime + "ms searchTime";
}
categ.text += ")";
}
function tracksToTree(trackList) {
/* Go through the list of all tracks for this assembly, filling
* out the necessary information for jstree to be able to work.
* Only include categories that have results.
* The groups object will get filled out along the way. */
trackGroups = uiState.trackGroups;
var ret = [];
var parentsHash = {};
var groups = {};
visibleTrackGroup.children = [];
@@ -237,120 +245,120 @@
_.assign(newCateg, track);
newCateg.text = track.longLabel;
newCateg.text = track.label;
var group = track.group;
newCateg.state = {checked: true, opened: true};
newCateg.text = track.label;
newCateg.li_attr = {title: track.description};
newCateg.numMatches = uiState.resultHash[newCateg.id].matches.length;
newCateg.searchTime = uiState.resultHash[newCateg.id].searchTime;
addCountAndTimeToLabel(newCateg);
if (track.visibility > 0) {
if (!groups.visible) {
groups.visible = visibleTrackGroup;
}
groups.visible.children.push(newCateg);
- if (newCateg.searchTime !== undefined) {
+ if (typeof newCateg.searchTime !== "undefined") {
if (groups.visible.searchTime < 0)
groups.visible.searchTime = 0;
groups.visible.searchTime += newCateg.searchTime;
}
groups.visible.numMatches += newCateg.numMatches;
} else {
var last = newCateg;
var doNewComp = true;
if (track.parents) {
var tracksAndLabels = track.parents.split(',');
var l = tracksAndLabels.length;
for (var i = 0; i < l; i+=2) {
var parentTrack= tracksAndLabels[i];
var parentLabel = tracksAndLabels[i+1];
if (!(parentTrack in parentsHash)) {
parent = {};
parent.id = parentTrack;
parent.label = parentLabel;
parent.children = [last];
parent.li_attr = {title: "Search for track items in all of the searchable subtracks of the " + parentLabel + " track"};
parent.numMatches = last.numMatches;
parent.searchTime = last.searchTime;
parentsHash[parentTrack] = parent;
addCountAndTimeToLabel(parent);
last = parent;
doNewComp = true;
- } else if (last !== undefined) {
+ } else if (typeof last !== "undefined") {
// if we are processing the first parent, we need to add ourself (last)
// as a child so the subtrack list is correct, but we still need
// to go up through the parent list and update the summarized counts
if (doNewComp) {
parentsHash[parentTrack].children.push(last);
}
doNewComp = false;
parentsHash[parentTrack].numMatches += last.numMatches;
- if (last.searchTime !== undefined) {
+ if (typeof last.searchTime !== "undefined") {
parentsHash[parentTrack].searchTime += last.searchTime;
}
addCountAndTimeToLabel(parent);
}
}
}
- if (groups[group] !== undefined && last !== undefined) {
+ if (typeof groups[group] !== "undefined" && typeof last !== "undefined") {
groups[group].numMatches += last.numMatches;
- if (last.searchTime !== undefined) {
+ if (typeof last.searchTime !== "undefined") {
groups[group].searchTime += last.searchTime;
}
addCountAndTimeToLabel(groups[group]);
if (doNewComp) {
groups[group].children.push(last);
}
} else if (doNewComp) {
groups[group] = {};
groups[group].id = group;
groups[group].name = group;
groups[group].label = group;
addCountAndTimeToLabel(groups[group]);
groups[group].numMatches = last.numMatches;
groups[group].searchTime = last.searchTime;
groups[group].children = [last];
- if (trackGroups !== undefined && group in trackGroups) {
+ if (typeof trackGroups !== "undefined" && group in trackGroups) {
groups[group].priority = trackGroups[group].priority;
groups[group].label = trackGroups[group].label;
addCountAndTimeToLabel(groups[group]);
} else {
trackGroups[group] = groups[group];
}
}
}
});
if ("visible" in groups) {
groups.visible.children = sortTrackCategories(groups.visible.children);
addCountAndTimeToLabel(groups.visible);
ret.push(groups.visible);
}
hiddenTrackChildren = [];
_.each(groups, function(group) {
if (group.id !== "Visible Tracks") {
group.li_attr = {title: "Search for track items in the " + group.label+ " set of tracks"};
group.state = {checked: true, opened: true};
group.children = sortTrackCategories(group.children);
hiddenTrackChildren.push(group);
}
});
if (hiddenTrackChildren.length > 0) {
hiddenTrackChildren = sortByTrackGroups(hiddenTrackChildren);
_.each(hiddenTrackChildren, function(group) {
hiddenTrackGroup.children.push(group);
- if (group.searchTime !== undefined) {
+ if (typeof group.searchTime !== "undefined") {
if (hiddenTrackGroup.searchTime < 0) {
hiddenTrackGroup.searchTime = 0;
}
hiddenTrackGroup.searchTime += group.searchTime;
}
hiddenTrackGroup.numMatches += group.numMatches;
});
addCountAndTimeToLabel(hiddenTrackGroup);
ret.push(hiddenTrackGroup);
}
return ret;
}
function filtersToJstree() {
/* Turns uiState.categs into uiState.currentCategs, which populates the
@@ -397,61 +405,61 @@
return thisCategs[ele];
}))
};
}
function showOrHideResults(event, node) {
/* When a checkbox is checked/uncheck in the tree, show/hide the corresponding
* result section in the list of results */
var state = node.state.checked;
if (node.children.length > 0) {
_.each(node.children, function(n) {
showOrHideResults(event, $("#searchCategories").jstree().get_node(n));
});
} else {
resultLi = $('[id="' + node.id + 'Results"');
- if (resultLi !== undefined) // if we don't have any results for this track resultLi is undefined
+ if (typeof resultLi !== "undefined") // if we don't have any results for this track resultLi is undefined
_.each(resultLi, function(li) {
li.style = state ? "display" : "display: none";
});
}
}
function buildTree(node, cb) {
cb.call(this, uiState.currentCategs[node.id]);
}
function makeCategoryTree() {
var parentDiv = $("#searchCategories");
$.jstree.defaults.core.themes.icons = false;
$.jstree.defaults.core.themes.dots = true;
$.jstree.defaults.contextmenu.show_at_node = false;
parentDiv.jstree({
'plugins' : ['contextmenu', 'checkbox'],
'core': {
'data': buildTree,
'check_callback': true
},
'checkbox': {
'tie_selection': false
}
});
parentDiv.css('height', "auto");
}
function updateFilters(uiState) {
- if (uiState.categs !== undefined) {
+ if (typeof uiState.categs !== "undefined") {
filtersToJstree();
makeCategoryTree();
}
}
function clearOldFacetCounts() {
$("[id*='extraInfo']").remove();
}
function printMatches(list, matches, title, searchDesc) {
var printCount = 0;
_.each(matches, function(match, printCount) {
var position = match.position.split(':');
var url, matchTitle;
if (title === "helpDocs") {
@@ -522,58 +530,69 @@
}
});
}
function showMoreResults() {
var trackName = this.id.replace(/Results_.*/, "");
var isHidden = $("." + trackName + "_hidden")[0].style.display === "none";
_.each($("." + trackName + "_hidden"), function(hiddenLi) {
if (isHidden) {
hiddenLi.style = "display:";
} else {
hiddenLi.style = "display: none";
}
});
if (isHidden) {
+ if (this.nextSibling) {
+ // click on the '+' icon
newText = this.nextSibling.innerHTML.replace(/Show/,"Hide");
this.nextSibling.innerHTML = newText;
this.src = "../images/remove_sm.gif";
} else {
+ // click on the link text
+ this.innerHTML = this.innerHTML.replace(/Show/,"Hide");
+ }
+ } else {
+ if (this.nextSibling) {
+ // click on the '-' icon
newText = this.nextSibling.innerHTML.replace(/Hide/,"Show");
this.nextSibling.innerHTML = newText;
this.src = "../images/add_sm.gif";
+ } else {
+ this.innerHTML = this.innerHTML.replace(/Hide/,"Show");
+ }
}
}
function collapseNode() {
var toCollapse = this.parentNode.childNodes[3];
var isHidden = toCollapse.style.display === "none";
if (isHidden)
{
toCollapse.style = 'display:';
this.src = "../images/remove_sm.gif";
}
else
{
toCollapse.style = 'display: none';
this.src = "../images/add_sm.gif";
}
}
function updateSearchResults(uiState) {
var parentDiv = $("#searchResults");
- if (uiState && uiState.search !== undefined) {
+ if (uiState && typeof uiState.search !== "undefined") {
$("#searchBarSearchString").val(uiState.search);
} else {
// back button all the way to the beginning
$("#searchBarSearchString").val("");
}
if (uiState && uiState.positionMatches && uiState.positionMatches.length > 0) {
// clear the old search results if there were any:
parentDiv.empty();
// create the elements that will hold results:
var newList = document.createElement("ul");
var noUlStyle = document.createAttribute("class");
noUlStyle.value = "ulNoStyle";
newList.setAttributeNode(noUlStyle);
parentDiv.append(newList);
@@ -589,74 +608,85 @@
var newListObj = document.createElement("li");
var idAttr = document.createAttribute("id");
idAttr.value = title + 'Results';
newListObj.setAttributeNode(idAttr);
var noLiStyle = document.createAttribute("class");
noLiStyle.value = "liNoStyle";
newListObj.setAttributeNode(noLiStyle);
newListObj.innerHTML += "";
newListObj.innerHTML += "";
newListObj.innerHTML += " " + searchDesc + ":";
//printOneFullMatch(newList, matches[0], title, searchDesc);
// Now loop through each actual hit on this table and unpack onto list
var subList = document.createElement("ul");
printMatches(subList, matches, title, searchDesc);
if (matches.length > 10) {
+ idStr = idAttr.value + "_" + categoryCount;
subList.innerHTML += "
No results
"; parentDiv.empty(); parentDiv.html(msg); clearOldFacetCounts(); } else { parentDiv.empty(); } } function fillOutAssemblies(e) { organism = $("#speciesSelect")[0].value; select = $("#dbSelect"); select.empty(); _.each(_.sortBy(uiState.genomes[organism], ['orderKey']), function(assembly) { newOpt = document.createElement("option"); newOpt.value = assembly.name; newOpt.label = trackHubSkipHubName(assembly.organism) + " " + assembly.description; if (assembly.name == db) { newOpt.selected = true; } $("#dbSelect").append(newOpt); }); // if we are getting here from a change event on the species dropdown // and are switching to the default assembly for a species, we can // automatically send a search for this organism+assembly - if (e !== undefined) { + if (typeof e !== "undefined") { switchAssemblies($("#dbSelect")[0].value); } } function buildSpeciesDropdown() { // Process the species select dropdowns _.each(uiState.genomes, function(genome) { newOpt = document.createElement("option"); newOpt.value = genome[0].organism; newOpt.label = trackHubSkipHubName(genome[0].organism); if (genome.some(function(assembly) { if (assembly.isCurated) { if (assembly.name === trackHubSkipHubName(db)) { return true; } @@ -690,31 +720,36 @@ alert("Warning: " + jsonData.warning); return true; } else { if (debugCartJson) { console.log('from server:\n', jsonData); } return true; } return false; } function updateStateAndPage(jsonData, doSaveHistory) { // Update uiState with new values and update the page. _.assign(uiState, jsonData); db = uiState.db; - if (jsonData.positionMatches !== undefined) { + if (typeof jsonData === "undefined" || jsonData === null) { + // now that the show more text is a link, a popstate event gets fired because the url changes + // we can safely return because there is no state to change + return; + } + if (typeof jsonData.positionMatches !== "undefined") { // clear the old resultHash uiState.resultHash = {}; _.each(uiState.positionMatches, function(match) { 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": ""}; @@ -731,31 +766,31 @@ updateStateAndPage(jsonData, true); } $("#spinner").remove(); } function handleErrorState(jqXHR, textStatus) { cart.defaultErrorCallback(jqXHR, textStatus); $("#spinner").remove(); } function sendUserSearch() { // User has clicked the search button, if they also entered a search // term, fire off a search cart.debug(debugCartJson); var searchTerm = $("#searchBarSearchString").val().replaceAll("\"",""); - if (searchTerm !== undefined && searchTerm.length > 0) { + if (typeof searchTerm !== 'undefined' && searchTerm.length > 0) { // put up a loading image $("#searchBarSearchButton").after(""); // redirect to hgBlat if the input looks like a DNA sequence // minimum length=19 so we do not accidentally redirect to hgBlat for a gene identifier // like ATG5 var dnaRe = new RegExp("^(>[^\n\r ]+[\n\r ]+)?(\\s*[actgnACTGN \n\r]{19,}\\s*)$"); if (dnaRe.test(searchTerm)) { var blatUrl = "hgBlat?type=BLAT%27s+guess&userSeq="+searchTerm; window.location.href = blatUrl; return false; } // if the user entered a plain position string like chr1:blah-blah, just // go to the old cgi/hgTracks