sphinx_highlight.js - sphere - GPU-based 3D discrete element method algorithm with optional fluid coupling
HTML git clone git://src.adamsgaard.dk/sphere
DIR Log
DIR Files
DIR Refs
DIR LICENSE
---
sphinx_highlight.js (5325B)
---
1 /* Highlighting utilities for Sphinx HTML documentation. */
2 "use strict";
3
4 const SPHINX_HIGHLIGHT_ENABLED = true;
5
6 /**
7 * highlight a given string on a node by wrapping it in
8 * span elements with the given class name.
9 */
10 const _highlight = (node, addItems, text, className) => {
11 if (node.nodeType === Node.TEXT_NODE) {
12 const val = node.nodeValue;
13 const parent = node.parentNode;
14 const pos = val.toLowerCase().indexOf(text);
15 if (
16 pos >= 0
17 && !parent.classList.contains(className)
18 && !parent.classList.contains("nohighlight")
19 ) {
20 let span;
21
22 const closestNode = parent.closest("body, svg, foreignObject");
23 const isInSVG = closestNode && closestNode.matches("svg");
24 if (isInSVG) {
25 span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
26 } else {
27 span = document.createElement("span");
28 span.classList.add(className);
29 }
30
31 span.appendChild(document.createTextNode(val.substr(pos, text.length)));
32 const rest = document.createTextNode(val.substr(pos + text.length));
33 parent.insertBefore(span, parent.insertBefore(rest, node.nextSibling));
34 node.nodeValue = val.substr(0, pos);
35 /* There may be more occurrences of search term in this node. So call this
36 * function recursively on the remaining fragment.
37 */
38 _highlight(rest, addItems, text, className);
39
40 if (isInSVG) {
41 const rect = document.createElementNS(
42 "http://www.w3.org/2000/svg",
43 "rect",
44 );
45 const bbox = parent.getBBox();
46 rect.x.baseVal.value = bbox.x;
47 rect.y.baseVal.value = bbox.y;
48 rect.width.baseVal.value = bbox.width;
49 rect.height.baseVal.value = bbox.height;
50 rect.setAttribute("class", className);
51 addItems.push({ parent: parent, target: rect });
52 }
53 }
54 } else if (node.matches && !node.matches("button, select, textarea")) {
55 node.childNodes.forEach((el) => _highlight(el, addItems, text, className));
56 }
57 };
58 const _highlightText = (thisNode, text, className) => {
59 let addItems = [];
60 _highlight(thisNode, addItems, text, className);
61 addItems.forEach((obj) =>
62 obj.parent.insertAdjacentElement("beforebegin", obj.target),
63 );
64 };
65
66 /**
67 * Small JavaScript module for the documentation.
68 */
69 const SphinxHighlight = {
70 /**
71 * highlight the search words provided in localstorage in the text
72 */
73 highlightSearchWords: () => {
74 if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight
75
76 // get and clear terms from localstorage
77 const url = new URL(window.location);
78 const highlight =
79 localStorage.getItem("sphinx_highlight_terms")
80 || url.searchParams.get("highlight")
81 || "";
82 localStorage.removeItem("sphinx_highlight_terms");
83 // Update history only if '?highlight' is present; otherwise it
84 // clears text fragments (not set in window.location by the browser)
85 if (url.searchParams.has("highlight")) {
86 url.searchParams.delete("highlight");
87 window.history.replaceState({}, "", url);
88 }
89
90 // get individual terms from highlight string
91 const terms = highlight
92 .toLowerCase()
93 .split(/\s+/)
94 .filter((x) => x);
95 if (terms.length === 0) return; // nothing to do
96
97 // There should never be more than one element matching "div.body"
98 const divBody = document.querySelectorAll("div.body");
99 const body = divBody.length ? divBody[0] : document.querySelector("body");
100 window.setTimeout(() => {
101 terms.forEach((term) => _highlightText(body, term, "highlighted"));
102 }, 10);
103
104 const searchBox = document.getElementById("searchbox");
105 if (searchBox === null) return;
106 searchBox.appendChild(
107 document
108 .createRange()
109 .createContextualFragment(
110 '<p class="highlight-link">'
111 + '<a href="javascript:SphinxHighlight.hideSearchWords()">'
112 + _("Hide Search Matches")
113 + "</a></p>",
114 ),
115 );
116 },
117
118 /**
119 * helper function to hide the search marks again
120 */
121 hideSearchWords: () => {
122 document
123 .querySelectorAll("#searchbox .highlight-link")
124 .forEach((el) => el.remove());
125 document
126 .querySelectorAll("span.highlighted")
127 .forEach((el) => el.classList.remove("highlighted"));
128 localStorage.removeItem("sphinx_highlight_terms");
129 },
130
131 initEscapeListener: () => {
132 // only install a listener if it is really needed
133 if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return;
134
135 document.addEventListener("keydown", (event) => {
136 // bail for input elements
137 if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName))
138 return;
139 // bail with special keys
140 if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey)
141 return;
142 if (
143 DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS
144 && event.key === "Escape"
145 ) {
146 SphinxHighlight.hideSearchWords();
147 event.preventDefault();
148 }
149 });
150 },
151 };
152
153 _ready(() => {
154 /* Do not call highlightSearchWords() when we are on the search page.
155 * It will highlight words from the *previous* search query.
156 */
157 if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords();
158 SphinxHighlight.initEscapeListener();
159 });