1/* Highlighting utilities for Sphinx HTML documentation. */ 2"use strict"; 3 4const 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 */ 10const _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}; 58const _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 */ 69const 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 url.searchParams.delete("highlight"); 84 window.history.replaceState({}, "", url); 85 86 // get individual terms from highlight string 87 const terms = highlight 88 .toLowerCase() 89 .split(/\s+/) 90 .filter((x) => x); 91 if (terms.length === 0) return; // nothing to do 92 93 // There should never be more than one element matching "div.body" 94 const divBody = document.querySelectorAll("div.body"); 95 const body = divBody.length ? divBody[0] : document.querySelector("body"); 96 window.setTimeout(() => { 97 terms.forEach((term) => _highlightText(body, term, "highlighted")); 98 }, 10); 99 100 const searchBox = document.getElementById("searchbox"); 101 if (searchBox === null) return; 102 searchBox.appendChild( 103 document 104 .createRange() 105 .createContextualFragment( 106 '<p class="highlight-link">' + 107 '<a href="javascript:SphinxHighlight.hideSearchWords()">' + 108 _("Hide Search Matches") + 109 "</a></p>", 110 ), 111 ); 112 }, 113 114 /** 115 * helper function to hide the search marks again 116 */ 117 hideSearchWords: () => { 118 document 119 .querySelectorAll("#searchbox .highlight-link") 120 .forEach((el) => el.remove()); 121 document 122 .querySelectorAll("span.highlighted") 123 .forEach((el) => el.classList.remove("highlighted")); 124 localStorage.removeItem("sphinx_highlight_terms"); 125 }, 126 127 initEscapeListener: () => { 128 // only install a listener if it is really needed 129 if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; 130 131 document.addEventListener("keydown", (event) => { 132 // bail for input elements 133 if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) 134 return; 135 // bail with special keys 136 if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) 137 return; 138 if ( 139 DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && 140 event.key === "Escape" 141 ) { 142 SphinxHighlight.hideSearchWords(); 143 event.preventDefault(); 144 } 145 }); 146 }, 147}; 148 149_ready(() => { 150 /* Do not call highlightSearchWords() when we are on the search page. 151 * It will highlight words from the *previous* search query. 152 */ 153 if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords(); 154 SphinxHighlight.initEscapeListener(); 155});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.