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);