This repository has been archived on 2026-10-28. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
karldns.de/karldns/assets/clipboard.js
T

88 lines
2.9 KiB
JavaScript

const legacyCopy = (value) => {
const textarea = document.createElement("textarea");
textarea.value = value;
textarea.setAttribute("readonly", "");
textarea.style.position = "fixed";
textarea.style.top = "-9999px";
textarea.style.left = "-9999px";
document.body.append(textarea);
textarea.select();
textarea.setSelectionRange(0, textarea.value.length);
try {
return document.execCommand("copy");
} finally {
textarea.remove();
}
};
const copyText = async (value) => {
if (navigator.clipboard && window.isSecureContext) {
await navigator.clipboard.writeText(value);
return true;
}
return legacyCopy(value);
};
const showCopyStatus = (button, text, className, duration = 1200) => {
const label = button.querySelector("[data-copy-label]");
const oldLabel = label ? label.textContent : "";
button.classList.add(className);
if (label) {
label.setAttribute("aria-live", "polite");
label.textContent = text;
}
setTimeout(() => {
button.classList.remove(className);
if (label) label.textContent = oldLabel;
}, duration);
};
document.addEventListener("click", async (event) => {
document.querySelectorAll("details.copy-menu[open]").forEach((menu) => {
if (!menu.contains(event.target)) menu.removeAttribute("open");
});
const toggle = event.target.closest("[data-secret-toggle]");
if (toggle) {
const group = toggle.closest(".copy-field");
const field = group ? group.querySelector("[data-secret-input], [data-secret-value]") : null;
if (!field) return;
if (field.matches("[data-secret-input]")) {
const masked = field.type === "password";
field.type = masked ? "text" : "password";
toggle.textContent = masked ? "Hide" : "View";
toggle.setAttribute("aria-label", masked ? "Hide secret value" : "Show secret value");
} else {
const masked = field.dataset.secretMasked !== "false";
field.dataset.secretMasked = masked ? "false" : "true";
field.textContent = masked ? (field.dataset.secretValue || "") : "********";
toggle.textContent = masked ? "Hide" : "View";
toggle.setAttribute("aria-label", masked ? "Hide secret value" : "Show secret value");
}
return;
}
const button = event.target.closest("[data-copy]");
if (!button) return;
const menu = button.closest("details.copy-menu");
try {
const copied = await copyText(button.dataset.copy || "");
if (!copied) throw new Error("copy failed");
showCopyStatus(button, "Copied", "is-copied");
if (menu) {
setTimeout(() => {
menu.removeAttribute("open");
}, 250);
}
} catch (_) {
showCopyStatus(button, "Copy failed", "is-copy-error", 1800);
}
});
document.addEventListener("keydown", (event) => {
if (event.key !== "Escape") return;
document.querySelectorAll("details.copy-menu[open]").forEach((menu) => {
menu.removeAttribute("open");
});
});