d64d6f53f31908c3570247a94023297d44ce3ee4
hiram
Thu Sep 5 14:47:32 2024 -0700
add prompt text to input box and correctly do not place + in front of - refs #32596
diff --git src/hg/js/assemblySearch.js src/hg/js/assemblySearch.js
index 5cb6ac9..16595a5 100644
--- src/hg/js/assemblySearch.js
+++ src/hg/js/assemblySearch.js
@@ -1,810 +1,812 @@
// global variables:
/* jshint esnext: true */
var debug = true;
var measureTiming = true;
var urlParams;
var query = "";
var maxItemsOutput = 500;
var asmIdText = null;
// adjust default here and in assemblySearch.html
var browserExist = "mayExist";
var betterCommonName = null;
var comment = null;
var stateObject = {}; // maintain page state
var requestSubmitButton = null;
var completedAsmId = new Map(); // keep track of requests completed
// so they won't be repeated
// This function is called on DOMContentLoaded as the initialization
// procedure for first time page draw
document.addEventListener('DOMContentLoaded', function() {
// allow semi colon separators as well as ampersand
var urlArgList = window.location.search.replaceAll(";", "&");
urlParams = new URLSearchParams(urlArgList);
if (urlParams.has('level')) {
let asmLevel = urlParams.get('level');
document.getElementById('asmLevelAny').checked = true; // default
// only one of these four cases will be true
if (asmLevel === "complete")
document.getElementById('asmLevelComplete').checked = true;
if (asmLevel === "chromosome")
document.getElementById('asmLevelChromosome').checked = true;
if (asmLevel === "scaffold")
document.getElementById('asmLevelScaffold').checked = true;
if (asmLevel === "contig")
document.getElementById('asmLevelContig').checked = true;
}
if (urlParams.has('status')) {
let asmStatus = urlParams.get('status');
document.getElementById('statusAny').checked = true; // default
// only one of these three cases will be true
if (asmStatus === "latest")
document.getElementById('statusLatest').checked = true;
if (asmStatus === "replaced")
document.getElementById('statusReplaced').checked = true;
if (asmStatus === "suppressed")
document.getElementById('statusSuppressed').checked = true;
}
if (urlParams.has('category')) {
let refSeqCategory = urlParams.get('category');
document.getElementById('refSeqAny').checked = true; // default
// only one of these etwo cases will be true
if (refSeqCategory === "reference")
document.getElementById('refSeqReference').checked = true;
if (refSeqCategory === "representative")
document.getElementById('refSeqRepresentative').checked = true;
}
// default starts as hidden
stateObject.advancedSearchVisible = false;
advancedSearchVisible(false);
if (urlParams.has('advancedSearch')) {
let advancedSearch = urlParams.get('advancedSearch');
advancedSearchVisible(advancedSearch);
stateObject.advancedSearchVisible = advancedSearch;
}
if (urlParams.has('measureTiming')) { // accepts no value or other string
var measureValue = urlParams.get('measureTiming');
if ("0" === measureValue | "off" === measureValue) {
measureTiming = false;
} else { // any other string turns it on
measureTiming = true;
}
}
if (urlParams.has('browser')) {
var browserValue = urlParams.get('browser');
if ("mayExist" === browserValue) {
browserExist = "mayExist";
document.getElementById('mustExist').checked = true;
document.getElementById('notExist').checked = true;
} else if ("mustExist" === browserValue) {
browserExist = "mustExist";
document.getElementById('mustExist').checked = true;
document.getElementById('notExist').checked = false;
} else if ("notExist" === browserValue) {
browserExist = "notExist";
document.getElementById('mustExist').checked = false;
document.getElementById('notExist').checked = true;
// } else {
// not going to worry about this here today, but there should be
// a non-obtrusive dialog pop-up message about illegal arguments
// alert("warning: illegal value for browser=... must be one of: mayExist, mustExist, notExist");
}
}
if (urlParams.has('debug')) { // accepts no value or other string
var debugValue = urlParams.get('debug');
if ("0" === debugValue | "off" === debugValue) {
debug = false;
} else { // any other string turns it on
debug = true;
}
}
// add extra element to the help text bullet list for API example
if (debug) {
var searchTipList = document.getElementById("searchTipList");
// Create a new list item
var li = document.createElement("li");
li.innerHTML = "example API call: n/a";
// Append the new list item to the ordered list
searchTipList.appendChild(li);
}
var searchForm = document.getElementById('searchForm');
var advancedSearchButton = document.getElementById('advancedSearchButton');
var searchInput = document.getElementById('searchBox');
var clearButton = document.getElementById('clearSearch');
asmIdText = document.getElementById("formAsmId");
betterCommonName = document.getElementById("betterCommonName");
comment = document.getElementById("comment");
requestSubmitButton = document.getElementById("submitButton");
document.getElementById("modalFeedback").addEventListener("submit", checkForm, false);
modalInit();
var tableBody = document.getElementById('tableBody');
tableBody.innerHTML = '
(empty table)
';
clearButton.addEventListener('click', function() {
searchInput.value = ''; // Clear the search input field
});
searchForm.addEventListener('submit', function(event) {
event.preventDefault(); // Prevent form submission
var searchTerm = document.getElementById('searchBox').value;
var resultCountLimit = document.getElementById('maxItemsOutput');
var mustExist = document.getElementById('mustExist').checked;
var notExist = document.getElementById('notExist').checked;
browserExist = "mustExist";
if (mustExist && notExist) {
browserExist = "mayExist";
} else if (notExist) {
browserExist = "notExist";
}
makeRequest(searchTerm, browserExist, resultCountLimit.value);
});
advancedSearchButton.addEventListener('click', function() {
let shareThisSearch = document.getElementById('shareThisSearch');
shareThisSearch.innerHTML = " ";
let searchOptions = document.getElementById("advancedSearchOptions");
// I don't know why it is false the first time ?
if (! searchOptions.style.display || searchOptions.style.display === "none") {
advancedSearchVisible(true);
} else {
advancedSearchVisible(false);
}
});
// restore history on back button
window.addEventListener('popstate', function(e) {
var state = event.state;
if (state) {
stateObject.queryString = state.queryString;
stateObject.advancedSearchVisible = state.advancedSearchVisible;
stateObject.maxItemsOutput = state.maxItemsOutput;
stateObject.browser = state.browser;
stateObject.debug = state.debug;
stateObject.measureTiming = state.measureTiming;
stateObject.wordMatch = state.wordMatch;
stateObject.asmStatus = state.asmStatus;
stateObject.refSeqCategory = state.refSeqCategory;
stateObject.asmLevel = state.asmLevel;
stateObject.jsonData = state.jsonData;
document.getElementById('mustExist').checked = false;
document.getElementById('notExist').checked = false;
if (stateObject.browser === "mustExist") {
document.getElementById('mustExist').checked = true;
}
if (stateObject.browser === "notExist") {
document.getElementById('notExist').checked = true;
}
if (stateObject.browser === "mayExist") {
document.getElementById('mustExist').checked = true;
document.getElementById('notExist').checked = true;
}
advancedSearchVisible(stateObject.advancedSearchVisible);
if (stateObject.wordMatch === "allWords") {
document.getElementById("allWords").checked = true;
} else {
document.getElementById("anyWord").checked = true;
}
// only one of these four cases will be true
if (stateObject.asmStatus === "statusAny")
document.getElementById('statusAny').checked = true;
if (stateObject.asmStatus === "latest")
document.getElementById('statusLatest').checked = true;
if (stateObject.asmStatus === "replaced")
document.getElementById('statusReplaced').checked = true;
if (stateObject.asmStatus === "suppressed")
document.getElementById('statusSuppressed').checked = true;
// only one of these three cases will be true
if (stateObject.refSeqCategory === "refSeqAny")
document.getElementById('refSeqAny').checked = true;
if (stateObject.refSeqCategory === "reference")
document.getElementById('refSeqReference').checked = true;
if (stateObject.refSeqCategory === "representative")
document.getElementById('refSeqRepresentative').checked = true;
// only one of these five cases will be true
if (stateObject.asmLevel === "asmLevelAny")
document.getElementById('asmLevelAny').checked = true;
if (stateObject.asmLevel === "complete")
document.getElementById('asmLevelComplete').checked = true;
if (stateObject.asmLevel === "chromosome")
document.getElementById('asmLevelChromosome').checked = true;
if (stateObject.asmLevel === "scaffold")
document.getElementById('asmLevelScaffold').checked = true;
if (stateObject.asmLevel === "contig")
document.getElementById('asmLevelContig').checked = true;
document.getElementById('searchBox').value = stateObject.queryString;
populateTableAndInfo(JSON.parse(stateObject.jsonData));
// alert("state: '" + JSON.stringify(stateObject) + "'");
}
});
var tableHeader = document.getElementById('tableHeader');
headerRefresh(tableHeader);
if (urlParams.has('maxItemsOutput')) {
maxItemsOutput = parseInt(urlParams.get('maxItemsOutput'), 10);
if (maxItemsOutput < 1) {
maxItemsOutput = 1;
} else if (maxItemsOutput > 1000) {
maxItemsOutput = 1000;
}
document.getElementById('maxItemsOutput').value = maxItemsOutput;
}
if (urlParams.has('q')) {
query = urlParams.get('q');
if (query.length > 0) {
searchInput.value = query;
document.getElementById('submitSearch').click();
}
}
document.getElementById("measureTiming").style.display = "none";
});
// refresh the thead columns in the specified table
function headerRefresh(tableHead) {
// clear existing content
tableHead.innerHTML = '';
// re-populate header row - the sortable system added a class to
// the last sorted column, need to rebuild the headerRow to get the
// header back to pristine condition for the next sort
var headerRow = '
';
headerRow += '
View/ Request ⓘ"view" opens the genome browser for an existing assembly, "request" opens an assembly request form.
';
headerRow += '
English common name ⓘEnglish common name
';
headerRow += '
Scientific name ⓘscientific name
';
headerRow += '
NCBI Assembly ⓘLinks to NCBI resource record or UCSC downloads for local UCSC assemblies
';
headerRow += '
Year ⓘYear assembly was released.
';
headerRow += '
GenArk clade ⓘclade specification as found in the GenArk system.
';
headerRow += '
Description ⓘother meta data for this assembly.
';
headerRow += '
Status ⓘvarious other status
';
headerRow += '
';
tableHead.innerHTML = headerRow;
}
// call with visible true to make visible, false to hide
function advancedSearchVisible(visible) {
var advancedSearchButton = document.getElementById("advancedSearchButton");
var searchOptions = document.getElementById("advancedSearchOptions");
if (visible) {
searchOptions.style.display = "flex";
advancedSearchButton.textContent = "Hide advanced search options";
stateObject.advancedSearchVisible = true;
} else {
searchOptions.style.display = "none";
advancedSearchButton.textContent = "Show advanced search options";
stateObject.advancedSearchVisible = false;
}
}
// Function to highlight words in the result that match the words
// in the queryString. Work through each item of the rowData object,
// for each string, find out if it matches any of the words in the
// queryString. This is tricky, there are extra characters besides
// just [a-zA-Z] that are getting in the way of these matches, where
// the MySQL could match, for example: checking:
// 'HG02257' =? '(HG02257.pat'
// 'HG02257' =? 'HG02257.alt.pat.f1_v2'
// doesn't match here, but MySQL match did
function highlightMatch(queryString, rowData) {
// fixup the queryString words to get rid of the special characters
var words = queryString.split(/\s+/);
var wholeWord = []; // going to be words that match completely
var prefix = []; // going to be words that match prefix
for (let word of words) {
var noPrefix = word.replace(/^[-+]/, ''); // remove + - beginning
if (noPrefix.endsWith("*")) {
let subPrefix = noPrefix.replace(/\*$/, '').match(/(\w+)/g);
if (subPrefix.length > 1) {
let i = 0;
for ( ; i < subPrefix.length - 1; i++) {
wholeWord.push(subPrefix[i]);
}
prefix.push(subPrefix[i].replace(/\*$/, ''));
} else {
prefix.push(noPrefix.replace(/\*$/, ''));
}
} else {
wholeWord.push(noPrefix);
}
}
if (wholeWord.length > 0) {
for (let word of wholeWord) {
for (let key in rowData) {
if (rowData.hasOwnProperty(key)) {
if (typeof rowData[key] === 'string') {
let value = rowData[key];
let subWords = value.match(/(\w+)|(\W+)/g);
let newString = "";
let wholeSubs = word.match(/(\w+)/g);
if (wholeSubs.length > 0) {
for (let whole of wholeSubs) {
for (let subWord of subWords) {
if ( whole.toLowerCase() === subWord.toLowerCase() ) {
newString += "" + subWord + "";
} else {
newString += subWord;
}
}
newString = newString.trim();
if (newString !== rowData[key])
rowData[key] = newString;
} // for (let whole of wholeSubs)
} // if (wholeSubs.length > 0)
} // if (typeof rowData[key] === 'string')
} // if (rowData.hasOwnProperty(key))
} // for (let key in rowData)
} // for (let word of wholeWord)
} // if (wholeWord.length > 0)
if (prefix.length > 0) {
for (let word of prefix) {
for (let key in rowData) {
if (rowData.hasOwnProperty(key)) {
if (typeof rowData[key] === 'string') {
let value = rowData[key];
let subWords = value.match(/(\w+)|(\W+)/g);
let newString = "";
for (let subWord of subWords) {
if ( subWord.toLowerCase().startsWith(word.toLowerCase())) {
newString += "" + subWord + "";
} else {
newString += subWord;
}
}
newString = newString.trim();
if (newString !== rowData[key])
rowData[key] = newString;
}
}
}
}
}
}
// Function to generate the table and extra information
function populateTableAndInfo(jsonData) {
var tableHeader = document.getElementById('tableHeader');
var tableBody = document.getElementById('tableBody');
var metaData = document.getElementById('metaData');
document.getElementById('resultCounts').innerHTML = "";
document.getElementById('elapsedTime').innerHTML = "0";
// Clear existing table content
tableHeader.innerHTML = '';
tableBody.innerHTML = '';
metaData.innerHTML = '';
// Extract the genomic entries and the extra info
var genomicEntries = {};
var extraInfo = {};
for (var key in jsonData) {
if (jsonData[key].scientificName) {
genomicEntries[key] = jsonData[key];
} else {
extraInfo[key] = jsonData[key];
}
}
headerRefresh(tableHeader);
var count = 0;
for (var id in genomicEntries) {
highlightMatch(extraInfo.q, genomicEntries[id]);
var dataRow = '
';
var browserUrl = id;
var asmInfoUrl = id;
if (genomicEntries[id].browserExists) {
if (id.startsWith("GC")) {
browserUrl = "View";
asmInfoUrl = "" + id + "";
} else {
browserUrl = "View";
asmInfoUrl = "" + id + "";
}
dataRow += "
" + browserUrl + "
";
} else {
dataRow += "
";
}
dataRow += "
" + genomicEntries[id].commonName + "
";
dataRow += "
" + genomicEntries[id].scientificName + "
";
dataRow += "
" + asmInfoUrl + "
";
dataRow += "
" + genomicEntries[id].year + "
";
dataRow += "
" + genomicEntries[id].clade + "
";
dataRow += "
" + genomicEntries[id].description + "
";
var status = "
" + genomicEntries[id].priority + " ";
var hardSpace = " ";
if (genomicEntries[id].refSeqCategory) {
status += " " + genomicEntries[id].refSeqCategory;
hardSpace = "";
}
if (genomicEntries[id].versionStatus) {
status += " " + genomicEntries[id].versionStatus;
hardSpace = "";
}
if (genomicEntries[id].assemblyLevel) {
status += " " + genomicEntries[id].assemblyLevel;
hardSpace = "";
}
status += "