5a249cd50f592a3b7598110eec792cfc942fab3f max Wed Sep 9 08:52:06 2026 -0700 Address the v504 code review hgSession: an anonymous share name that arrives with the request is saved only when it is not already in the table. Every anonymous link sits under the one reserved user "l", so a name already there stays as it is and the caller is told so. The top-right Share dialog is unaffected, since it passes a name it has just reserved and such a name does not exist yet. Snapshot names are now left out of both My Sessions listings, which is what their "__" prefix has claimed all along. Share dialog: "Create link & copy" reports the copy instead of promising it. copyToClipboard says whether the text reached the clipboard, the dialog passes that on when a browser refuses, and it tries the asynchronous clipboard API before giving up. The preview is built with the same encoding the server uses, so a name holding a hyphen or a slash previews as the link that really gets made. Cancelling out of the name editor no longer copies a second time, and a reply with no link in it says so rather than showing "undefined". hgBlat: a second click on the share button while the first request is still out no longer mints a second snapshot session, and a box dismissed during the wait stays closed. Also: a snapshot moves a cart value into durable storage only when it is a trash path, the way sessionData's own callers check; sqlAddressMatch keeps to its own documented precondition when handed an empty address; alphaGenomeToWig compares its output with its input rather than with itself, rejects a position that is not all digits and skips an empty score; and hgc's default iframe width reaches the browser as one percent sign. refs #38294 diff --git src/hg/js/utils.js src/hg/js/utils.js index 5f27b64a1fd..1295e16b42f 100644 --- src/hg/js/utils.js +++ src/hg/js/utils.js @@ -37,57 +37,65 @@ span.style.marginLeft = "4px"; span.innerHTML = "" + "" + "" + "" + ""; addMouseover(span, text); return span; } 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); + * Returns true if the text really reached the clipboard. A browser will refuse a copy that no + * click of the user's asked for, so a caller that copies on its own behalf has to check. * */ 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'); + var ok = false; + try { + ok = document.execCommand('copy'); + } catch (e) { + ok = false; + } document.body.removeChild(textArea); + if (ok) buttonEl.innerHTML = 'Copied'; - ev.preventDefault(); + return ok; } function cfgPageOnVisChange(ev) { /* configuration page event listener when user changes visibility in dropdown */ if (ev.target.value === 'hide') ev.target.classList.replace("normalText", "hiddenText"); else ev.target.classList.replace("hiddenText", "normalText"); } function cfgPageAddListeners() { /* add event listener to dropdowns */ var els = document.querySelectorAll(".trackVis"); for (var i=0; i < els.length; i++) { var el = els[i];