Python Library Caching
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

sphinx_highlight.js 5.0KB

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