41b27825db55ab0699dacce3c18c34cf791e6428
hiram
  Sat Sep 7 23:49:38 2024 -0700
correctly manipulate the copy to clipboard link refs #32596

diff --git src/hg/js/assemblySearch.js src/hg/js/assemblySearch.js
index 16595a5..39c4bcb 100644
--- src/hg/js/assemblySearch.js
+++ src/hg/js/assemblySearch.js
@@ -45,30 +45,33 @@
        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
+    let copyIcon0 = document.getElementById('copyIcon0');
+    stateObject.copyIcon0 = copyIcon0.innerHTML;
+    document.getElementById('urlCopyLink').style.display = "none";
     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;
        }
     }
@@ -134,32 +137,31 @@
 
         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 = " ";
+       document.getElementById('urlCopyLink').style.display = "none";
        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;
@@ -455,30 +457,31 @@
     }
     resultCounts += "<em>out of </em><b>" + availableAssemblies.toLocaleString() + "</b> <em>total number of assemblies</em>";
     document.getElementById('resultCounts').innerHTML = resultCounts;
     if (measureTiming) {
       var etMs = extraInfo.elapsedTimeMs;
       var elapsedTime = "<b>" + etMs.toLocaleString() + "</b> <em>milliseconds</em>";
       if ( etMs > 1000 ) {
          var etSec = etMs/1000;
          elapsedTime = "<b>" + etSec.toFixed(2) + "</b> <em>seconds</em>";
       }
       document.getElementById('elapsedTime').innerHTML = elapsedTime.toLocaleString();
       document.getElementById("measureTiming").style.display = "inline";
     } else {
       document.getElementById("measureTiming").style.display = "none";
     }
+    document.getElementById('urlCopyLink').style.display = "inline";
 }	//	function populateTableAndInfo(jsonData)
 
 function enableButtons() {
     document.getElementById('submitSearch').disabled = false;
     document.getElementById('clearSearch').disabled = false;
 }
 
 function disableButtons() {
     document.getElementById('submitSearch').disabled = true;
     document.getElementById('clearSearch').disabled = true;
 }
 
 function parentTable(e) {
   while (e) {
       e = e.parentNode;
@@ -553,30 +556,65 @@
     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)
 
+// borrowed this code from utils.js
+function copyToClipboard(ev) {
+    /* copy a piece of text to clipboard. event.target is some DIV or SVG that is an icon.
+     * The attribute data-target of this element is the ID of the element that contains the text to copy.
+     * The text is either in the attribute data-copy or the innerText.
+     * see C function printCopyToClipboardButton(iconId, targetId);
+     * */
+
+    ev.preventDefault();
+
+    var buttonEl = ev.target.closest("button"); // user can click SVG or BUTTON element
+
+    var targetId = buttonEl.getAttribute("data-target");
+    if (targetId===null)
+        targetId = ev.target.parentNode.getAttribute("data-target");
+    var textEl = document.getElementById(targetId);
+    var text = textEl.getAttribute("data-copy");
+    if (text===null)
+        text = textEl.innerText;
+
+    var textArea = document.createElement("textarea");
+    textArea.value = text;
+    // Avoid scrolling to bottom
+    textArea.style.top = "0";
+    textArea.style.left = "0";
+    textArea.style.position = "fixed";
+    document.body.appendChild(textArea);
+    textArea.focus();
+    textArea.select();
+    document.execCommand('copy');
+    document.body.removeChild(textArea);
+    buttonEl.innerHTML = 'Copied';
+    ev.preventDefault();
+}
+
 // do not allow both checkboxes to go off
 function atLeastOneCheckBoxOn(e) {
   var mustExist = document.getElementById('mustExist').checked;
   var notExist = document.getElementById('notExist').checked;
   if (! mustExist && ! notExist ) {  // turn on the other one when both off
      if (e.name === "mustExist") {
        document.getElementById('notExist').checked = true;
      } else {
        document.getElementById('mustExist').checked = true;
      }
   }
 }
 
 function checkForm(e) {
   if (requestSubmitButton.value === "request completed") {
@@ -679,55 +717,64 @@
     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 optionsChange(e) {
   // options are changing, share URL is no longer viable, eliminate it
-  let shareThisSearch = document.getElementById('shareThisSearch');
-  shareThisSearch.innerHTML = "&nbsp;";
+  document.getElementById('copyIcon0').innerHTML = stateObject.copyIcon0;
+  document.getElementById('urlCopyLink').style.display = "none";
 }
 
 function makeRequest(query, browserExist, resultLimit) {
     // Disable the submit button
     disableButtons();
     var wordMatch = document.querySelector('input[name="wordMatch"]:checked').value;
     var asmStatus = document.querySelector('input[name="asmStatus"]:checked').value;
     var refSeqCategory = document.querySelector('input[name="refSeqCategory"]:checked').value;
     var asmLevel = document.querySelector('input[name="asmLevel"]:checked').value;
 
     // start with what the user requested:
     var queryString = query;
 
     // for allWords, place + sign in front of each word if not already there
     if (wordMatch === "allWords") {
       var words = queryString.split(/\s+/);
       if (words.length > 1) {	// not needed on only one word
         var queryPlus = "";	// compose new query string
+        let inQuote = false;
         words.forEach(function(word) {
-          if (word.startsWith("-")) {
-            queryPlus += " " + word; // do not add + to -
-          } else if (word.startsWith("+")) {
+          if (word.match(/^[-+]?"/)) {
+             if (word.match(/^[-+]/))
+                queryPlus += " " + word; // do not add + to - or + already there
+             else
+                queryPlus += " +" + word;
+             inQuote = true;
+          } else if (inQuote) {
              queryPlus += " " + word; // space separates each word
+             if (word.endsWith('"'))
+                inQuote = false;
+          } else if (word.match(/[^-+]/)) {
+            queryPlus += " " + word; // do not add + to - or + already there
           } else {
             queryPlus += " +" + word;
           }
         });
         queryString = queryPlus.trim();
       }
     }
     // remove stray white space from beginning or end
     queryString = queryString.trim();
 
     // Show the wait spinner
     document.querySelector(".submitContainer").classList.add("loading");
     document.getElementById("loadingSpinner").style.display = "block";
 
     var xhr = new XMLHttpRequest();
@@ -756,54 +803,55 @@
     var searchOptions = document.getElementById("advancedSearchOptions");
     if (searchOptions.style.display === "flex") {
         stateObject.advancedSearchVisible = true;
         historyUrl += ";advancedSearch=true";
     } else {
         stateObject.advancedSearchVisible = false;
     }
     stateObject.maxItemsOutput = maxItemsOutput;
     stateObject.browser = browserExist;
     stateObject.debug = debug;
     stateObject.measureTiming = measureTiming;
     stateObject.wordMatch = wordMatch;
     stateObject.asmStatus = asmStatus;
     stateObject.refSeqCategory = refSeqCategory;
     stateObject.asmLevel = asmLevel;
-    let shareThisSearch = document.getElementById('shareThisSearch');
-    let thisPageHref = "<a href='assemblySearch.html";
-    thisPageHref += historyUrl;
-    thisPageHref += "'>Share this search</a>";
-    shareThisSearch.innerHTML = thisPageHref;
+    let urlText0 = document.getElementById('urlText0');
+    let hostName = window.location.hostname;
+    urlText0.innerHTML = "https://" + hostName + "/assemblySearch.html" + historyUrl;
 
     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";
+            document.getElementById('copyIcon0').innerHTML = stateObject.copyIcon0;
             enableButtons();
 
             stateObject.jsonData = xhr.responseText;
             history.pushState(stateObject, '', historyUrl);
 
             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";
+            document.getElementById('copyIcon0').innerHTML = stateObject.copyIcon0;
+            document.getElementById('urlCopyLink').style.display = "none";
             enableButtons();
 	    var tableBody = document.getElementById('tableBody');
             tableBody.innerHTML = "<tr><td style='text-align:center;' colspan=8><b>no results found for query: <em>'" + queryString + "'</em></b></td></tr>";
             var metaData = document.getElementById('metaData');
             metaData.innerHTML = '';
 //            metaData.innerHTML = "<b>no results found for query: '" + queryString + "'</b>";
             document.getElementById('resultCounts').innerHTML = "";
             document.getElementById('elapsedTime').innerHTML = "0";
         }
     };
 
     xhr.onerror = function() {
         alert('console.log("Request failed")');
         console.log('Request failed');
     };