7b8b81e0a4869b24cdd8d7d31eaad33aacb87c0a hiram Wed Aug 21 09:03:50 2024 -0700 will now perform the request email properly and the html page can take arguments refs #32596 diff --git src/hg/js/assemblySearch.js src/hg/js/assemblySearch.js index f555584..461353b 100644 --- src/hg/js/assemblySearch.js +++ src/hg/js/assemblySearch.js @@ -1,179 +1,446 @@ +// global variables: + +var measureTiming = false; +var urlParams; +var searchFor = ""; +var maxItemsOutput = 500; +var asmIdText = null; +var betterCommonName = null; +var comment = null; +var requestSubmitButton = null; + +// 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 queryString = window.location.search.replaceAll(";", "&"); + urlParams = new URLSearchParams(queryString); + 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; + } + } + var searchForm = document.getElementById('searchForm'); 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(); 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('limitResultCount'); + var resultCountLimit = document.getElementById('maxItemsOutput'); var browserExist = "mustExist"; var mustExist = document.getElementById('mustExist').checked; var notExist = document.getElementById('notExist').checked; if (mustExist && notExist) { browserExist = "mayExist"; } else if (notExist) { browserExist = "notExist"; } var wordMatch = document.querySelector('input[name="wordMatch"]:checked').value; makeRequest(searchTerm, browserExist, resultCountLimit.value, wordMatch); }); + + 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('searchFor')) { + searchFor = urlParams.get('searchFor') + if (searchFor.length > 0) { + searchInput.value = searchFor; + 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 (count) ⓘbinomial scientific name
'; + headerRow += '
NCBI Assembly ⓘLinks to NCBI resource record.
'; + headerRow += '
genark clade ⓘclade specification as found in the GenArk system.
'; + headerRow += '
description ⓘother meta data for this assembly.
'; + headerRow += ''; + tableHead.innerHTML = headerRow; +} + // 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('searchString').innerHTML = ""; - document.getElementById('matchCounts').innerHTML = "0"; - document.getElementById('availableAssemblies').innerHTML = "0"; + 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 const genomicEntries = {}; const extraInfo = {}; for (const key in jsonData) { if (jsonData[key].scientificName) { genomicEntries[key] = jsonData[key]; } else { extraInfo[key] = jsonData[key]; } } - // 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'; - headerRow += 'English common name'; - headerRow += 'scientific name'; - headerRow += 'assembly'; - headerRow += 'clade'; - headerRow += 'description'; - headerRow += ''; - tableHeader.innerHTML = headerRow; + headerRefresh(tableHeader); var count = 0; for (const id in genomicEntries) { var dataRow = ''; var browserUrl = id; var ncbiUrl = id; if (genomicEntries[id].browserExists) { if (id.startsWith("GC")) { browserUrl = "view"; ncbiUrl = "" + id + "" } else { browserUrl = "view"; } dataRow += "" + browserUrl + ""; } else { - dataRow += "request"; + dataRow += ""; } dataRow += "" + genomicEntries[id].scientificName + ""; dataRow += "" + genomicEntries[id].commonName + ""; dataRow += "" + ncbiUrl + ""; dataRow += "" + genomicEntries[id].clade + ""; dataRow += "" + genomicEntries[id].description + ""; dataRow += ''; tableBody.innerHTML += dataRow; } var dataTable = document.getElementById('dataTable'); sorttable.makeSortable(dataTable); - document.getElementById('searchString').innerHTML = extraInfo['genomeSearch']; - document.getElementById('matchCounts').innerHTML = extraInfo['totalMatchCount'].toLocaleString(); - document.getElementById('availableAssemblies').innerHTML = extraInfo['availableAssemblies'].toLocaleString(); + var itemCount = parseInt(extraInfo['itemCount'], 10); + var totalMatchCount = parseInt(extraInfo['totalMatchCount'], 10); + var availableAssemblies = parseInt(extraInfo['availableAssemblies'], 10); + + var resultCounts = "results for search string: '" + extraInfo['genomeSearch'] + "', "; + if ( itemCount === totalMatchCount ) { + resultCounts += "showing " + itemCount.toLocaleString() + " match results, "; + } else { + resultCounts += "showing " + itemCount.toLocaleString() + " match results "; + resultCounts += "from " + totalMatchCount.toLocaleString() + " total matches, "; + } + resultCounts += "out of " + availableAssemblies.toLocaleString() + " total number of assemblies"; + document.getElementById('resultCounts').innerHTML = resultCounts; + if (measureTiming) { var etMs = extraInfo['elapsedTimeMs']; - var elapsedTime = etMs.toLocaleString() + " milliseconds"; + var elapsedTime = "" + etMs.toLocaleString() + " milliseconds"; if ( etMs > 1000 ) { var etSec = etMs/1000; - elapsedTime = etSec.toFixed(2) + " seconds"; + elapsedTime = "" + etSec.toFixed(2) + " seconds"; } document.getElementById('elapsedTime').innerHTML = elapsedTime.toLocaleString(); + document.getElementById("measureTiming").style.display = "inline"; + } else { + document.getElementById("measureTiming").style.display = "none"; + } } // function populateTableAndInfo(jsonData) function enableButtons() { - document.getElementById('submitButton').disabled = false; + document.getElementById('submitSearch').disabled = false; document.getElementById('clearSearch').disabled = false; } function disableButtons() { - document.getElementById('submitButton').disabled = true; + document.getElementById('submitSearch').disabled = true; document.getElementById('clearSearch').disabled = true; } +function parentTable(e) { + while (e) { + e = e.parentNode; + if (e.tagName.toLowerCase() === 'table') { + return e; + } + } + return undefined; +} + +function whichRow(e) { + while (e) { + if (e.rowIndex) { + return e.rowIndex; + } + e = e.parentNode; + } + return undefined; +} + +function closeModal(e) +{ + document.getElementById("modalWrapper").className = ""; + if (e.preventDefault) { + e.preventDefault(); + } else { + e.returnValue = false; + } +} + +function clickHandler(e) { + if(!e.target) e.target = e.srcElement; + if(e.target.tagName === "DIV") { + if(e.target.id != "modalWindow") closeModal(e); + } +} + +function keyHandler(e) { + if(e.keyCode === 27) closeModal(e); +} + +function modalInit() { + if(document.addEventListener) { + document.getElementById("modalClose").addEventListener("click", closeModal, false); + document.addEventListener("click", clickHandler, false); + document.addEventListener("keydown", keyHandler, false); + } else { + document.getElementById("modalClose").attachEvent("onclick", closeModal); + document.attachEvent("onclick", clickHandler); + document.attachEvent("onkeydown", keyHandler); + } +} + +function failedRequest(url) { + requestSubmitButton.value = "request failed"; + requestSubmitButton.disabled = true; +// garStatus.innerHTML = "FAILED: '" + url + "'"; +} + +function sendRequest(name, email, asmId, betterName, comment) { + var urlComponents = encodeURIComponent(name) + "&email=" + encodeURIComponent(email) + "&asmId=" + encodeURIComponent(asmId) + "&betterName=" + encodeURIComponent(betterName) + "&comment=" + encodeURIComponent(comment); + + var url = "/cgi-bin/asr?name=" + urlComponents; +alert("request url: '" + url + "'"); +// information about escaping characters: +// https://stackoverflow.com/questions/10772066/escaping-special-character-in-a-url/10772079 +// encodeURI() will not encode: ~!@#$&*()=:/,;?+' +// encodeURIComponent() will not encode: ~!*()' + +// var encoded = encodeURIComponent(url); +// encoded = encoded.replace("'","’"); +// var encoded = encodeURI(cleaner); + var xmlhttp = new XMLHttpRequest(); + xmlhttp.onreadystatechange = function() { + if (4 === this.readyState && 200 === this.status) { + requestSubmitButton.value = "request completed"; + } else { + if (4 === this.readyState && 404 === this.status) { + failedRequest(url); + } + } + }; + xmlhttp.open("GET", url, true); + xmlhttp.send(); + +} // sendRequest: function(name, email. asmId) + + +function checkForm(e) { +alert("checkForm"); + if (requestSubmitButton.value === "request completed") { + if (e.preventDefault) { + e.preventDefault(); + } else { + e.returnValue = false; + } + closeModal(e); + return; + } + var form = (e.target) ? e.target : e.srcElement; + if(form.name.value === "") { + alert("Please enter your Name"); + form.name.focus(); + if (e.preventDefault) { + e.preventDefault(); + } else { + e.returnValue = false; + } + return; + } + if(form.email.value === "") { + alert("Please enter a valid Email address"); + form.email.focus(); + if (e.preventDefault) { + e.preventDefault(); + } else { + e.returnValue = false; + } + return; + } +// validation regex from: +// https://www.w3resource.com/javascript/form/email-validation.php +// another example from +// https://www.simplilearn.com/tutorials/javascript-tutorial/email-validation-in-javascript +// var validRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/; +// another example from +// https://ui.dev/validate-email-address-javascript/ +// return /\S+@\S+\.\S+/.test(email) +// return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) +// var re = /^[^\s@]+@[^\s@]+$/; +// if (re.test(email)) { OK } + +// var validEmail = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/; + var validEmail = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/; + if(! validEmail.test(form.email.value)) { + alert("You have entered an invalid email address!"); + form.email.focus(); + if (e.preventDefault) { + e.preventDefault(); + } else { + e.returnValue = false; + } + return; + } + sendRequest(form.name.value, form.email.value, asmIdText.textContent, betterCommonName.value, comment.value); + if (e.preventDefault) { + e.preventDefault(); + } else { + e.returnValue = false; + } +} // checkForm: function(e) + +function asmOpenModal(e) { + if (e.name) { + var modalWindow = document.getElementById("modalWindow"); + var pTable = parentTable(e); + var thisRow = whichRow(e); + var colGroup = document.getElementById('colDefinitions'); + var comName = "n/a"; + var sciName = "n/a"; + var descr = "n/a"; + var i = 0; + for (i = 0; i < colGroup.children.length; i++) { + if (colGroup.children[i].id === "comName") { + comName = pTable.rows[thisRow].cells[i].innerText; + } else if (colGroup.children[i].id === "sciName") { + sciName = pTable.rows[thisRow].cells[i].innerText; + } else if (colGroup.children[i].id === "description") { + descr = pTable.rows[thisRow].cells[i].innerText; + } + } + document.getElementById("commonName").textContent = comName; + document.getElementById("formSciName").textContent = sciName; + document.getElementById("formAsmId").textContent = e.name; + document.getElementById("comment").textContent = descr; + requestSubmitButton.value = "Submit request"; + document.getElementById("modalWrapper").className = "overlay"; + requestSubmitButton.disabled = false; + var overflow = modalWindow.offsetHeight - document.documentElement.clientHeight; + if (overflow > 0) { + modalWindow.style.maxHeight = (parseInt(window.getComputedStyle(modalWindow).height) - overflow) + "px"; + } + modalWindow.style.marginTop = (-modalWindow.offsetHeight)/2 + "px"; + modalWindow.style.marginLeft = (-modalWindow.offsetWidth)/2 + "px"; + } + if (e.preventDefault) { + e.preventDefault(); + } else { + e.returnValue = false; + } +} + function makeRequest(query, browserExist, resultLimit, wordMatch) { // Disable the submit button disableButtons(); var queryString = query; // for allWords, place + sign in front of each word if not already there if (wordMatch === "allWords") { const words = query.split(/\s+/); if (words.length > 1) { // not needed on only one word var queryPlus = ""; // compose new query string words.forEach(word => { if (word.startsWith("+")) { queryPlus += " " + word; // space separated each word } else { queryPlus += " +" + word; } }); queryString = queryPlus.trimStart(); // remove first space character } } // Show the wait spinner document.querySelector(".submitContainer").classList.add("loading"); document.getElementById("loadingSpinner").style.display = "block"; var xhr = new XMLHttpRequest(); - var url = "/cgi-bin/hubApi/findGenome?genomeSearch=" + encodeURIComponent(queryString); + var urlPrefix = "/cgi-bin/hubApi" + var url = "/findGenome?genomeSearch=" + encodeURIComponent(queryString); url += ";browser=" + browserExist; url += ";maxItemsOutput=" + resultLimit; - xhr.open('GET', url, true); + var apiUrl = "" + url + ""; + document.getElementById("recentAjax").innerHTML = apiUrl; + + xhr.open('GET', urlPrefix + url, true); xhr.onload = function() { if (xhr.status === 200) { // Hide the wait spinner once the AJAX request is complete document.querySelector(".submitContainer").classList.remove("loading"); document.getElementById("loadingSpinner").style.display = "none"; enableButtons(); var data = JSON.parse(xhr.responseText); populateTableAndInfo(data); } else { // Hide the wait spinner once the AJAX request is complete document.querySelector(".submitContainer").classList.remove("loading"); document.getElementById("loadingSpinner").style.display = "none"; enableButtons(); var tableBody = document.getElementById('tableBody'); var metaData = document.getElementById('metaData'); tableBody.innerHTML = ''; metaData.innerHTML = ''; metaData.innerHTML = "no results found for query: '" + queryString + "'"; - document.getElementById('searchString').innerHTML = queryString; - document.getElementById('matchCounts').innerHTML = "0"; - document.getElementById('availableAssemblies').innerHTML = "0"; + document.getElementById('resultCounts').innerHTML = ""; document.getElementById('elapsedTime').innerHTML = "0"; } }; xhr.onerror = function() { alert('console.log("Request failed")'); console.log('Request failed'); }; xhr.send(); }