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