Skip to content

Commit 4cb25a5

Browse files
committed
WIP
1 parent f5d080f commit 4cb25a5

File tree

3 files changed

+133
-81
lines changed

3 files changed

+133
-81
lines changed
Lines changed: 110 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,126 @@
11
// ``function*`` denotes a generator in JavaScript, see
22
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*
33
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
27-
}
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;
2827
}
28+
}
2929
}
3030

31+
// Extract copyable text from the code block ignoring the
32+
// prompts and output.
33+
function getCopyableText(rootElement) {
34+
rootElement = rootElement.cloneNode(true);
35+
// get all the elements with the "gp" (Generic.Prompt),
36+
// "go" (Generic.Output), or "gt" (Generic.Traceback) CSS class
37+
const tracebacks = rootElement.querySelectorAll(".gt");
38+
for (const el of tracebacks) {
39+
while (
40+
el.nextSibling &&
41+
(el.nextSibling.nodeType !== Node.DOCUMENT_NODE ||
42+
!el.nextSibling.matches(".gp, .go"))
43+
) {
44+
el.nextSibling.remove();
45+
}
46+
}
47+
rootElement.re
48+
const elements = rootElement.querySelectorAll(".gp, .go, .gt");
49+
// remove the prompts and output from the code block
50+
for (const el of elements) {
51+
el.remove();
52+
}
53+
// get the text content of the code block
54+
return rootElement.innerText;
55+
}
3156

3257
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"
58+
/* Add a [>>>] button in the top-right corner of code samples to hide
59+
* the >>> and ... prompts and the output and thus make the code
60+
* copyable. */
61+
// const hide_text = _("Hide the prompts and output")
62+
// const show_text = _("Show the prompts and output")
3863

39-
const button = document.createElement("span")
40-
button.classList.add("copybutton")
41-
button.innerText = ">>>"
42-
button.title = hide_text
43-
button.dataset.hidden = "false"
44-
const buttonClick = event => {
45-
// 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
53-
}
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
60-
}
61-
buttonEl.title = hide_text
62-
buttonEl.dataset.hidden = "false"
63-
}
64-
}
64+
const button = document.createElement("button");
65+
button.classList.add("copybutton");
66+
button.type = "button";
67+
button.innerText = _("Copy");
68+
button.title = _("Copy code to clipboard");
69+
// button.dataset.hidden = "false"
70+
let timeout;
71+
const buttonClick = (event) => {
72+
// define the behavior of the button when it's clicked
73+
// event.preventDefault()
74+
clearTimeout(timeout);
75+
const buttonEl = event.currentTarget;
76+
const codeEl = buttonEl.nextElementSibling;
77+
navigator.clipboard.writeText(getCopyableText(codeEl));
78+
buttonEl.innerText = _("Copied!");
79+
timeout = setTimeout(() => {
80+
buttonEl.innerText = _("Copy");
81+
}, 1500);
82+
// if (buttonEl.dataset.hidden === "false") {
83+
// // hide the code output
84+
// for (const el of getHideableCopyButtonElements(codeEl)) {
85+
// el.hidden = true
86+
// }
87+
// buttonEl.title = show_text
88+
// buttonEl.dataset.hidden = "true"
89+
// } else {
90+
// // show the code output
91+
// for (const el of getHideableCopyButtonElements(codeEl)) {
92+
// el.hidden = false
93+
// }
94+
// buttonEl.title = hide_text
95+
// buttonEl.dataset.hidden = "false"
96+
// }
97+
};
6598

66-
const highlightedElements = document.querySelectorAll(
67-
".highlight-python .highlight,"
68-
+ ".highlight-python3 .highlight,"
69-
+ ".highlight-pycon .highlight,"
70-
+ ".highlight-pycon3 .highlight,"
71-
+ ".highlight-default .highlight"
72-
)
99+
const highlightedElements = document.querySelectorAll(
100+
".highlight-python .highlight," +
101+
".highlight-python3 .highlight," +
102+
".highlight-pycon .highlight," +
103+
".highlight-pycon3 .highlight," +
104+
".highlight-default .highlight"
105+
);
73106

74-
// create and add the button to all the code blocks that contain >>>
75-
highlightedElements.forEach(el => {
76-
el.style.position = "relative"
107+
// create and add the button to all the code blocks that contain >>>
108+
highlightedElements.forEach((el) => {
109+
el.style.position = "relative";
77110

78-
// if we find a console prompt (.gp), prepend the (deeply cloned) button
79-
const clonedButton = button.cloneNode(true)
80-
// 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-
}
85-
})
86-
}
111+
// if we find a console prompt (.gp), prepend the (deeply cloned) button
112+
const clonedButton = button.cloneNode(true);
113+
// the onclick attribute is not cloned, set it on the new element
114+
clonedButton.onclick = buttonClick;
115+
el.prepend(clonedButton);
116+
console.log("adding button");
117+
});
118+
};
87119

88120
if (document.readyState !== "loading") {
89-
loadCopyButton()
121+
loadCopyButton();
90122
} else {
91-
document.addEventListener("DOMContentLoaded", loadCopyButton)
123+
document.addEventListener("DOMContentLoaded", loadCopyButton);
92124
}
125+
126+
console.log("test!");

python_docs_theme/static/pydoctheme.css

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -438,17 +438,22 @@ div.genindex-jumpbox a {
438438
top: 0;
439439
right: 0;
440440
font-family: Menlo, Consolas, Monaco, Liberation Mono, Lucida Console, monospace;
441-
padding-left: 0.2em;
442-
padding-right: 0.2em;
441+
padding: 0.5em;
443442
border-radius: 0 3px 0 0;
444-
color: #ac9; /* follows div.body pre */
445443
border-color: #ac9; /* follows div.body pre */
446444
border-style: solid; /* follows div.body pre */
447445
border-width: 1px; /* follows div.body pre */
446+
color: #000;
447+
background-color: #fff;
448+
449+
}
450+
451+
.copybutton:hover {
452+
background-color: #eee;
448453
}
449454

450-
.copybutton[data-hidden='true'] {
451-
text-decoration: line-through;
455+
.copybutton:active {
456+
background-color: #ddd;
452457
}
453458

454459
@media (max-width: 1023px) {

python_docs_theme/static/pydoctheme_dark.css

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,3 +176,16 @@ img.invert-in-dark-mode {
176176
--versionchanged: var(--middle-color);
177177
--deprecated: var(--bad-color);
178178
}
179+
180+
.copybutton {
181+
color: #ac9; /* follows div.body pre */
182+
background-color: #222222;
183+
}
184+
185+
.copybutton:hover {
186+
background-color: #434343;
187+
}
188+
189+
.copybutton:active {
190+
background-color: #656565;
191+
}

0 commit comments

Comments
 (0)