1// catch all plugin for various quarto features 2window.QuartoSupport = function () { 3 function isPrintView() { 4 return /print-pdf/gi.test(window.location.search); 5 } 6 7 // helper for theme toggling 8 function toggleBackgroundTheme(el, onDarkBackground, onLightBackground) { 9 if (onDarkBackground) { 10 el.classList.add('has-dark-background') 11 } else { 12 el.classList.remove('has-dark-background') 13 } 14 if (onLightBackground) { 15 el.classList.add('has-light-background') 16 } else { 17 el.classList.remove('has-light-background') 18 } 19 } 20 21 // implement controlsAudo 22 function controlsAuto(deck) { 23 const config = deck.getConfig(); 24 if (config.controlsAuto === true) { 25 const iframe = window.location !== window.parent.location; 26 const localhost = 27 window.location.hostname === "localhost" || 28 window.location.hostname === "127.0.0.1"; 29 deck.configure({ 30 controls: 31 (iframe && !localhost) || 32 (deck.hasVerticalSlides() && config.navigationMode !== "linear"), 33 }); 34 } 35 } 36 37 // helper to provide event handlers for all links in a container 38 function handleLinkClickEvents(deck, container) { 39 Array.from(container.querySelectorAll("a")).forEach((el) => { 40 const url = el.getAttribute("href"); 41 if (/^(http|www)/gi.test(url)) { 42 el.addEventListener( 43 "click", 44 (ev) => { 45 const fullscreen = !!window.document.fullscreen; 46 const dataPreviewLink = el.getAttribute("data-preview-link"); 47 48 // if there is a local specifcation then use that 49 if (dataPreviewLink) { 50 if ( 51 dataPreviewLink === "true" || 52 (dataPreviewLink === "auto" && fullscreen) 53 ) { 54 ev.preventDefault(); 55 deck.showPreview(url); 56 return false; 57 } 58 } else { 59 const previewLinks = !!deck.getConfig().previewLinks; 60 const previewLinksAuto = 61 deck.getConfig().previewLinksAuto === true; 62 if (previewLinks == true || (previewLinksAuto && fullscreen)) { 63 ev.preventDefault(); 64 deck.showPreview(url); 65 return false; 66 } 67 } 68 69 // if the deck is in an iframe we want to open it externally 70 // (don't do this when in vscode though as it has its own 71 // handler for opening links externally that will be play) 72 const iframe = window.location !== window.parent.location; 73 if ( 74 iframe && 75 !window.location.search.includes("quartoPreviewReqId=") 76 ) { 77 ev.preventDefault(); 78 ev.stopImmediatePropagation(); 79 window.open(url, "_blank"); 80 return false; 81 } 82 83 // if the user has set data-preview-link to "auto" we need to handle the event 84 // (because reveal will interpret "auto" as true) 85 if (dataPreviewLink === "auto") { 86 ev.preventDefault(); 87 ev.stopImmediatePropagation(); 88 const target = 89 el.getAttribute("target") || 90 (ev.ctrlKey || ev.metaKey ? "_blank" : ""); 91 if (target) { 92 window.open(url, target); 93 } else { 94 window.location.href = url; 95 } 96 return false; 97 } 98 }, 99 false 100 ); 101 } 102 }); 103 } 104 105 // implement previewLinksAuto 106 function previewLinksAuto(deck) { 107 handleLinkClickEvents(deck, deck.getRevealElement()); 108 } 109 110 // apply styles 111 function applyGlobalStyles(deck) { 112 if (deck.getConfig()["smaller"] === true) { 113 const revealParent = deck.getRevealElement(); 114 revealParent.classList.add("smaller"); 115 } 116 } 117 118 // add logo image 119 function addLogoImage(deck) { 120 const revealParent = deck.getRevealElement(); 121 const logoImg = document.querySelector(".slide-logo"); 122 if (logoImg) { 123 revealParent.appendChild(logoImg); 124 revealParent.classList.add("has-logo"); 125 } 126 } 127 128 // tweak slide-number element 129 function tweakSlideNumber(deck) { 130 deck.on("slidechanged", function (ev) { 131 const revealParent = deck.getRevealElement(); 132 const slideNumberEl = revealParent.querySelector(".slide-number");
133 const onDarkBackground = Reveal.getSlideBackground(ev.indexh, ev.indexv).classList.contains('has-dark-background'); 134 const onLightBackground = Reveal.getSlideBackground(ev.indexh, ev.indexv).classList.contains('has-light-background'); 135 toggleBackgroundTheme(slideNumberEl, onDarkBackground, onLightBackground); 136 }) 137 } 138 139 // add footer text 140 function addFooter(deck) { 141 const revealParent = deck.getRevealElement(); 142 const defaultFooterDiv = document.querySelector(".footer-default"); 143 if (defaultFooterDiv) { 144 revealParent.appendChild(defaultFooterDiv); 145 handleLinkClickEvents(deck, defaultFooterDiv); 146 if (!isPrintView()) { 147 deck.on("slidechanged", function (ev) { 148 const prevSlideFooter = document.querySelector( 149 ".reveal > .footer:not(.footer-default)" 150 ); 151 if (prevSlideFooter) { 152 prevSlideFooter.remove(); 153 } 154 const currentSlideFooter = ev.currentSlide.querySelector(".footer"); 155 const onDarkBackground = Reveal.getSlideBackground(ev.indexh, ev.indexv).classList.contains('has-dark-background')
156 const onLightBackground = Reveal.getSlideBackground(ev.indexh, ev.indexv).classList.contains('has-light-background') 157 if (currentSlideFooter) { 158 defaultFooterDiv.style.display = "none"; 159 const slideFooter = currentSlideFooter.cloneNode(true); 160 handleLinkClickEvents(deck, slideFooter); 161 deck.getRevealElement().appendChild(slideFooter); 162 toggleBackgroundTheme(slideFooter, onDarkBackground, onLightBackground) 163 } else { 164 defaultFooterDiv.style.display = "block"; 165 toggleBackgroundTheme(defaultFooterDiv, onDarkBackground, onLightBackground) 166 } 167 }); 168 } 169 } 170 } 171 172 // add chalkboard buttons 173 function addChalkboardButtons(deck) { 174 const chalkboard = deck.getPlugin("RevealChalkboard"); 175 if (chalkboard && !isPrintView()) { 176 const revealParent = deck.getRevealElement(); 177 const chalkboardDiv = document.createElement("div"); 178 chalkboardDiv.classList.add("slide-chalkboard-buttons"); 179 if (document.querySelector(".slide-menu-button")) { 180 chalkboardDiv.classList.add("slide-menu-offset"); 181 } 182 // add buttons 183 const buttons = [ 184 { 185 icon: "easel2", 186 title: "Toggle Chalkboard (b)", 187 onclick: chalkboard.toggleChalkboard, 188 }, 189 { 190 icon: "brush", 191 title: "Toggle Notes Canvas (c)", 192 onclick: chalkboard.toggleNotesCanvas, 193 }, 194 ]; 195 buttons.forEach(function (button) { 196 const span = document.createElement("span"); 197 span.title = button.title; 198 const icon = document.createElement("i"); 199 icon.classList.add("fas"); 200 icon.classList.add("fa-" + button.icon); 201 span.appendChild(icon); 202 span.onclick = function (event) { 203 event.preventDefault(); 204 button.onclick(); 205 }; 206 chalkboardDiv.appendChild(span); 207 }); 208 revealParent.appendChild(chalkboardDiv); 209 const config = deck.getConfig(); 210 if (!config.chalkboard.buttons) { 211 chalkboardDiv.classList.add("hidden"); 212 } 213 214 // show and hide chalkboard buttons on slidechange 215 deck.on("slidechanged", function (ev) { 216 const config = deck.getConfig(); 217 let buttons = !!config.chalkboard.buttons; 218 const slideButtons = ev.currentSlide.getAttribute( 219 "data-chalkboard-buttons" 220 ); 221 if (slideButtons) { 222 if (slideButtons === "true" || slideButtons === "1") { 223 buttons = true; 224 } else if (slideButtons === "false" || slideButtons === "0") { 225 buttons = false; 226 } 227 } 228 if (buttons) { 229 chalkboardDiv.classList.remove("hidden"); 230 } else { 231 chalkboardDiv.classList.add("hidden"); 232 } 233 }); 234 } 235 } 236 237 function handleTabbyClicks() { 238 const tabs = document.querySelectorAll(".panel-tabset-tabby > li > a"); 239 for (let i = 0; i < tabs.length; i++) { 240 const tab = tabs[i]; 241 tab.onclick = function (ev) { 242 ev.preventDefault(); 243 ev.stopPropagation(); 244 return false; 245 }; 246 } 247 } 248 249 function fixupForPrint(deck) { 250 if (isPrintView()) { 251 const slides = deck.getSlides(); 252 slides.forEach(function (slide) { 253 slide.removeAttribute("data-auto-animate"); 254 }); 255 window.document.querySelectorAll(".hljs").forEach(function (el) { 256 el.classList.remove("hljs"); 257 }); 258 window.document.querySelectorAll(".hljs-ln-code").forEach(function (el) { 259 el.classList.remove("hljs-ln-code"); 260 }); 261 } 262 } 263 264 function handleSlideChanges(deck) { 265 // dispatch for htmlwidgets 266 const fireSlideEnter = () => { 267 const event = window.document.createEvent("Event"); 268 event.initEvent("slideenter", true, true); 269 window.document.dispatchEvent(event); 270 }; 271 272 const fireSlideChanged = (previousSlide, currentSlide) => { 273 fireSlideEnter(); 274 275 // dispatch for shiny 276 if (window.jQuery) { 277 if (previousSlide) { 278 window.jQuery(previousSlide).trigger("hidden"); 279 } 280 if (currentSlide) { 281 window.jQuery(currentSlide).trigger("shown"); 282 } 283 } 284 }; 285 286 // fire slideEnter for tabby tab activations (for htmlwidget resize behavior) 287 document.addEventListener("tabby", fireSlideEnter, false); 288 289 deck.on("slidechanged", function (event) { 290 fireSlideChanged(event.previousSlide, event.currentSlide); 291 }); 292 } 293 294 function workaroundMermaidDistance(deck) { 295 if (window.document.querySelector("pre.mermaid-js")) { 296 const slideCount = deck.getTotalSlides(); 297 deck.configure({ 298 mobileViewDistance: slideCount, 299 viewDistance: slideCount, 300 }); 301 } 302 } 303 304 function handleWhiteSpaceInColumns(deck) { 305 for (const outerDiv of window.document.querySelectorAll("div.columns")) { 306 // remove all whitespace text nodes 307 // whitespace nodes cause the columns to be misaligned 308 // since they have inline-block layout 309 // 310 // Quarto emits no whitespace nodes, but third-party tooling 311 // has bugs that can cause whitespace nodes to be emitted.
312 // See https://github.com/quarto-dev/quarto-cli/issues/8382 313 for (const node of outerDiv.childNodes) { 314 if (node.nodeType === 3 && node.nodeValue.trim() === "") { 315 outerDiv.removeChild(node); 316 } 317 } 318 } 319 } 320 321 return { 322 id: "quarto-support", 323 init: function (deck) { 324 controlsAuto(deck); 325 previewLinksAuto(deck); 326 fixupForPrint(deck); 327 applyGlobalStyles(deck); 328 addLogoImage(deck); 329 tweakSlideNumber(deck); 330 addFooter(deck); 331 addChalkboardButtons(deck); 332 handleTabbyClicks(); 333 handleSlideChanges(deck); 334 workaroundMermaidDistance(deck); 335 handleWhiteSpaceInColumns(deck); 336 }, 337 }; 338};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.