// Copy-to-clipboard for `.js-copy` buttons, loaded by the Domains page only // (domains-registry D9). // // Delegated on document so it survives the claim status view's 5s HTMX // self-swap, and CSP-safe: strict CSP drops inline handlers, so every button // carries its payload in data-copy and nothing else. // // An integration's dashboard card that needs the same behavior ships its own // copy inside its card script (the member-dashboard contract requires card // scripts to come from the integration's own static mount), so this handler // may be functionally duplicated by integration bundles. Core's copy and an // integration's never load on the same page — this one belongs to core // surfaces — so there is no double-fire and neither may be deleted in favor // of the other. (function () { document.addEventListener("click", function (evt) { var button = evt.target.closest(".js-copy"); if (!button || !button.dataset.copy) return; // Clipboard API requires a secure context (HTTPS or localhost — both true // for every supported deployment). if (!navigator.clipboard) return; var showFeedback = function (html) { if (button.dataset.copyRestore) return; // feedback already showing button.dataset.copyRestore = button.innerHTML; button.innerHTML = html; setTimeout(function () { // The 5s poll may have replaced the node; only restore if it's live. if (button.isConnected && button.dataset.copyRestore) { button.innerHTML = button.dataset.copyRestore; delete button.dataset.copyRestore; } }, 1500); }; navigator.clipboard .writeText(button.dataset.copy) .then(function () { showFeedback('Copied ✓'); }) .catch(function () { // Permission denied or document unfocused — tell the member instead // of failing silently (and swallow the rejection). showFeedback('Copy failed'); }); }); })();