83 lines
2.2 KiB
JavaScript
83 lines
2.2 KiB
JavaScript
const overlay = document.createElement("section");
|
|
overlay.className = "error-overlay";
|
|
overlay.hidden = true;
|
|
overlay.innerHTML = `
|
|
<div class="error-overlay-header">
|
|
<strong>JavaScript error</strong>
|
|
<div class="error-overlay-actions">
|
|
<button type="button" data-action="copy">Copy</button>
|
|
<button type="button" data-action="dismiss" aria-label="Dismiss error">Dismiss</button>
|
|
</div>
|
|
</div>
|
|
<pre></pre>
|
|
`;
|
|
|
|
const messageNode = overlay.querySelector("pre");
|
|
const copyButton = overlay.querySelector("[data-action='copy']");
|
|
const dismissButton = overlay.querySelector("[data-action='dismiss']");
|
|
|
|
copyButton.addEventListener("click", async () => {
|
|
await copyText(messageNode.textContent);
|
|
showCopyStatus();
|
|
});
|
|
|
|
dismissButton.addEventListener("click", () => {
|
|
overlay.hidden = true;
|
|
});
|
|
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
document.body.append(overlay);
|
|
});
|
|
|
|
window.addEventListener("error", (event) => {
|
|
showError(formatErrorEvent(event));
|
|
});
|
|
|
|
window.addEventListener("unhandledrejection", (event) => {
|
|
showError(formatReason(event.reason));
|
|
});
|
|
|
|
function showError(message) {
|
|
messageNode.textContent = message;
|
|
copyButton.textContent = "Copy";
|
|
overlay.hidden = false;
|
|
}
|
|
|
|
async function copyText(value) {
|
|
if (navigator.clipboard?.writeText) {
|
|
await navigator.clipboard.writeText(value);
|
|
return;
|
|
}
|
|
|
|
const textArea = document.createElement("textarea");
|
|
textArea.value = value;
|
|
textArea.setAttribute("readonly", "");
|
|
textArea.className = "error-overlay-copy-buffer";
|
|
document.body.append(textArea);
|
|
textArea.select();
|
|
document.execCommand("copy");
|
|
textArea.remove();
|
|
}
|
|
|
|
function showCopyStatus() {
|
|
copyButton.textContent = "Copied";
|
|
|
|
window.setTimeout(() => {
|
|
copyButton.textContent = "Copy";
|
|
}, 1400);
|
|
}
|
|
|
|
function formatErrorEvent(event) {
|
|
const location = [event.filename, event.lineno, event.colno].filter(Boolean).join(":");
|
|
const detail = formatReason(event.error ?? event.message);
|
|
return location ? `${detail}\n\n${location}` : detail;
|
|
}
|
|
|
|
function formatReason(reason) {
|
|
if (reason instanceof Error) {
|
|
return reason.stack ?? `${reason.name}: ${reason.message}`;
|
|
}
|
|
|
|
return String(reason);
|
|
}
|