1// Localization support 2const messages = { 3 'en': { 4 'copy': 'Copy', 5 'copy_to_clipboard': 'Copy to clipboard', 6 'copy_success': 'Copied!', 7 'copy_failure': 'Failed to copy', 8 }, 9 'es' : { 10 'copy': 'Copiar', 11 'copy_to_clipboard': 'Copiar al portapapeles', 12 'copy_success': '¡Copiado!', 13 'copy_failure': 'Error al copiar', 14 }, 15 'de' : { 16 'copy': 'Kopieren', 17 'copy_to_clipboard': 'In die Zwischenablage kopieren', 18 'copy_success': 'Kopiert!', 19 'copy_failure': 'Fehler beim Kopieren', 20 }, 21 'fr' : { 22 'copy': 'Copier', 23 'copy_to_clipboard': 'Copier dans le presse-papier', 24 'copy_success': 'Copié !', 25 'copy_failure': 'Ãchec de la copie', 26 }, 27 'ru': { 28 'copy': 'СкопиÑоваÑÑ', 29 'copy_to_clipboard': 'СкопиÑоваÑÑ Ð² бÑÑеÑ', 30 'copy_success': 'СкопиÑовано!', 31 'copy_failure': 'Ðе ÑдалоÑÑ ÑкопиÑоваÑÑ', 32 }, 33 'zh-CN': { 34 'copy': 'å¤å¶', 35 'copy_to_clipboard': 'å¤å¶å°åªè´´æ¿', 36 'copy_success': 'å¤å¶æå!', 37 'copy_failure': 'å¤å¶å¤±è´¥', 38 }, 39 'it' : { 40 'copy': 'Copiare', 41 'copy_to_clipboard': 'Copiato negli appunti', 42 'copy_success': 'Copiato!', 43 'copy_failure': 'Errore durante la copia', 44 } 45} 46 47let locale = 'en' 48if( document.documentElement.lang !== undefined 49 && messages[document.documentElement.lang] !== undefined ) { 50 locale = document.documentElement.lang 51} 52 53let doc_url_root = DOCUMENTATION_OPTIONS.URL_ROOT; 54if (doc_url_root == '#') { 55 doc_url_root = ''; 56} 57 58/** 59 * SVG files for our copy buttons 60 */ 61let iconCheck = `<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-check" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#22863a" fill="none" stroke-linecap="round" stroke-linejoin="round"> 62 <title>${messages[locale]['copy_success']}</title> 63 <path stroke="none" d="M0 0h24v24H0z" fill="none"/> 64 <path d="M5 12l5 5l10 -10" /> 65</svg>` 66 67// If the user specified their own SVG use that, otherwise use the default 68let iconCopy = ``; 69if (!iconCopy) { 70 iconCopy = `<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-copy" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="#000000" fill="none" stroke-linecap="round" stroke-linejoin="round"> 71 <title>${messages[locale]['copy_to_clipboard']}</title> 72 <path stroke="none" d="M0 0h24v24H0z" fill="none"/> 73 <rect x="8" y="8" width="12" height="12" rx="2" /> 74 <path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2" /> 75</svg>` 76} 77 78/** 79 * Set up copy/paste for code blocks 80 */ 81 82const runWhenDOMLoaded = cb => { 83 if (document.readyState != 'loading') { 84 cb() 85 } else if (document.addEventListener) { 86 document.addEventListener('DOMContentLoaded', cb) 87 } else { 88 document.attachEvent('onreadystatechange', function() { 89 if (document.readyState == 'complete') cb() 90 }) 91 } 92} 93 94const codeCellId = index => `codecell${index}` 95 96// Clears selected text since ClipboardJS will select the text when copying 97const clearSelection = () => { 98 if (window.getSelection) { 99 window.getSelection().removeAllRanges() 100 } else if (document.selection) { 101 document.selection.empty() 102 } 103} 104 105// Changes tooltip text for a moment, then changes it back 106// We want the timeout of our `success` class to be a bit shorter than the 107// tooltip and icon change, so that we can hide the icon before changing back. 108var timeoutIcon = 2000; 109var timeoutSuccessClass = 1500; 110 111const temporarilyChangeTooltip = (el, oldText, newText) => { 112 el.setAttribute('data-tooltip', newText) 113 el.classList.add('success') 114 // Remove success a little bit sooner than we change the tooltip 115 // So that we can use CSS to hide the copybutton first 116 setTimeout(() => el.classList.remove('success'), timeoutSuccessClass) 117 setTimeout(() => el.setAttribute('data-tooltip', oldText), timeoutIcon) 118} 119 120// Changes the copy button icon for two seconds, then changes it back 121const temporarilyChangeIcon = (el) => { 122 el.innerHTML = iconCheck; 123 setTimeout(() => {el.innerHTML = iconCopy}, timeoutIcon) 124} 125 126const addCopyButtonToCodeCells = () => { 127 // If ClipboardJS hasn't loaded, wait a bit and try again. This 128 // happens because we load ClipboardJS asynchronously. 129 if (window.ClipboardJS === undefined) { 130 setTimeout(addCopyButtonToCodeCells, 250) 131 return 132 } 133
134 // Add copybuttons to all of our code cells 135 const COPYBUTTON_SELECTOR = 'div.highlight pre'; 136 const codeCells = document.querySelectorAll(COPYBUTTON_SELECTOR) 137 codeCells.forEach((codeCell, index) => { 138 const id = codeCellId(index) 139 codeCell.setAttribute('id', id) 140 141 const clipboardButton = id => 142 `<button class="copybtn o-tooltip--left" data-tooltip="${messages[locale]['copy']}" data-clipboard-target="#${id}"> 143 ${iconCopy} 144 </button>` 145 codeCell.insertAdjacentHTML('afterend', clipboardButton(id)) 146 }) 147 148function escapeRegExp(string) { 149 return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string 150} 151 152/** 153 * Removes excluded text from a Node. 154 * 155 * @param {Node} target Node to filter. 156 * @param {string} exclude CSS selector of nodes to exclude. 157 * @returns {DOMString} Text from `target` with text removed. 158 */ 159function filterText(target, exclude) { 160 const clone = target.cloneNode(true); // clone as to not modify the live DOM 161 if (exclude) { 162 // remove excluded nodes 163 clone.querySelectorAll(exclude).forEach(node => node.remove()); 164 } 165 return clone.innerText; 166} 167 168// Callback when a copy button is clicked. Will be passed the node that was clicked 169// should then grab the text and replace pieces of text that shouldn't be used in output 170function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") { 171 var regexp; 172 var match; 173 174 // Do we check for line continuation characters and "HERE-documents"? 175 var useLineCont = !!lineContinuationChar 176 var useHereDoc = !!hereDocDelim 177 178 // create regexp to capture prompt and remaining line 179 if (isRegexp) { 180 regexp = new RegExp('^(' + copybuttonPromptText + ')(.*)') 181 } else { 182 regexp = new RegExp('^(' + escapeRegExp(copybuttonPromptText) + ')(.*)') 183 } 184 185 const outputLines = []; 186 var promptFound = false; 187 var gotLineCont = false; 188 var gotHereDoc = false; 189 const lineGotPrompt = []; 190 for (const line of textContent.split('\n')) { 191 match = line.match(regexp) 192 if (match || gotLineCont || gotHereDoc) { 193 promptFound = regexp.test(line) 194 lineGotPrompt.push(promptFound) 195 if (removePrompts && promptFound) { 196 outputLines.push(match[2]) 197 } else { 198 outputLines.push(line) 199 } 200 gotLineCont = line.endsWith(lineContinuationChar) & useLineCont 201 if (line.includes(hereDocDelim) & useHereDoc) 202 gotHereDoc = !gotHereDoc 203 } else if (!onlyCopyPromptLines) { 204 outputLines.push(line) 205 } else if (copyEmptyLines && line.trim() === '') { 206 outputLines.push(line) 207 } 208 } 209 210 // If no lines with the prompt were found then just use original lines 211 if (lineGotPrompt.some(v => v === true)) { 212 textContent = outputLines.join('\n'); 213 } 214 215 // Remove a trailing newline to avoid auto-running when pasting 216 if (textContent.endsWith("\n")) { 217 textContent = textContent.slice(0, -1) 218 } 219 return textContent 220} 221 222 223var copyTargetText = (trigger) => { 224 var target = document.querySelector(trigger.attributes['data-clipboard-target'].value); 225 226 // get filtered text 227 let exclude = '.linenos'; 228 229 let text = filterText(target, exclude); 230 return formatCopyText(text, '>>> |\\.\\.\\. |\\$ |In \\[\\d*\\]: | {2,5}\\.\\.\\.: | {5,8}: ', true, true, true, true, '', '') 231} 232 233 // Initialize with a callback so we can modify the text before copy 234 const clipboard = new ClipboardJS('.copybtn', {text: copyTargetText}) 235 236 // Update UI with error/success messages 237 clipboard.on('success', event => { 238 clearSelection() 239 temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_success']) 240 temporarilyChangeIcon(event.trigger) 241 }) 242 243 clipboard.on('error', event => { 244 temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_failure']) 245 }) 246} 247 248runWhenDOMLoaded(addCopyButtonToCodeCells)
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.