562b1b24f9d7cf5157c799f733ba219e2e9f7da9
max
  Wed Sep 9 09:06:30 2026 -0700
Assembly search page uses the shared copyToClipboard instead of its own copy

The page carried a copy of copyToClipboard marked "borrowed this code from
utils.js", and the two had already drifted apart: the fix that stops the button
claiming a copy that a browser refused went into one and not the other.  The
page now loads utils.js, as nine other static pages already do, and its own
copy is gone.  jquery is already loaded by the page header, so nothing else was
needed.

The page also declared a global named debug, which utils.js declares too.  Both
start out false and the two uses in utils.js are in functions this page never
calls, so nothing was broken, but the page flag is now searchDebug.  The debug
URL parameter and stateObject.debug keep their names.

refs #38294

diff --git src/hg/js/assemblySearch.js src/hg/js/assemblySearch.js
index 1f767e4dc78..f9cb67a62be 100644
--- src/hg/js/assemblySearch.js
+++ src/hg/js/assemblySearch.js
@@ -1,20 +1,20 @@
 // global variables:
 
 /* jshint esnext: true */
 
-var debug = false;
+var searchDebug = false;   // not "debug": utils.js owns that global name
 var measureTiming = false;
 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
 var maxLength = 1024;	// limit all incoming strings to this length
 
@@ -87,38 +87,38 @@
           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;
+         searchDebug = false;
        } else {			// any other string turns it on
-         debug = true;
+         searchDebug = true;
        }
     }
 
     // add extra element to the help text bullet list for API example
-    if (debug) {
+    if (searchDebug) {
       var searchTipList = document.getElementById("searchTipList");
       // Create a new list item
       var li = document.createElement("li");
       li.innerHTML = "example API call: <span id=\"recentAjax\">n/a</span>";
       // 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");
     asmIdText.textContent = asmIdText.textContent.substring(0,maxLength);
     betterCommonName = document.getElementById("betterCommonName");
@@ -554,83 +554,30 @@
         "comment="    + encodeURIComponent(comment);
 
     var xmlhttp = new XMLHttpRequest();
     xmlhttp.onreadystatechange = function() {
          if (4 === this.readyState && 200 === this.status) {
             requestSubmitButton.value = "request completed";
          } else if (4 === this.readyState && this.status >= 400) {
             failedRequest(url);
          }
        };
     xmlhttp.open("GET", url, true);
     xmlhttp.send();
 
 }  //      sendRequest: function(name, email, asmId, betterName, comment)
 
-// 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();
-    var ok = false;
-    try {
-        ok = document.execCommand('copy');
-    } catch (e) {
-        ok = false;
-    }
-    document.body.removeChild(textArea);
-    if (ok) {
-        /* Say "Copied", then put the button's own label back after three seconds so it is clear
-         * that the button can be used again.  This page does not load utils.js, so it cannot call
-         * copyButtonSaysCopied() there; keep the two in step. */
-        if (buttonEl.copyButtonLabel === undefined)
-            buttonEl.copyButtonLabel = buttonEl.innerHTML;
-        if (buttonEl.copyButtonTimer)
-            clearTimeout(buttonEl.copyButtonTimer);
-        buttonEl.innerHTML = 'Copied';
-        buttonEl.copyButtonTimer = setTimeout(function() {
-            buttonEl.innerHTML = buttonEl.copyButtonLabel;
-            buttonEl.copyButtonTimer = null;
-        }, 3000);
-    }
-    return ok;
-}
-
 // 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") {
@@ -794,52 +741,52 @@
     // Show the wait spinner
     document.querySelector(".submitContainer").classList.add("loading");
     document.getElementById("loadingSpinner").style.display = "block";
 
     var xhr = new XMLHttpRequest();
     var urlPrefix = "/cgi-bin/hubApi";
     var historyUrl = "?q=" + encodeURIComponent(queryString);
     historyUrl += ";browser=" + browserExist;
     historyUrl += ";maxItemsOutput=" + resultLimit;
     if (asmStatus !== "statusAny")	// default is any assembly status
        historyUrl += ";status=" + asmStatus;	// something specific is being requested
     if (refSeqCategory !== "refSeqAny")	// default is any RefSeq category
        historyUrl += ";category=" + refSeqCategory;	// something specific
     if (asmLevel !== "asmLevelAny")	// default is any level of assembly
        historyUrl += ";level=" + asmLevel;	// something specific
-    if (debug)
+    if (searchDebug)
        historyUrl += ";debug=1";
     if (measureTiming)
        historyUrl += ";measureTiming=1";
 
     var url = "/findGenome" + historyUrl;
 
-    if (debug) {
+    if (searchDebug) {
       var apiUrl = "<a href='" + urlPrefix + url + "' target=_blank>" + url + "</a>";
       document.getElementById("recentAjax").innerHTML = apiUrl;
     }
     stateObject.queryString = queryString;
     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.debug = searchDebug;
     stateObject.measureTiming = measureTiming;
     stateObject.wordMatch = wordMatch;
     stateObject.asmStatus = asmStatus;
     stateObject.refSeqCategory = refSeqCategory;
     stateObject.asmLevel = asmLevel;
     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");