|
| 1 | +/* BonitoDocumenter client behaviour: theme, sidebar, copy, highlight, outline, search */ |
| 2 | +(function () { |
| 3 | + // ---- theme (apply ASAP to avoid flash) ---- |
| 4 | + function preferred() { |
| 5 | + var s = localStorage.getItem("docs-theme"); |
| 6 | + if (s) return s; |
| 7 | + return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; |
| 8 | + } |
| 9 | + function applyTheme(t) { |
| 10 | + document.documentElement.classList.toggle("dark", t === "dark"); |
| 11 | + } |
| 12 | + applyTheme(preferred()); |
| 13 | + |
| 14 | + function onReady(fn) { |
| 15 | + if (document.readyState !== "loading") fn(); |
| 16 | + else document.addEventListener("DOMContentLoaded", fn); |
| 17 | + } |
| 18 | + |
| 19 | + // ---- highlight.js (loaded lazily from CDN; degrades gracefully offline) ---- |
| 20 | + var HLJS_VERSION = "11.9.0"; |
| 21 | + var HLJS_BASE = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/" + HLJS_VERSION; |
| 22 | + function setHljsTheme() { |
| 23 | + var link = document.getElementById("hljs-theme"); |
| 24 | + if (!link) return; |
| 25 | + var dark = document.documentElement.classList.contains("dark"); |
| 26 | + link.href = HLJS_BASE + "/styles/" + (dark ? "github-dark" : "github") + ".min.css"; |
| 27 | + } |
| 28 | + function loadHljs(cb) { |
| 29 | + if (window.hljs) { cb(); return; } |
| 30 | + var link = document.createElement("link"); |
| 31 | + link.rel = "stylesheet"; |
| 32 | + link.id = "hljs-theme"; |
| 33 | + document.head.appendChild(link); |
| 34 | + setHljsTheme(); |
| 35 | + var s = document.createElement("script"); |
| 36 | + s.src = HLJS_BASE + "/highlight.min.js"; |
| 37 | + s.onload = function () { |
| 38 | + // The cdnjs "common" build omits some languages (Julia included), so pull |
| 39 | + // in the extra grammars our docs use before highlighting. |
| 40 | + var langs = ["julia", "julia-repl", "toml", "bash", "python"]; |
| 41 | + var remaining = langs.length; |
| 42 | + function done() { if (--remaining <= 0) cb(); } |
| 43 | + langs.forEach(function (lang) { |
| 44 | + var ls = document.createElement("script"); |
| 45 | + ls.src = HLJS_BASE + "/languages/" + lang + ".min.js"; |
| 46 | + ls.onload = done; |
| 47 | + ls.onerror = done; |
| 48 | + document.head.appendChild(ls); |
| 49 | + }); |
| 50 | + }; |
| 51 | + s.onerror = function () { /* offline: leave code unhighlighted */ }; |
| 52 | + document.head.appendChild(s); |
| 53 | + } |
| 54 | + function highlightAll() { |
| 55 | + if (!window.hljs) return; |
| 56 | + document.querySelectorAll('div[class*="language-"] code').forEach(function (el) { |
| 57 | + if (el.dataset.highlighted) return; |
| 58 | + try { window.hljs.highlightElement(el); } catch (e) {} |
| 59 | + }); |
| 60 | + } |
| 61 | + |
| 62 | + onReady(function () { |
| 63 | + // theme toggle |
| 64 | + document.querySelectorAll("[data-toggle-theme]").forEach(function (btn) { |
| 65 | + btn.addEventListener("click", function () { |
| 66 | + var dark = document.documentElement.classList.toggle("dark"); |
| 67 | + localStorage.setItem("docs-theme", dark ? "dark" : "light"); |
| 68 | + setHljsTheme(); |
| 69 | + }); |
| 70 | + }); |
| 71 | + |
| 72 | + // sidebar toggle (mobile) |
| 73 | + var sb = document.querySelector(".VPSidebar"); |
| 74 | + var bd = document.querySelector(".VPSidebar-backdrop"); |
| 75 | + function closeSidebar() { sb && sb.classList.remove("open"); bd && bd.classList.remove("open"); } |
| 76 | + document.querySelectorAll("[data-toggle-sidebar]").forEach(function (b) { |
| 77 | + b.addEventListener("click", function () { |
| 78 | + sb && sb.classList.toggle("open"); |
| 79 | + bd && bd.classList.toggle("open"); |
| 80 | + }); |
| 81 | + }); |
| 82 | + bd && bd.addEventListener("click", closeSidebar); |
| 83 | + |
| 84 | + // version dropdown |
| 85 | + document.querySelectorAll(".VPVersion .trigger").forEach(function (t) { |
| 86 | + t.addEventListener("click", function (e) { |
| 87 | + e.stopPropagation(); |
| 88 | + t.closest(".VPVersion").classList.toggle("open"); |
| 89 | + }); |
| 90 | + }); |
| 91 | + document.addEventListener("click", function () { |
| 92 | + document.querySelectorAll(".VPVersion.open").forEach(function (v) { v.classList.remove("open"); }); |
| 93 | + }); |
| 94 | + |
| 95 | + // copy buttons |
| 96 | + document.querySelectorAll(".vp-doc .copy").forEach(function (btn) { |
| 97 | + btn.addEventListener("click", function () { |
| 98 | + var wrap = btn.closest('div[class*="language-"]'); |
| 99 | + var code = wrap && wrap.querySelector("code"); |
| 100 | + if (!code) return; |
| 101 | + navigator.clipboard.writeText(code.innerText).then(function () { |
| 102 | + btn.classList.add("copied"); |
| 103 | + setTimeout(function () { btn.classList.remove("copied"); }, 1500); |
| 104 | + }); |
| 105 | + }); |
| 106 | + }); |
| 107 | + |
| 108 | + loadHljs(highlightAll); |
| 109 | + initOutline(); |
| 110 | + initSearch(); |
| 111 | + initVersions(); |
| 112 | + renderMath(); |
| 113 | + }); |
| 114 | + |
| 115 | + function initVersions() { |
| 116 | + var menu = document.querySelector(".VPVersion .menu"); |
| 117 | + if (!menu) return; |
| 118 | + // `versions.js` is written by Documenter's `deploydocs` at the docs root and |
| 119 | + // defines `DOC_VERSIONS` (an array of version folder names). |
| 120 | + var versions = window.DOC_VERSIONS; |
| 121 | + if (Array.isArray(versions) && versions.length) { |
| 122 | + menu.innerHTML = versions.map(function (v) { |
| 123 | + return '<a href="' + new URL(v + '/', window.__PSE_VERSIONS_ROOT__).href + '">' + esc(v) + "</a>"; |
| 124 | + }).join(""); |
| 125 | + return; |
| 126 | + } |
| 127 | + // No versions.js (local build, or before deploydocs has written it): fall |
| 128 | + // back to a single entry for the current version so the menu isn't empty. |
| 129 | + var label = document.querySelector(".VPVersion .trigger span"); |
| 130 | + var name = label ? label.textContent.trim() : "dev"; |
| 131 | + menu.innerHTML = '<a href=".">' + esc(name) + "</a>"; |
| 132 | + } |
| 133 | + |
| 134 | + function renderMath() { |
| 135 | + if (typeof window.renderMathInElement !== "function") return; |
| 136 | + try { |
| 137 | + window.renderMathInElement(document.querySelector(".vp-doc") || document.body, { |
| 138 | + delimiters: [ |
| 139 | + { left: "\\[", right: "\\]", display: true }, |
| 140 | + { left: "\\(", right: "\\)", display: false }, |
| 141 | + ], |
| 142 | + throwOnError: false, |
| 143 | + }); |
| 144 | + } catch (e) {} |
| 145 | + } |
| 146 | + |
| 147 | + function initOutline() { |
| 148 | + var links = Array.prototype.slice.call(document.querySelectorAll(".VPAside .outline a")); |
| 149 | + if (!links.length) return; |
| 150 | + var map = new Map(); |
| 151 | + links.forEach(function (a) { |
| 152 | + var id = decodeURIComponent(a.hash.slice(1)); |
| 153 | + var el = document.getElementById(id); |
| 154 | + if (el) map.set(el, a); |
| 155 | + }); |
| 156 | + var obs = new IntersectionObserver(function (entries) { |
| 157 | + entries.forEach(function (e) { |
| 158 | + if (e.isIntersecting) { |
| 159 | + links.forEach(function (l) { l.classList.remove("active"); }); |
| 160 | + var a = map.get(e.target); |
| 161 | + a && a.classList.add("active"); |
| 162 | + } |
| 163 | + }); |
| 164 | + }, { rootMargin: "0px 0px -75% 0px" }); |
| 165 | + map.forEach(function (a, el) { obs.observe(el); }); |
| 166 | + } |
| 167 | + |
| 168 | + function esc(s) { |
| 169 | + return (s || "").replace(/[&<>]/g, function (c) { |
| 170 | + return { "&": "&", "<": "<", ">": ">" }[c]; |
| 171 | + }); |
| 172 | + } |
| 173 | + |
| 174 | + function initSearch() { |
| 175 | + var data = window.__DOCS_SEARCH__ || []; |
| 176 | + var bd = document.querySelector(".VPSearch-backdrop"); |
| 177 | + var input = document.querySelector(".VPSearch input"); |
| 178 | + var results = document.querySelector(".VPSearch .results"); |
| 179 | + if (!bd || !input || !results) return; |
| 180 | + var active = -1, items = []; |
| 181 | + |
| 182 | + function open() { bd.classList.add("open"); input.value = ""; render(""); setTimeout(function () { input.focus(); }, 0); } |
| 183 | + function close() { bd.classList.remove("open"); } |
| 184 | + |
| 185 | + function render(q) { |
| 186 | + q = q.trim().toLowerCase(); |
| 187 | + var matches = q |
| 188 | + ? data.filter(function (d) { |
| 189 | + return d.title.toLowerCase().indexOf(q) >= 0 || d.text.toLowerCase().indexOf(q) >= 0; |
| 190 | + }).slice(0, 30) |
| 191 | + : data.slice(0, 8); |
| 192 | + items = matches; |
| 193 | + active = matches.length ? 0 : -1; |
| 194 | + if (!matches.length) { results.innerHTML = '<div class="empty">No results</div>'; return; } |
| 195 | + results.innerHTML = matches.map(function (m, i) { |
| 196 | + return '<a class="result ' + (i === 0 ? "active" : "") + '" href="' + m.url + '">' + |
| 197 | + '<div class="r-title">' + esc(m.title) + "</div>" + |
| 198 | + '<div class="r-context">' + esc(m.text.slice(0, 120)) + "</div></a>"; |
| 199 | + }).join(""); |
| 200 | + } |
| 201 | + |
| 202 | + input.addEventListener("input", function () { render(input.value); }); |
| 203 | + input.addEventListener("keydown", function (e) { |
| 204 | + var els = Array.prototype.slice.call(results.querySelectorAll(".result")); |
| 205 | + if (e.key === "ArrowDown") { e.preventDefault(); active = Math.min(active + 1, els.length - 1); } |
| 206 | + else if (e.key === "ArrowUp") { e.preventDefault(); active = Math.max(active - 1, 0); } |
| 207 | + else if (e.key === "Enter") { if (items[active]) window.location.href = items[active].url; return; } |
| 208 | + else if (e.key === "Escape") { close(); return; } |
| 209 | + els.forEach(function (el, i) { el.classList.toggle("active", i === active); }); |
| 210 | + els[active] && els[active].scrollIntoView({ block: "nearest" }); |
| 211 | + }); |
| 212 | + bd.addEventListener("click", function (e) { if (e.target === bd) close(); }); |
| 213 | + document.querySelectorAll("[data-open-search]").forEach(function (b) { b.addEventListener("click", open); }); |
| 214 | + document.addEventListener("keydown", function (e) { |
| 215 | + if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") { e.preventDefault(); open(); } |
| 216 | + }); |
| 217 | + } |
| 218 | +})(); |
0 commit comments