org-howto/ext/fniessen/readtheorg/js/code-copy.js
Roland Conybeare 0981466ff2
All checks were successful
Deploy / publish (push) Successful in 14s
vendor readtheorg from new location + vendor for next time.
All formatting disappeared, beause the upstream source moved.
2026-08-16 12:27:52 -04:00

118 lines
2.8 KiB
JavaScript

/**
* Code Copy Button Functionality
* Adds copy buttons to code blocks for easy clipboard copying.
*/
document.addEventListener('DOMContentLoaded', () => {
const codeBlocks = document.querySelectorAll(
'pre.src, .org-src-container pre'
);
codeBlocks.forEach((codeBlock) => {
// Create copy button.
const copyButton = document.createElement('button');
copyButton.className = 'copy-code-button';
copyButton.type = 'button';
copyButton.textContent = 'Copy';
copyButton.setAttribute('aria-label', 'Copy code to clipboard');
// Position the button.
const container =
codeBlock.closest('.org-src-container') || codeBlock.parentElement;
if (!container) {
return;
}
container.style.position = 'relative';
container.appendChild(copyButton);
// Add click handler.
copyButton.addEventListener('click', async () => {
const codeText = cleanCodeText(codeBlock);
try {
await copyText(codeText);
showCopyFeedback(copyButton, true);
} catch (error) {
console.error('Failed to copy code:', error);
showCopyFeedback(copyButton, false);
}
});
});
});
/**
* Extracts and cleans text from a code block.
*
* @param {HTMLElement} codeBlock
* @returns {string}
*/
function cleanCodeText(codeBlock) {
const codeText = codeBlock.textContent || codeBlock.innerText || '';
return codeText
.split('\n')
.map((line) => line.replace(/^\s*\d+:\s/, ''))
.join('\n')
.trim();
}
/**
* Copies text using the Clipboard API, with a fallback for older browsers.
*
* @param {string} text
* @returns {Promise<void>}
*/
async function copyText(text) {
if (navigator.clipboard && window.isSecureContext) {
await navigator.clipboard.writeText(text);
return;
}
fallbackCopy(text);
}
/**
* Copies text using a temporary textarea.
*
* @param {string} text
*/
function fallbackCopy(text) {
const textArea = document.createElement('textarea');
textArea.value = text;
textArea.setAttribute('readonly', '');
textArea.style.position = 'fixed';
textArea.style.top = '0';
textArea.style.left = '-9999px';
document.body.appendChild(textArea);
textArea.select();
const successful = document.execCommand('copy');
document.body.removeChild(textArea);
if (!successful) {
throw new Error('The fallback copy command failed.');
}
}
/**
* Displays temporary feedback on the copy button.
*
* @param {HTMLButtonElement} button
* @param {boolean} success
*/
function showCopyFeedback(button, success) {
const originalText = button.textContent;
button.textContent = success ? 'Copied!' : 'Failed';
button.classList.add(success ? 'copied' : 'failed');
window.setTimeout(() => {
button.textContent = originalText;
button.classList.remove('copied', 'failed');
}, 2000);
}