doctools.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. /*
  2. * Base JavaScript utilities for all Sphinx HTML documentation.
  3. */
  4. "use strict";
  5. const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([
  6. "TEXTAREA",
  7. "INPUT",
  8. "SELECT",
  9. "BUTTON",
  10. ]);
  11. const _ready = (callback) => {
  12. if (document.readyState !== "loading") {
  13. callback();
  14. } else {
  15. document.addEventListener("DOMContentLoaded", callback);
  16. }
  17. };
  18. /**
  19. * Small JavaScript module for the documentation.
  20. */
  21. const Documentation = {
  22. init: () => {
  23. Documentation.initDomainIndexTable();
  24. Documentation.initOnKeyListeners();
  25. },
  26. /**
  27. * i18n support
  28. */
  29. TRANSLATIONS: {},
  30. PLURAL_EXPR: (n) => (n === 1 ? 0 : 1),
  31. LOCALE: "unknown",
  32. // gettext and ngettext don't access this so that the functions
  33. // can safely bound to a different name (_ = Documentation.gettext)
  34. gettext: (string) => {
  35. const translated = Documentation.TRANSLATIONS[string];
  36. switch (typeof translated) {
  37. case "undefined":
  38. return string; // no translation
  39. case "string":
  40. return translated; // translation exists
  41. default:
  42. return translated[0]; // (singular, plural) translation tuple exists
  43. }
  44. },
  45. ngettext: (singular, plural, n) => {
  46. const translated = Documentation.TRANSLATIONS[singular];
  47. if (typeof translated !== "undefined")
  48. return translated[Documentation.PLURAL_EXPR(n)];
  49. return n === 1 ? singular : plural;
  50. },
  51. addTranslations: (catalog) => {
  52. Object.assign(Documentation.TRANSLATIONS, catalog.messages);
  53. Documentation.PLURAL_EXPR = new Function(
  54. "n",
  55. `return (${catalog.plural_expr})`
  56. );
  57. Documentation.LOCALE = catalog.locale;
  58. },
  59. /**
  60. * helper function to focus on search bar
  61. */
  62. focusSearchBar: () => {
  63. document.querySelectorAll("input[name=q]")[0]?.focus();
  64. },
  65. /**
  66. * Initialise the domain index toggle buttons
  67. */
  68. initDomainIndexTable: () => {
  69. const toggler = (el) => {
  70. const idNumber = el.id.substr(7);
  71. const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`);
  72. if (el.src.substr(-9) === "minus.png") {
  73. el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`;
  74. toggledRows.forEach((el) => (el.style.display = "none"));
  75. } else {
  76. el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`;
  77. toggledRows.forEach((el) => (el.style.display = ""));
  78. }
  79. };
  80. const togglerElements = document.querySelectorAll("img.toggler");
  81. togglerElements.forEach((el) =>
  82. el.addEventListener("click", (event) => toggler(event.currentTarget))
  83. );
  84. togglerElements.forEach((el) => (el.style.display = ""));
  85. if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler);
  86. },
  87. initOnKeyListeners: () => {
  88. // only install a listener if it is really needed
  89. if (
  90. !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS &&
  91. !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS
  92. )
  93. return;
  94. document.addEventListener("keydown", (event) => {
  95. // bail for input elements
  96. if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return;
  97. // bail with special keys
  98. if (event.altKey || event.ctrlKey || event.metaKey) return;
  99. if (!event.shiftKey) {
  100. switch (event.key) {
  101. case "ArrowLeft":
  102. if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break;
  103. const prevLink = document.querySelector('link[rel="prev"]');
  104. if (prevLink && prevLink.href) {
  105. window.location.href = prevLink.href;
  106. event.preventDefault();
  107. }
  108. break;
  109. case "ArrowRight":
  110. if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break;
  111. const nextLink = document.querySelector('link[rel="next"]');
  112. if (nextLink && nextLink.href) {
  113. window.location.href = nextLink.href;
  114. event.preventDefault();
  115. }
  116. break;
  117. }
  118. }
  119. // some keyboard layouts may need Shift to get /
  120. switch (event.key) {
  121. case "/":
  122. if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break;
  123. Documentation.focusSearchBar();
  124. event.preventDefault();
  125. }
  126. });
  127. },
  128. };
  129. // quick alias for translations
  130. const _ = Documentation.gettext;
  131. _ready(Documentation.init);