| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,65 +1,59 @@ | |||
| 1 | - // ``function*`` denotes a generator in JavaScript, see | ||
| 2 | - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function* | ||
| 3 | - function* getHideableCopyButtonElements(rootElement) { | ||
| 4 | - // yield all elements with the "go" (Generic.Output), | ||
| 5 | - // "gp" (Generic.Prompt), or "gt" (Generic.Traceback) CSS class | ||
| 6 | - for (const el of rootElement.querySelectorAll('.go, .gp, .gt')) { | ||
| 7 | - yield el | ||
| 8 | - } | ||
| 9 | - // tracebacks (.gt) contain bare text elements that need to be | ||
| 10 | - // wrapped in a span to hide or show the element | ||
| 11 | - for (let el of rootElement.querySelectorAll('.gt')) { | ||
| 12 | - while ((el = el.nextSibling) && el.nodeType !== Node.DOCUMENT_NODE) { | ||
| 13 | - // stop wrapping text nodes when we hit the next output or | ||
| 14 | - // prompt element | ||
| 15 | - if (el.nodeType === Node.ELEMENT_NODE && el.matches(".gp, .go")) { | ||
| 16 | - break | ||
| 17 | - } | ||
| 18 | - // if the node is a text node with content, wrap it in a | ||
| 19 | - // span element so that we can control visibility | ||
| 20 | - if (el.nodeType === Node.TEXT_NODE && el.textContent.trim()) { | ||
| 21 | - const wrapper = document.createElement("span") | ||
| 22 | - el.after(wrapper) | ||
| 23 | - wrapper.appendChild(el) | ||
| 24 | - el = wrapper | ||
| 25 | - } | ||
| 26 | - yield el | ||
| 1 | + // Extract copyable text from the code block ignoring the | ||
| 2 | + // prompts and output. | ||
| 3 | + function getCopyableText(rootElement) { | ||
| 4 | + rootElement = rootElement.cloneNode(true) | ||
| 5 | + // tracebacks (.gt) contain bare text elements that | ||
| 6 | + // need to be removed | ||
| 7 | + const tracebacks = rootElement.querySelectorAll(".gt") | ||
| 8 | + for (const el of tracebacks) { | ||
| 9 | + while ( | ||
| 10 | + el.nextSibling && | ||
| 11 | + (el.nextSibling.nodeType !== Node.DOCUMENT_NODE || | ||
| 12 | + !el.nextSibling.matches(".gp, .go")) | ||
| 13 | + ) { | ||
| 14 | + el.nextSibling.remove() | ||
| 27 | 15 | } | |
| 28 | 16 | } | |
| 17 | + // Remove all elements with the "go" (Generic.Output), | ||
| 18 | + // "gp" (Generic.Prompt), or "gt" (Generic.Traceback) CSS class | ||
| 19 | + const elements = rootElement.querySelectorAll(".gp, .go, .gt") | ||
| 20 | + for (const el of elements) { | ||
| 21 | + el.remove() | ||
| 22 | + } | ||
| 23 | + return rootElement.innerText.trim() | ||
| 29 | 24 | } | |
| 30 | 25 | ||
| 31 | - | ||
| 32 | 26 | const loadCopyButton = () => { | |
| 33 | - /* Add a [>>>] button in the top-right corner of code samples to hide | ||
| 34 | - * the >>> and ... prompts and the output and thus make the code | ||
| 35 | - * copyable. */ | ||
| 36 | - const hide_text = _("Hide the prompts and output") | ||
| 37 | - const show_text = _("Show the prompts and output") | ||
| 38 | - | ||
| 39 | - const button = document.createElement("span") | ||
| 27 | + const button = document.createElement("button") | ||
| 40 | 28 | button.classList.add("copybutton") | |
| 41 | - button.innerText = ">>>" | ||
| 42 | - button.title = hide_text | ||
| 43 | - button.dataset.hidden = "false" | ||
| 44 | - const buttonClick = event => { | ||
| 29 | + button.type = "button" | ||
| 30 | + button.innerText = _("Copy") | ||
| 31 | + button.title = _("Copy to clipboard") | ||
| 32 | + | ||
| 33 | + const makeOnButtonClick = () => { | ||
| 34 | + let timeout = null | ||
| 45 | 35 | // define the behavior of the button when it's clicked | |
| 46 | - event.preventDefault() | ||
| 47 | - const buttonEl = event.currentTarget | ||
| 48 | - const codeEl = buttonEl.nextElementSibling | ||
| 49 | - if (buttonEl.dataset.hidden === "false") { | ||
| 50 | - // hide the code output | ||
| 51 | - for (const el of getHideableCopyButtonElements(codeEl)) { | ||
| 52 | - el.hidden = true | ||
| 36 | + return async event => { | ||
| 37 | + // check if the clipboard is available | ||
| 38 | + if (!navigator.clipboard || !navigator.clipboard.writeText) { | ||
| 39 | + return; | ||
| 53 | 40 | } | |
| 54 | - buttonEl.title = show_text | ||
| 55 | - buttonEl.dataset.hidden = "true" | ||
| 56 | - } else { | ||
| 57 | - // show the code output | ||
| 58 | - for (const el of getHideableCopyButtonElements(codeEl)) { | ||
| 59 | - el.hidden = false | ||
| 41 | + | ||
| 42 | + clearTimeout(timeout) | ||
| 43 | + const buttonEl = event.currentTarget | ||
| 44 | + const codeEl = buttonEl.nextElementSibling | ||
| 45 | + | ||
| 46 | + try { | ||
| 47 | + await navigator.clipboard.writeText(getCopyableText(codeEl)) | ||
| 48 | + } catch (e) { | ||
| 49 | + console.error(e.message) | ||
| 50 | + return | ||
| 60 | 51 | } | |
| 61 | - buttonEl.title = hide_text | ||
| 62 | - buttonEl.dataset.hidden = "false" | ||
| 52 | + | ||
| 53 | + buttonEl.innerText = _("Copied!") | ||
| 54 | + timeout = setTimeout(() => { | ||
| 55 | + buttonEl.innerText = _("Copy") | ||
| 56 | + }, 1500) | ||
| 63 | 57 | } | |
| 64 | 58 | } | |
| 65 | 59 | ||
@@ -78,10 +72,8 @@ const loadCopyButton = () => { | |||
| 78 | 72 | // if we find a console prompt (.gp), prepend the (deeply cloned) button | |
| 79 | 73 | const clonedButton = button.cloneNode(true) | |
| 80 | 74 | // the onclick attribute is not cloned, set it on the new element | |
| 81 | - clonedButton.onclick = buttonClick | ||
| 82 | - if (el.querySelector(".gp") !== null) { | ||
| 83 | - el.prepend(clonedButton) | ||
| 84 | - } | ||
| 75 | + clonedButton.onclick = makeOnButtonClick() | ||
| 76 | + el.prepend(clonedButton) | ||
| 85 | 77 | }) | |
| 86 | 78 | } | |
| 87 | 79 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -442,17 +442,23 @@ div.genindex-jumpbox a { | |||
| 442 | 442 | top: 0; | |
| 443 | 443 | right: 0; | |
| 444 | 444 | font-family: Menlo, Consolas, Monaco, Liberation Mono, Lucida Console, monospace; | |
| 445 | - padding-left: 0.2em; | ||
| 446 | - padding-right: 0.2em; | ||
| 445 | + font-size: 80%; | ||
| 446 | + padding-left: .5em; | ||
| 447 | + padding-right: .5em; | ||
| 448 | + height: 100%; | ||
| 449 | + max-height: min(100%, 2.4em); | ||
| 447 | 450 | border-radius: 0 3px 0 0; | |
| 448 | - color: #ac9; /* follows div.body pre */ | ||
| 449 | - border-color: #ac9; /* follows div.body pre */ | ||
| 450 | - border-style: solid; /* follows div.body pre */ | ||
| 451 | - border-width: 1px; /* follows div.body pre */ | ||
| 451 | + color: #000; | ||
| 452 | + background-color: #fff; | ||
| 453 | + border: 1px solid #ac9; /* follows div.body pre */ | ||
| 454 | + } | ||
| 455 | + | ||
| 456 | + .copybutton:hover { | ||
| 457 | + background-color: #eee; | ||
| 452 | 458 | } | |
| 453 | 459 | ||
| 454 | - .copybutton[data-hidden='true'] { | ||
| 455 | - text-decoration: line-through; | ||
| 460 | + .copybutton:active { | ||
| 461 | + background-color: #ddd; | ||
| 456 | 462 | } | |
| 457 | 463 | ||
| 458 | 464 | @media (max-width: 1023px) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -176,3 +176,16 @@ img.invert-in-dark-mode { | |||
| 176 | 176 | --versionchanged: var(--middle-color); | |
| 177 | 177 | --deprecated: var(--bad-color); | |
| 178 | 178 | } | |
| 179 | + | ||
| 180 | + .copybutton { | ||
| 181 | + color: #ac9; /* follows div.body pre */ | ||
| 182 | + background-color: #222222; /* follows body */ | ||
| 183 | + } | ||
| 184 | + | ||
| 185 | + .copybutton:hover { | ||
| 186 | + background-color: #434343; | ||
| 187 | + } | ||
| 188 | + | ||
| 189 | + .copybutton:active { | ||
| 190 | + background-color: #656565; | ||
| 191 | + } | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments