|  | 
| 1 |  | -$(document).ready(function() { | 
|  | 1 | + | 
|  | 2 | +const loadCopyButton = () => { | 
| 2 | 3 |     /* Add a [>>>] button on the top-right corner of code samples to hide | 
| 3 | 4 |      * the >>> and ... prompts and the output and thus make the code | 
| 4 | 5 |      * copyable. */ | 
| 5 |  | -    var div = $('.highlight-python .highlight,' + | 
| 6 |  | -                '.highlight-python3 .highlight,' + | 
| 7 |  | -                '.highlight-pycon .highlight,' + | 
| 8 |  | -                '.highlight-pycon3 .highlight,' + | 
| 9 |  | -                '.highlight-default .highlight'); | 
| 10 |  | -    var pre = div.find('pre'); | 
|  | 6 | +    const hide_text = "Hide the prompts and output" | 
|  | 7 | +    const show_text = "Show the prompts and output" | 
| 11 | 8 | 
 | 
| 12 |  | -    // get the styles from the current theme | 
| 13 |  | -    pre.parent().parent().css('position', 'relative'); | 
| 14 |  | -    var hide_text = 'Hide the prompts and output'; | 
| 15 |  | -    var show_text = 'Show the prompts and output'; | 
| 16 |  | -    var border_width = pre.css('border-top-width'); | 
| 17 |  | -    var border_style = pre.css('border-top-style'); | 
| 18 |  | -    var border_color = pre.css('border-top-color'); | 
| 19 |  | -    var button_styles = { | 
| 20 |  | -        'cursor':'pointer', 'position': 'absolute', 'top': '0', 'right': '0', | 
| 21 |  | -        'border-color': border_color, 'border-style': border_style, | 
| 22 |  | -        'border-width': border_width, 'color': border_color, 'text-size': '75%', | 
| 23 |  | -        'font-family': 'monospace', 'padding-left': '0.2em', 'padding-right': '0.2em', | 
| 24 |  | -        'border-radius': '0 3px 0 0' | 
|  | 9 | +    const button = document.createElement("span") | 
|  | 10 | +    button.classList.add("copybutton") | 
|  | 11 | +    button.innerText = ">>>" | 
|  | 12 | +    button.title = hide_text | 
|  | 13 | +    button.dataset.hidden = "false" | 
|  | 14 | +    const buttonClick = event => { | 
|  | 15 | +        // define the behavior of the button when it's clicked | 
|  | 16 | +        event.preventDefault() | 
|  | 17 | +        const buttonEl = event.currentTarget | 
|  | 18 | +        const codeEl = buttonEl.nextElementSibling | 
|  | 19 | +        if (buttonEl.dataset.hidden === "false") { | 
|  | 20 | +            // hide the code output | 
|  | 21 | +            codeEl.querySelectorAll('.go, .gp, .gt').forEach(el => el.hidden = true) | 
|  | 22 | +            codeEl.querySelectorAll('.gt').forEach(el => { | 
|  | 23 | +                while ((el = el.nextSibling) && el.nodeType !== Node.DOCUMENT_NODE) { | 
|  | 24 | +                    if (el.nodeType === Node.ELEMENT_NODE && el.matches(".gp, .go")) break; | 
|  | 25 | +                    if (el.nodeType === Node.TEXT_NODE && el.textContent.trim()) { | 
|  | 26 | +                        const wrapper = document.createElement('span'); | 
|  | 27 | +                        el.after(wrapper); | 
|  | 28 | +                        wrapper.appendChild(el); | 
|  | 29 | +                        el = wrapper | 
|  | 30 | +                    } | 
|  | 31 | +                    el.hidden = true | 
|  | 32 | +                } | 
|  | 33 | +            }) | 
|  | 34 | +            buttonEl.title = show_text | 
|  | 35 | +            buttonEl.dataset.hidden = "true" | 
|  | 36 | +        } else { | 
|  | 37 | +            // show the code output | 
|  | 38 | +            codeEl.childNodes.forEach(el => el.hidden = false) | 
|  | 39 | +            buttonEl.title = hide_text | 
|  | 40 | +            buttonEl.dataset.hidden = "false" | 
|  | 41 | +        } | 
| 25 | 42 |     } | 
| 26 | 43 | 
 | 
|  | 44 | +    const highlightedElements = document.querySelectorAll( | 
|  | 45 | +        ".highlight-python .highlight," | 
|  | 46 | +        + ".highlight-python3 .highlight," | 
|  | 47 | +        + ".highlight-pycon .highlight," | 
|  | 48 | +        + ".highlight-pycon3 .highlight," | 
|  | 49 | +        + ".highlight-default .highlight" | 
|  | 50 | +    ) | 
|  | 51 | + | 
| 27 | 52 |     // create and add the button to all the code blocks that contain >>> | 
| 28 |  | -    div.each(function(index) { | 
| 29 |  | -        var jthis = $(this); | 
| 30 |  | -        if (jthis.find('.gp').length > 0) { | 
| 31 |  | -            var button = $('<span class="copybutton">>>></span>'); | 
| 32 |  | -            button.css(button_styles) | 
| 33 |  | -            button.attr('title', hide_text); | 
| 34 |  | -            button.data('hidden', 'false'); | 
| 35 |  | -            jthis.prepend(button); | 
| 36 |  | -        } | 
| 37 |  | -        // tracebacks (.gt) contain bare text elements that need to be | 
| 38 |  | -        // wrapped in a span to work with .nextUntil() (see later) | 
| 39 |  | -        jthis.find('pre:has(.gt)').contents().filter(function() { | 
| 40 |  | -            return ((this.nodeType == 3) && (this.data.trim().length > 0)); | 
| 41 |  | -        }).wrap('<span>'); | 
| 42 |  | -    }); | 
|  | 53 | +    highlightedElements.forEach(el => { | 
|  | 54 | +        el.style.position = "relative" | 
| 43 | 55 | 
 | 
| 44 |  | -    // define the behavior of the button when it's clicked | 
| 45 |  | -    $('.copybutton').click(function(e){ | 
| 46 |  | -        e.preventDefault(); | 
| 47 |  | -        var button = $(this); | 
| 48 |  | -        if (button.data('hidden') === 'false') { | 
| 49 |  | -            // hide the code output | 
| 50 |  | -            button.parent().find('.go, .gp, .gt').hide(); | 
| 51 |  | -            button.next('pre').find('.gt').nextUntil('.gp, .go').css('visibility', 'hidden'); | 
| 52 |  | -            button.css('text-decoration', 'line-through'); | 
| 53 |  | -            button.attr('title', show_text); | 
| 54 |  | -            button.data('hidden', 'true'); | 
| 55 |  | -        } else { | 
| 56 |  | -            // show the code output | 
| 57 |  | -            button.parent().find('.go, .gp, .gt').show(); | 
| 58 |  | -            button.next('pre').find('.gt').nextUntil('.gp, .go').css('visibility', 'visible'); | 
| 59 |  | -            button.css('text-decoration', 'none'); | 
| 60 |  | -            button.attr('title', hide_text); | 
| 61 |  | -            button.data('hidden', 'false'); | 
|  | 56 | +        // if we find a console prompt (.gp), prepend the (deeply cloned) button | 
|  | 57 | +        const clonedButton = button.cloneNode(true) | 
|  | 58 | +        // the onclick attribute is not cloned, set it on the new element | 
|  | 59 | +        clonedButton.onclick = buttonClick | 
|  | 60 | +        if (el.querySelector(".gp") !== null) { | 
|  | 61 | +            el.prepend(clonedButton) | 
| 62 | 62 |         } | 
| 63 |  | -    }); | 
| 64 |  | -}); | 
|  | 63 | +    }) | 
|  | 64 | +} | 
|  | 65 | + | 
|  | 66 | +if (document.readyState !== "loading") { | 
|  | 67 | +    loadCopyButton() | 
|  | 68 | +} else { | 
|  | 69 | +    document.addEventListener("DOMContentLoaded", loadCopyButton) | 
|  | 70 | +} | 
0 commit comments