diff --git a/python_docs_theme/__init__.py b/python_docs_theme/__init__.py
index bbe1352..68bb9e3 100644
--- a/python_docs_theme/__init__.py
+++ b/python_docs_theme/__init__.py
@@ -52,12 +52,11 @@ def _html_page_context(
def setup(app):
current_dir = os.path.abspath(os.path.dirname(__file__))
- app.add_html_theme(
- 'python_docs_theme', current_dir)
+ app.add_html_theme("python_docs_theme", current_dir)
app.connect("html-page-context", _html_page_context)
return {
- 'parallel_read_safe': True,
- 'parallel_write_safe': True,
+ "parallel_read_safe": True,
+ "parallel_write_safe": True,
}
diff --git a/python_docs_theme/static/copybutton.js b/python_docs_theme/static/copybutton.js
index 16dee00..7367c4a 100644
--- a/python_docs_theme/static/copybutton.js
+++ b/python_docs_theme/static/copybutton.js
@@ -1,64 +1,92 @@
-$(document).ready(function() {
- /* Add a [>>>] button on the top-right corner of code samples to hide
+// ``function*`` denotes a generator in JavaScript, see
+// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*
+function* getHideableCopyButtonElements(rootElement) {
+ // yield all elements with the "go" (Generic.Output),
+ // "gp" (Generic.Prompt), or "gt" (Generic.Traceback) CSS class
+ for (const el of rootElement.querySelectorAll('.go, .gp, .gt')) {
+ yield el
+ }
+ // tracebacks (.gt) contain bare text elements that need to be
+ // wrapped in a span to hide or show the element
+ for (let el of rootElement.querySelectorAll('.gt')) {
+ while ((el = el.nextSibling) && el.nodeType !== Node.DOCUMENT_NODE) {
+ // stop wrapping text nodes when we hit the next output or
+ // prompt element
+ if (el.nodeType === Node.ELEMENT_NODE && el.matches(".gp, .go")) {
+ break
+ }
+ // if the node is a text node with content, wrap it in a
+ // span element so that we can control visibility
+ if (el.nodeType === Node.TEXT_NODE && el.textContent.trim()) {
+ const wrapper = document.createElement("span")
+ el.after(wrapper)
+ wrapper.appendChild(el)
+ el = wrapper
+ }
+ yield el
+ }
+ }
+}
+
+
+const loadCopyButton = () => {
+ /* Add a [>>>] button in the top-right corner of code samples to hide
* the >>> and ... prompts and the output and thus make the code
* copyable. */
- var div = $('.highlight-python .highlight,' +
- '.highlight-python3 .highlight,' +
- '.highlight-pycon .highlight,' +
- '.highlight-pycon3 .highlight,' +
- '.highlight-default .highlight');
- var pre = div.find('pre');
+ const hide_text = "Hide the prompts and output"
+ const show_text = "Show the prompts and output"
- // get the styles from the current theme
- pre.parent().parent().css('position', 'relative');
- var hide_text = 'Hide the prompts and output';
- var show_text = 'Show the prompts and output';
- var border_width = pre.css('border-top-width');
- var border_style = pre.css('border-top-style');
- var border_color = pre.css('border-top-color');
- var button_styles = {
- 'cursor':'pointer', 'position': 'absolute', 'top': '0', 'right': '0',
- 'border-color': border_color, 'border-style': border_style,
- 'border-width': border_width, 'color': border_color, 'text-size': '75%',
- 'font-family': 'monospace', 'padding-left': '0.2em', 'padding-right': '0.2em',
- 'border-radius': '0 3px 0 0'
+ const button = document.createElement("span")
+ button.classList.add("copybutton")
+ button.innerText = ">>>"
+ button.title = hide_text
+ button.dataset.hidden = "false"
+ const buttonClick = event => {
+ // define the behavior of the button when it's clicked
+ event.preventDefault()
+ const buttonEl = event.currentTarget
+ const codeEl = buttonEl.nextElementSibling
+ if (buttonEl.dataset.hidden === "false") {
+ // hide the code output
+ for (const el of getHideableCopyButtonElements(codeEl)) {
+ el.hidden = true
+ }
+ buttonEl.title = show_text
+ buttonEl.dataset.hidden = "true"
+ } else {
+ // show the code output
+ for (const el of getHideableCopyButtonElements(codeEl)) {
+ el.hidden = false
+ }
+ buttonEl.title = hide_text
+ buttonEl.dataset.hidden = "false"
+ }
}
+ const highlightedElements = document.querySelectorAll(
+ ".highlight-python .highlight,"
+ + ".highlight-python3 .highlight,"
+ + ".highlight-pycon .highlight,"
+ + ".highlight-pycon3 .highlight,"
+ + ".highlight-default .highlight"
+ )
+
// create and add the button to all the code blocks that contain >>>
- div.each(function(index) {
- var jthis = $(this);
- if (jthis.find('.gp').length > 0) {
- var button = $('>>>');
- button.css(button_styles)
- button.attr('title', hide_text);
- button.data('hidden', 'false');
- jthis.prepend(button);
- }
- // tracebacks (.gt) contain bare text elements that need to be
- // wrapped in a span to work with .nextUntil() (see later)
- jthis.find('pre:has(.gt)').contents().filter(function() {
- return ((this.nodeType == 3) && (this.data.trim().length > 0));
- }).wrap('');
- });
+ highlightedElements.forEach(el => {
+ el.style.position = "relative"
- // define the behavior of the button when it's clicked
- $('.copybutton').click(function(e){
- e.preventDefault();
- var button = $(this);
- if (button.data('hidden') === 'false') {
- // hide the code output
- button.parent().find('.go, .gp, .gt').hide();
- button.next('pre').find('.gt').nextUntil('.gp, .go').css('visibility', 'hidden');
- button.css('text-decoration', 'line-through');
- button.attr('title', show_text);
- button.data('hidden', 'true');
- } else {
- // show the code output
- button.parent().find('.go, .gp, .gt').show();
- button.next('pre').find('.gt').nextUntil('.gp, .go').css('visibility', 'visible');
- button.css('text-decoration', 'none');
- button.attr('title', hide_text);
- button.data('hidden', 'false');
+ // if we find a console prompt (.gp), prepend the (deeply cloned) button
+ const clonedButton = button.cloneNode(true)
+ // the onclick attribute is not cloned, set it on the new element
+ clonedButton.onclick = buttonClick
+ if (el.querySelector(".gp") !== null) {
+ el.prepend(clonedButton)
}
- });
-});
+ })
+}
+
+if (document.readyState !== "loading") {
+ loadCopyButton()
+} else {
+ document.addEventListener("DOMContentLoaded", loadCopyButton)
+}
diff --git a/python_docs_theme/static/menu.js b/python_docs_theme/static/menu.js
index b2eabb3..e233585 100644
--- a/python_docs_theme/static/menu.js
+++ b/python_docs_theme/static/menu.js
@@ -1,55 +1,57 @@
-document.addEventListener('DOMContentLoaded', function () {
+document.addEventListener("DOMContentLoaded", function () {
// Make tables responsive by wrapping them in a div and making them scrollable
- const tables = document.querySelectorAll('table.docutils');
+ const tables = document.querySelectorAll("table.docutils")
tables.forEach(function(table){
- table.outerHTML = '' + table.outerHTML + '
'
- });
+ table.outerHTML = '' + table.outerHTML + "
"
+ })
- const togglerInput = document.querySelector('.toggler__input');
- const togglerLabel = document.querySelector('.toggler__label');
- const sideMenu = document.querySelector('.menu-wrapper');
- const menuItems = document.querySelectorAll('.menu')
- const doc = document.querySelector('.document');
- const body = document.querySelector('body');
+ const togglerInput = document.querySelector(".toggler__input")
+ const togglerLabel = document.querySelector(".toggler__label")
+ const sideMenu = document.querySelector(".menu-wrapper")
+ const menuItems = document.querySelectorAll(".menu")
+ const doc = document.querySelector(".document")
+ const body = document.querySelector("body")
function closeMenu() {
- togglerInput.checked = false;
- sideMenu.setAttribute("aria-expanded", 'false');
- sideMenu.setAttribute('aria-hidden', 'true');
- togglerLabel.setAttribute('aria-pressed', 'false');
- body.style.overflow = 'visible';
+ togglerInput.checked = false
+ sideMenu.setAttribute("aria-expanded", "false")
+ sideMenu.setAttribute("aria-hidden", "true")
+ togglerLabel.setAttribute("aria-pressed", "false")
+ body.style.overflow = "visible"
}
function openMenu() {
- togglerInput.checked = true;
- sideMenu.setAttribute("aria-expanded", 'true');
- sideMenu.setAttribute('aria-hidden', 'false');
- togglerLabel.setAttribute('aria-pressed', 'true');
- body.style.overflow = 'hidden';
+ togglerInput.checked = true
+ sideMenu.setAttribute("aria-expanded", "true")
+ sideMenu.setAttribute("aria-hidden", "false")
+ togglerLabel.setAttribute("aria-pressed", "true")
+ body.style.overflow = "hidden"
}
// Close menu when link on the sideMenu is clicked
- sideMenu.addEventListener('click', function (event) {
- let target = event.target;
- if (target.tagName.toLowerCase() !== 'a') return;
- closeMenu();
+ sideMenu.addEventListener("click", function (event) {
+ let target = event.target
+ if (target.tagName.toLowerCase() !== "a") {
+ return
+ }
+ closeMenu()
})
// Add accessibility data when sideMenu is opened/closed
- togglerInput.addEventListener('change', function (e) {
- togglerInput.checked ? openMenu() : closeMenu();
- });
+ togglerInput.addEventListener("change", function (_event) {
+ togglerInput.checked ? openMenu() : closeMenu()
+ })
// Make sideMenu links tabbable only when visible
for(let menuItem of menuItems) {
if(togglerInput.checked) {
- menuItem.setAttribute('tabindex', '0');
+ menuItem.setAttribute("tabindex", "0")
} else {
- menuItem.setAttribute('tabindex', '-1');
+ menuItem.setAttribute("tabindex", "-1")
}
}
// Close sideMenu when document body is clicked
- doc.addEventListener('click', function () {
+ doc.addEventListener("click", function () {
if (togglerInput.checked) {
- closeMenu();
+ closeMenu()
}
})
})
\ No newline at end of file
diff --git a/python_docs_theme/static/pydoctheme.css b/python_docs_theme/static/pydoctheme.css
index 6c141f8..5c0d394 100644
--- a/python_docs_theme/static/pydoctheme.css
+++ b/python_docs_theme/static/pydoctheme.css
@@ -104,10 +104,12 @@ div.sphinxsidebar h4 {
}
div.sphinxsidebarwrapper {
+ width: 217px;
box-sizing: border-box;
height: 100%;
overflow-x: hidden;
overflow-y: auto;
+ float: left;
}
div.sphinxsidebarwrapper > h3:first-child {
@@ -135,6 +137,33 @@ div.sphinxsidebar input[type='text'] {
max-width: 150px;
}
+#sidebarbutton {
+ /* Sphinx 4.x and earlier compat */
+ height: 100%;
+ background-color: #CCCCCC;
+ margin-left: 0;
+ color: #444444;
+ font-size: 1.2em;
+ cursor: pointer;
+ padding-top: 1px;
+ float: right;
+ display: table;
+ /* after Sphinx 4.x and earlier is dropped, only the below is needed */
+ width: 12px;
+ border-radius: 0 5px 5px 0;
+ border-left: none;
+}
+
+#sidebarbutton span {
+ /* Sphinx 4.x and earlier compat */
+ display: table-cell;
+ vertical-align: middle;
+}
+
+#sidebarbutton:hover {
+ background-color: #AAAAAA;
+}
+
div.body {
padding: 0 0 0 1.2em;
}
@@ -279,6 +308,26 @@ div.genindex-jumpbox a {
text-align: center;
}
+.copybutton {
+ cursor: pointer;
+ position: absolute;
+ top: 0;
+ right: 0;
+ text-size: 75%;
+ font-family: monospace;
+ padding-left: 0.2em;
+ padding-right: 0.2em;
+ border-radius: 0 3px 0 0;
+ color: #ac9; /* follows div.body pre */
+ border-color: #ac9; /* follows div.body pre */
+ border-style: solid; /* follows div.body pre */
+ border-width: 1px; /* follows div.body pre */
+}
+
+.copybutton[data-hidden='true'] {
+ text-decoration: line-through;
+}
+
@media (max-width: 1023px) {
/* Body layout */
div.body {
diff --git a/python_docs_theme/static/sidebar.js b/python_docs_theme/static/sidebar.js
index 6b5c694..70886d4 100644
--- a/python_docs_theme/static/sidebar.js
+++ b/python_docs_theme/static/sidebar.js
@@ -2,142 +2,83 @@
* sidebar.js
* ~~~~~~~~~~
*
- * This script makes the Sphinx sidebar collapsible. This is a slightly
- * modified version of Sphinx's own sidebar.js.
+ * This file is functionally identical to "sidebar.js" in Sphinx 5.0.
+ * When support for Sphinx 4 and earlier is dropped from the theme,
+ * this file can be removed.
*
- * .sphinxsidebar contains .sphinxsidebarwrapper. This script adds in
- * .sphixsidebar, after .sphinxsidebarwrapper, the #sidebarbutton used to
- * collapse and expand the sidebar.
+ * This script makes the Sphinx sidebar collapsible.
*
- * When the sidebar is collapsed the .sphinxsidebarwrapper is hidden and the
- * width of the sidebar and the margin-left of the document are decreased.
- * When the sidebar is expanded the opposite happens. This script saves a
- * per-browser/per-session cookie used to remember the position of the sidebar
- * among the pages. Once the browser is closed the cookie is deleted and the
- * position reset to the default (expanded).
+ * .sphinxsidebar contains .sphinxsidebarwrapper. This script adds
+ * in .sphinxsidebar, after .sphinxsidebarwrapper, the #sidebarbutton
+ * used to collapse and expand the sidebar.
*
- * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ * When the sidebar is collapsed the .sphinxsidebarwrapper is hidden
+ * and the width of the sidebar and the margin-left of the document
+ * are decreased. When the sidebar is expanded the opposite happens.
+ * This script saves a per-browser/per-session cookie used to
+ * remember the position of the sidebar among the pages.
+ * Once the browser is closed the cookie is deleted and the position
+ * reset to the default (expanded).
+ *
+ * :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
-$(function() {
+const initialiseSidebar = () => {
// global elements used by the functions.
- // the 'sidebarbutton' element is defined as global after its
- // creation, in the add_sidebar_button function
- var bodywrapper = $('.bodywrapper');
- var sidebar = $('.sphinxsidebar');
- var sidebarwrapper = $('.sphinxsidebarwrapper');
-
- // original margin-left of the bodywrapper and width of the sidebar
- // with the sidebar expanded
- var bw_margin_expanded = bodywrapper.css('margin-left');
- var ssb_width_expanded = sidebar.width();
-
- // margin-left of the bodywrapper and width of the sidebar
- // with the sidebar collapsed
- var bw_margin_collapsed = '.8em';
- var ssb_width_collapsed = '.8em';
-
- // colors used by the current theme
- var dark_color = '#AAAAAA';
- var light_color = '#CCCCCC';
+ const bodyWrapper = document.getElementsByClassName("bodywrapper")[0]
+ const sidebar = document.getElementsByClassName("sphinxsidebar")[0]
+ const sidebarWrapper = document.getElementsByClassName("sphinxsidebarwrapper")[0]
- function sidebar_is_collapsed() {
- return sidebarwrapper.is(':not(:visible)');
+ // exit early if the document has no sidebar for some reason
+ if (typeof sidebar === "undefined") {
+ return
}
- function toggle_sidebar() {
- if (sidebar_is_collapsed())
- expand_sidebar();
- else
- collapse_sidebar();
- }
+ // create the sidebar button element
+ const sidebarButton = document.createElement("div")
+ sidebarButton.id = "sidebarbutton"
+ // create the sidebar button arrow element
+ const sidebarArrow = document.createElement("span")
+ sidebarArrow.innerText = "«"
+ sidebarButton.appendChild(sidebarArrow)
+ sidebar.appendChild(sidebarButton)
- function collapse_sidebar() {
- sidebarwrapper.hide();
- sidebar.css('width', ssb_width_collapsed);
- bodywrapper.css('margin-left', bw_margin_collapsed);
- sidebarbutton.css({
- 'margin-left': '0',
- 'border-radius': '5px'
- });
- sidebarbutton.find('span').text('»');
- sidebarbutton.attr('title', _('Expand sidebar'));
- document.cookie = 'sidebar=collapsed';
+ const collapse_sidebar = () => {
+ bodyWrapper.style.marginLeft = ".8em"
+ sidebar.style.width = ".8em"
+ sidebarWrapper.style.display = "none"
+ sidebarArrow.innerText = "»"
+ sidebarButton.title = _("Expand sidebar")
+ window.localStorage.setItem("sidebar", "collapsed")
}
- function expand_sidebar() {
- bodywrapper.css('margin-left', bw_margin_expanded);
- sidebar.css('width', ssb_width_expanded);
- sidebarwrapper.show();
- sidebarbutton.css({
- 'margin-left': ssb_width_expanded-12,
- 'border-radius': '0 5px 5px 0'
- });
- sidebarbutton.find('span').text('«');
- sidebarbutton.attr('title', _('Collapse sidebar'));
- document.cookie = 'sidebar=expanded';
+ const expand_sidebar = () => {
+ bodyWrapper.style.marginLeft = ""
+ sidebar.style.removeProperty("width")
+ sidebarWrapper.style.display = ""
+ sidebarArrow.innerText = "«"
+ sidebarButton.title = _("Collapse sidebar")
+ window.localStorage.setItem("sidebar", "expanded")
}
- function add_sidebar_button() {
- sidebarwrapper.css({
- 'float': 'left',
- 'margin-right': '0',
- 'width': ssb_width_expanded - 13
- });
- // create the button
- sidebar.append(
- ''
- );
- var sidebarbutton = $('#sidebarbutton');
- sidebarbutton.find('span').css({
- 'display': 'block',
- 'position': 'fixed',
- 'top': '50%'
- });
-
- sidebarbutton.click(toggle_sidebar);
- sidebarbutton.attr('title', _('Collapse sidebar'));
- sidebarbutton.css({
- 'border-radius': '0 5px 5px 0',
- 'color': '#444444',
- 'background-color': '#CCCCCC',
- 'font-size': '1.2em',
- 'cursor': 'pointer',
- 'height': '100%',
- 'padding-left': '1px',
- 'margin-left': ssb_width_expanded - 12
- });
+ sidebarButton.addEventListener("click", () => {
+ (sidebarWrapper.style.display === "none") ? expand_sidebar() : collapse_sidebar()
+ })
- sidebarbutton.hover(
- function () {
- $(this).css('background-color', dark_color);
- },
- function () {
- $(this).css('background-color', light_color);
- }
- );
+ const sidebar_state = window.localStorage.getItem("sidebar")
+ if (sidebar_state === "collapsed") {
+ collapse_sidebar()
}
-
- function set_position_from_cookie() {
- if (!document.cookie)
- return;
- var items = document.cookie.split(';');
- for(var k=0; k