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