1/** 2 * Highlights code within the provided element 3 * @param {HTMLElement} element - The element containing code to highlight 4 */ 5 6import { ensureHighlightJs, initializeThemeUpdates } from '../highlightUtils.js'; 7 8export async function highlightBlock(element) { 9 // Check if element is valid 10 if (!element) { 11 return; 12 } 13 14 await ensureHighlightJs(); 15 initializeThemeUpdates(); 16 17 if (!window.hljs) { 18 return; 19 } 20 21 // Find all code blocks within the element 22 const codeElements = element.querySelectorAll("pre code"); 23 24 if (codeElements.length === 0) { 25 // If no pre code elements found, try to highlight the element itself if it's a code element 26 if (element.tagName === 'CODE' || element.classList.contains('language-')) { 27 hljs.highlightElement(element); 28 } else { 29 // Look for any code elements 30 const allCodeElements = element.querySelectorAll("code"); 31 allCodeElements.forEach(block => { 32 hljs.highlightElement(block); 33 }); 34 } 35 } else { 36 codeElements.forEach(block => { 37 hljs.highlightElement(block); 38 }); 39 } 40} 41 42export function getCodeText(element) { 43 if (!element) { 44 return ""; 45 } 46 47 const codeElement = element.querySelector("pre code") || element.querySelector("code"); 48 return codeElement ? codeElement.textContent : element.textContent || ""; 49} 50 51/** 52 * Wires the copy-to-clipboard icon inside the code block to a NATIVE click handler. 53 * 54 * Safari only permits navigator.clipboard.writeText from inside the synchronous call stack of a 55 * user gesture (a "transient activation"). The previous Blazor Server `@onclick` handler 56 * round-tripped over SignalR and then invoked writeText from the server -> JS, i.e. outside the 57 * gesture â Chrome tolerates that, Safari rejects it with NotAllowedError, so the button silently 58 * did nothing. Attaching a native DOM click listener here keeps the whole read-then-write in the 59 * gesture. A synchronous execCommand("copy") fallback covers older Safari / non-secure contexts. 60 * 61 * @param {HTMLElement} element - The code-block container holding the `.copy-to-clipboard` icon. 62 */ 63export function setupCopyButton(element) { 64 if (!element) { 65 return; 66 } 67 const button = element.querySelector(".copy-to-clipboard"); 68 if (!button || button.dataset.copyWired === "1") { 69 return; // idempotent â OnAfterRenderAsync can re-run 70 } 71 button.dataset.copyWired = "1"; 72 73 const doCopy = () => { 74 const text = getCodeText(element); 75 // Called synchronously in the gesture handler so Safari's gesture check passes. 76 if (navigator.clipboard && window.isSecureContext) { 77 navigator.clipboard.writeText(text).then( 78 () => flashCopied(button), 79 () => { if (copyViaExecCommand(text)) flashCopied(button); }); 80 } else if (copyViaExecCommand(text)) { 81 flashCopied(button); 82 } 83 }; 84 85 button.addEventListener("click", doCopy); 86 // Keyboard activation for the role="button" icon (Enter / Space). 87 button.addEventListener("keydown", (e) => { 88 if (e.key === "Enter" || e.key === " ") { 89 e.preventDefault(); 90 doCopy(); 91 } 92 }); 93} 94 95function copyViaExecCommand(text) { 96 const ta = document.createElement("textarea"); 97 ta.value = text; 98 ta.setAttribute("readonly", ""); 99 ta.style.position = "fixed"; 100 ta.style.top = "0"; 101 ta.style.left = "0"; 102 ta.style.opacity = "0"; 103 document.body.appendChild(ta); 104 try { 105 ta.focus(); 106 ta.select(); 107 return document.execCommand("copy"); 108 } catch { 109 return false; 110 } finally { 111 document.body.removeChild(ta); // always clean up, even if select/execCommand throws 112 } 113} 114 115function flashCopied(button) { 116 button.classList.add("copied"); 117 setTimeout(() => button.classList.remove("copied"), 1200); 118}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.