summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Turner <9087854+aa-turner@users.noreply.github.com>2022-09-23 20:06:48 +0100
committerAdam Turner <9087854+AA-Turner@users.noreply.github.com>2022-09-24 15:10:57 +0100
commitc7c0e4048d72d4125a63ba9b83effe59d253774c (patch)
treeefe4865208480da6ffff62b7a8aae58e4d852a07
parent8db24515ea4298207a71556518e9a91bfc5ad6a3 (diff)
downloadsphinx-git-c7c0e4048d72d4125a63ba9b83effe59d253774c.tar.gz
Split out `sphinx_highlight.js`
-rw-r--r--doc/conf.py1
-rw-r--r--karma.conf.js2
-rw-r--r--sphinx/builders/html/__init__.py1
-rw-r--r--sphinx/themes/basic/static/doctools.js130
-rw-r--r--sphinx/themes/basic/static/sphinx_highlight.js145
-rw-r--r--tests/js/documentation_options.js1
-rw-r--r--tests/js/sphinx_highlight.js (renamed from tests/js/doctools.js)2
-rw-r--r--tests/test_build_html.py3
8 files changed, 163 insertions, 122 deletions
diff --git a/doc/conf.py b/doc/conf.py
index a721508de..eafa42a7d 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -48,6 +48,7 @@ epub_post_files = [('usage/installation.xhtml', 'Installing Sphinx'),
('develop.xhtml', 'Sphinx development')]
epub_exclude_files = ['_static/opensearch.xml', '_static/doctools.js',
'_static/jquery.js', '_static/searchtools.js',
+ '_static/sphinx_highlight.js',
'_static/underscore.js', '_static/basic.css',
'_static/language_data.js',
'search.html', '_static/websupport.js']
diff --git a/karma.conf.js b/karma.conf.js
index 082584cf7..8a18e80ba 100644
--- a/karma.conf.js
+++ b/karma.conf.js
@@ -15,8 +15,10 @@ module.exports = function(config) {
// list of files / patterns to load in the browser
files: [
+ 'tests/js/documentation_options.js',
'sphinx/themes/basic/static/doctools.js',
'sphinx/themes/basic/static/searchtools.js',
+ 'sphinx/themes/basic/static/sphinx_highlight.js',
'tests/js/*.js'
],
diff --git a/sphinx/builders/html/__init__.py b/sphinx/builders/html/__init__.py
index e80ac3c7e..4fe40eb6f 100644
--- a/sphinx/builders/html/__init__.py
+++ b/sphinx/builders/html/__init__.py
@@ -354,6 +354,7 @@ class StandaloneHTMLBuilder(Builder):
self.add_js_file('underscore.js', priority=200)
self.add_js_file('_sphinx_javascript_frameworks_compat.js', priority=200)
self.add_js_file('doctools.js', priority=200)
+ self.add_js_file('sphinx_highlight.js', priority=200)
for filename, attrs in self.app.registry.js_files:
self.add_js_file(filename, **attrs)
diff --git a/sphinx/themes/basic/static/doctools.js b/sphinx/themes/basic/static/doctools.js
index c3db08d1c..527b876ca 100644
--- a/sphinx/themes/basic/static/doctools.js
+++ b/sphinx/themes/basic/static/doctools.js
@@ -10,6 +10,13 @@
*/
"use strict";
+const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([
+ "TEXTAREA",
+ "INPUT",
+ "SELECT",
+ "BUTTON",
+]);
+
const _ready = (callback) => {
if (document.readyState !== "loading") {
callback();
@@ -19,72 +26,10 @@ const _ready = (callback) => {
};
/**
- * highlight a given string on a node by wrapping it in
- * span elements with the given class name.
- */
-const _highlight = (node, addItems, text, className) => {
- if (node.nodeType === Node.TEXT_NODE) {
- const val = node.nodeValue;
- const parent = node.parentNode;
- const pos = val.toLowerCase().indexOf(text);
- if (
- pos >= 0 &&
- !parent.classList.contains(className) &&
- !parent.classList.contains("nohighlight")
- ) {
- let span;
-
- const closestNode = parent.closest("body, svg, foreignObject");
- const isInSVG = closestNode && closestNode.matches("svg");
- if (isInSVG) {
- span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
- } else {
- span = document.createElement("span");
- span.classList.add(className);
- }
-
- span.appendChild(document.createTextNode(val.substr(pos, text.length)));
- parent.insertBefore(
- span,
- parent.insertBefore(
- document.createTextNode(val.substr(pos + text.length)),
- node.nextSibling
- )
- );
- node.nodeValue = val.substr(0, pos);
-
- if (isInSVG) {
- const rect = document.createElementNS(
- "http://www.w3.org/2000/svg",
- "rect"
- );
- const bbox = parent.getBBox();
- rect.x.baseVal.value = bbox.x;
- rect.y.baseVal.value = bbox.y;
- rect.width.baseVal.value = bbox.width;
- rect.height.baseVal.value = bbox.height;
- rect.setAttribute("class", className);
- addItems.push({ parent: parent, target: rect });
- }
- }
- } else if (node.matches && !node.matches("button, select, textarea")) {
- node.childNodes.forEach((el) => _highlight(el, addItems, text, className));
- }
-};
-const _highlightText = (thisNode, text, className) => {
- let addItems = [];
- _highlight(thisNode, addItems, text, className);
- addItems.forEach((obj) =>
- obj.parent.insertAdjacentElement("beforebegin", obj.target)
- );
-};
-
-/**
* Small JavaScript module for the documentation.
*/
const Documentation = {
init: () => {
- Documentation.highlightSearchWords();
Documentation.initDomainIndexTable();
Documentation.initOnKeyListeners();
},
@@ -127,51 +72,6 @@ const Documentation = {
},
/**
- * highlight the search words provided in the url in the text
- */
- highlightSearchWords: () => {
- const highlight =
- new URLSearchParams(window.location.search).get("highlight") || "";
- const terms = highlight.toLowerCase().split(/\s+/).filter(x => x);
- if (terms.length === 0) return; // nothing to do
-
- // There should never be more than one element matching "div.body"
- const divBody = document.querySelectorAll("div.body");
- const body = divBody.length ? divBody[0] : document.querySelector("body");
- window.setTimeout(() => {
- terms.forEach((term) => _highlightText(body, term, "highlighted"));
- }, 10);
-
- const searchBox = document.getElementById("searchbox");
- if (searchBox === null) return;
- searchBox.appendChild(
- document
- .createRange()
- .createContextualFragment(
- '<p class="highlight-link">' +
- '<a href="javascript:Documentation.hideSearchWords()">' +
- Documentation.gettext("Hide Search Matches") +
- "</a></p>"
- )
- );
- },
-
- /**
- * helper function to hide the search marks again
- */
- hideSearchWords: () => {
- document
- .querySelectorAll("#searchbox .highlight-link")
- .forEach((el) => el.remove());
- document
- .querySelectorAll("span.highlighted")
- .forEach((el) => el.classList.remove("highlighted"));
- const url = new URL(window.location);
- url.searchParams.delete("highlight");
- window.history.replaceState({}, "", url);
- },
-
- /**
* helper function to focus on search bar
*/
focusSearchBar: () => {
@@ -210,15 +110,11 @@ const Documentation = {
)
return;
- const blacklistedElements = new Set([
- "TEXTAREA",
- "INPUT",
- "SELECT",
- "BUTTON",
- ]);
document.addEventListener("keydown", (event) => {
- if (blacklistedElements.has(document.activeElement.tagName)) return; // bail for input elements
- if (event.altKey || event.ctrlKey || event.metaKey) return; // bail with special keys
+ // bail for input elements
+ if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return;
+ // bail with special keys
+ if (event.altKey || event.ctrlKey || event.metaKey) return;
if (!event.shiftKey) {
switch (event.key) {
@@ -240,10 +136,6 @@ const Documentation = {
event.preventDefault();
}
break;
- case "Escape":
- if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break;
- Documentation.hideSearchWords();
- event.preventDefault();
}
}
diff --git a/sphinx/themes/basic/static/sphinx_highlight.js b/sphinx/themes/basic/static/sphinx_highlight.js
new file mode 100644
index 000000000..04889cc9d
--- /dev/null
+++ b/sphinx/themes/basic/static/sphinx_highlight.js
@@ -0,0 +1,145 @@
+/* Highlighting utilities for Sphinx HTML documentation. */
+"use strict";
+
+/**
+ * highlight a given string on a node by wrapping it in
+ * span elements with the given class name.
+ */
+const _highlight = (node, addItems, text, className) => {
+ if (node.nodeType === Node.TEXT_NODE) {
+ const val = node.nodeValue;
+ const parent = node.parentNode;
+ const pos = val.toLowerCase().indexOf(text);
+ if (
+ pos >= 0 &&
+ !parent.classList.contains(className) &&
+ !parent.classList.contains("nohighlight")
+ ) {
+ let span;
+
+ const closestNode = parent.closest("body, svg, foreignObject");
+ const isInSVG = closestNode && closestNode.matches("svg");
+ if (isInSVG) {
+ span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
+ } else {
+ span = document.createElement("span");
+ span.classList.add(className);
+ }
+
+ span.appendChild(document.createTextNode(val.substr(pos, text.length)));
+ parent.insertBefore(
+ span,
+ parent.insertBefore(
+ document.createTextNode(val.substr(pos + text.length)),
+ node.nextSibling
+ )
+ );
+ node.nodeValue = val.substr(0, pos);
+
+ if (isInSVG) {
+ const rect = document.createElementNS(
+ "http://www.w3.org/2000/svg",
+ "rect"
+ );
+ const bbox = parent.getBBox();
+ rect.x.baseVal.value = bbox.x;
+ rect.y.baseVal.value = bbox.y;
+ rect.width.baseVal.value = bbox.width;
+ rect.height.baseVal.value = bbox.height;
+ rect.setAttribute("class", className);
+ addItems.push({ parent: parent, target: rect });
+ }
+ }
+ } else if (node.matches && !node.matches("button, select, textarea")) {
+ node.childNodes.forEach((el) => _highlight(el, addItems, text, className));
+ }
+};
+const _highlightText = (thisNode, text, className) => {
+ let addItems = [];
+ _highlight(thisNode, addItems, text, className);
+ addItems.forEach((obj) =>
+ obj.parent.insertAdjacentElement("beforebegin", obj.target)
+ );
+};
+
+/**
+ * Small JavaScript module for the documentation.
+ */
+const SphinxHighlight = {
+
+ /**
+ * highlight the search words provided in the url in the text
+ */
+ highlightSearchWords: () => {
+ const highlight =
+ new URLSearchParams(window.location.search).get("highlight") || "";
+ const terms = highlight.toLowerCase().split(/\s+/).filter(x => x);
+ if (terms.length === 0) return; // nothing to do
+
+ // There should never be more than one element matching "div.body"
+ const divBody = document.querySelectorAll("div.body");
+ const body = divBody.length ? divBody[0] : document.querySelector("body");
+ window.setTimeout(() => {
+ terms.forEach((term) => _highlightText(body, term, "highlighted"));
+ }, 10);
+
+ const searchBox = document.getElementById("searchbox");
+ if (searchBox === null) return;
+ searchBox.appendChild(
+ document
+ .createRange()
+ .createContextualFragment(
+ '<p class="highlight-link">' +
+ '<a href="javascript:SphinxHighlight.hideSearchWords()">' +
+ Documentation.gettext("Hide Search Matches") +
+ "</a></p>"
+ )
+ );
+ },
+
+ /**
+ * helper function to hide the search marks again
+ */
+ hideSearchWords: () => {
+ document
+ .querySelectorAll("#searchbox .highlight-link")
+ .forEach((el) => el.remove());
+ document
+ .querySelectorAll("span.highlighted")
+ .forEach((el) => el.classList.remove("highlighted"));
+ const url = new URL(window.location);
+ url.searchParams.delete("highlight");
+ window.history.replaceState({}, "", url);
+ },
+
+ initOnKeyListeners: () => {
+ // only install a listener if it is really needed
+ if (
+ !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS
+ )
+ return;
+
+ const blacklistedElements = new Set([
+ "TEXTAREA",
+ "INPUT",
+ "SELECT",
+ "BUTTON",
+ ]);
+ document.addEventListener("keydown", (event) => {
+ if (blacklistedElements.has(document.activeElement.tagName)) return; // bail for input elements
+ if (event.altKey || event.ctrlKey || event.metaKey) return; // bail with special keys
+
+ if (!event.shiftKey) {
+ switch (event.key) {
+ case "Escape":
+ if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break;
+ SphinxHighlight.hideSearchWords();
+ event.preventDefault();
+ }
+ }
+ });
+ },
+};
+
+_ready(SphinxHighlight.highlightSearchWords);
+_ready(SphinxHighlight.initOnKeyListeners);
diff --git a/tests/js/documentation_options.js b/tests/js/documentation_options.js
new file mode 100644
index 000000000..e736460a0
--- /dev/null
+++ b/tests/js/documentation_options.js
@@ -0,0 +1 @@
+const DOCUMENTATION_OPTIONS = {};
diff --git a/tests/js/doctools.js b/tests/js/sphinx_highlight.js
index 7268a6a8c..1f52eabb9 100644
--- a/tests/js/doctools.js
+++ b/tests/js/sphinx_highlight.js
@@ -1,5 +1,3 @@
-const DOCUMENTATION_OPTIONS = {};
-
describe('highlightText', function() {
const cyrillicTerm = 'шеллы';
diff --git a/tests/test_build_html.py b/tests/test_build_html.py
index c7d2daf47..0cdeb4708 100644
--- a/tests/test_build_html.py
+++ b/tests/test_build_html.py
@@ -1232,7 +1232,8 @@ def test_assets_order(app):
# js_files
expected = ['_static/early.js', '_static/jquery.js', '_static/underscore.js',
- '_static/doctools.js', 'https://example.com/script.js', '_static/normal.js',
+ '_static/doctools.js', '_static/sphinx_highlight.js',
+ 'https://example.com/script.js', '_static/normal.js',
'_static/late.js', '_static/js/custom.js', '_static/lazy.js']
pattern = '.*'.join('src="%s"' % f for f in expected)
assert re.search(pattern, content, re.S)