PageSourceSearch

https://soci-269-w3.netlify.app/index_files/libs/revealjs/plugin/quarto-support/support.js

js soci-269-w3.netlify.app collected 2026-10-03 11:26:02 UTC 14,339 bytes, 419 lines download raw bytes

1// catch all plugin for various quarto features
2window.QuartoSupport = function () {
3  function isPrintView() {
4    return /print-pdf/gi.test(window.location.search) || /view=print/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      // No slide number in scroll view
132      if (deck.isScrollView()) { return }
133      const revealParent = deck.getRevealElement();
134      const slideNumberEl = revealParent.querySelector(".slide-number");
135      const slideBackground = Reveal.getSlideBackground(ev.currentSlide);
136      const onDarkBackground = slideBackground.classList.contains('has-dark-background')
137      const onLightBackground = slideBackground.classList.contains('has-light-background')
138      toggleBackgroundTheme(slideNumberEl, onDarkBackground, onLightBackground);
139    })
140  }
141
142  // add footer text
143  function addFooter(deck) {
144    const revealParent = deck.getRevealElement();
145    const defaultFooterDiv = document.querySelector(".footer-default");
146    // Set per slide footer if any defined, 
147    // or show default unless data-footer="false" for no footer on this slide
148    const setSlideFooter = (ev, defaultFooterDiv) => {
149      const currentSlideFooter = ev.currentSlide.querySelector(".footer");
150      const onDarkBackground = deck.getSlideBackground(ev.currentSlide).classList.contains('has-dark-background')
151      const onLightBackground = deck.getSlideBackground(ev.currentSlide).classList.contains('has-light-background')
152      if (currentSlideFooter) {
153        defaultFooterDiv.style.display = "none";
154        const slideFooter = currentSlideFooter.cloneNode(true);
155        handleLinkClickEvents(deck, slideFooter);
156        deck.getRevealElement().appendChild(slideFooter);
157        toggleBackgroundTheme(slideFooter, onDarkBackground, onLightBackground)
158      } else if (ev.currentSlide.getAttribute("data-footer") === "false") {
159        defaultFooterDiv.style.display = "none";
160      } else {
161        defaultFooterDiv.style.display = "block";
162        toggleBackgroundTheme(defaultFooterDiv, onDarkBackground, onLightBackground)
163      }
164    }
165    if (defaultFooterDiv) {
166      // move default footnote to the div.reveal element
167      revealParent.appendChild(defaultFooterDiv);
168      handleLinkClickEvents(deck, defaultFooterDiv);
169
170      if (!isPrintView()) {
171        // Ready even is needed so that footer customization applies on first loaded slide
172        deck.on('ready', (ev) => {
173          // Set footer (custom, default or none)
174          setSlideFooter(ev, defaultFooterDiv)
175        });
176        // Any new navigated new slide will get the custom footnote check
177        deck.on("slidechanged", function (ev) {
178          // Remove presentation footer defined by previous slide
179          const prevSlideFooter = document.querySelector(
180            ".reveal > .footer:not(.footer-default)"
181          );
182          if (prevSlideFooter) {
183            prevSlideFooter.remove();
184          }
185          // Set new one (custom, default or none)
186          setSlideFooter(ev, defaultFooterDiv)
187        });
188      }
189    }
190  }
191
192  // add chalkboard buttons
193  function addChalkboardButtons(deck) {
194    const chalkboard = deck.getPlugin("RevealChalkboard");
195    if (chalkboard && !isPrintView()) {
196      const revealParent = deck.getRevealElement();
197      const chalkboardDiv = document.createElement("div");
198      chalkboardDiv.classList.add("slide-chalkboard-buttons");
199      if (document.querySelector(".slide-menu-button")) {
200        chalkboardDiv.classList.add("slide-menu-offset");
201      }
202      // add buttons
203      const buttons = [
204        {
205          icon: "easel2",
206          title: "Toggle Chalkboard (b)",
207          onclick: chalkboard.toggleChalkboard,
208        },
209        {
210          icon: "brush",
211          title: "Toggle Notes Canvas (c)",
212          onclick: chalkboard.toggleNotesCanvas,
213        },
214      ];
215      buttons.forEach(function (button) {
216        const span = document.createElement("span");
217        span.title = button.title;
218        const icon = document.createElement("i");
219        icon.classList.add("fas");
220        icon.classList.add("fa-" + button.icon);
221        span.appendChild(icon);
222        span.onclick = function (event) {
223          event.preventDefault();
224          button.onclick();
225        };
226        chalkboardDiv.appendChild(span);
227      });
228      revealParent.appendChild(chalkboardDiv);
229      const config = deck.getConfig();
230      if (!config.chalkboard.buttons) {
231        chalkboardDiv.classList.add("hidden");
232      }
233
234      // show and hide chalkboard buttons on slidechange
235      deck.on("slidechanged", function (ev) {
236        const config = deck.getConfig();
237        let buttons = !!config.chalkboard.buttons;
238        const slideButtons = ev.currentSlide.getAttribute(
239          "data-chalkboard-buttons"
240        );
241        if (slideButtons) {
242          if (slideButtons === "true" || slideButtons === "1") {
243            buttons = true;
244          } else if (slideButtons === "false" || slideButtons === "0") {
245            buttons = false;
246          }
247        }
248        if (buttons) {
249          chalkboardDiv.classList.remove("hidden");
250        } else {
251          chalkboardDiv.classList.add("hidden");
252        }
253      });
254    }
255  }
256
257  function handleTabbyClicks() {
258    const tabs = document.querySelectorAll(".panel-tabset-tabby > li > a");
259    for (let i = 0; i < tabs.length; i++) {
260      const tab = tabs[i];
261      tab.onclick = function (ev) {
262        ev.preventDefault();
263        ev.stopPropagation();
264        return false;
265      };
266    }
267  }
268
269  function fixupForPrint(deck) {
270    if (isPrintView()) {
271      const slides = deck.getSlides();
272      slides.forEach(function (slide) {
273        slide.removeAttribute("data-auto-animate");
274      });
275      window.document.querySelectorAll(".hljs").forEach(function (el) {
276        el.classList.remove("hljs");
277      });
278      window.document.querySelectorAll(".hljs-ln-code").forEach(function (el) {
279        el.classList.remove("hljs-ln-code");
280      });
281    }
282  }
283
284  // dispatch for htmlwidgets
285  // they use slideenter event to trigger resize
286  const fireSlideEnter = () => {
287    const event = window.document.createEvent("Event");
288    event.initEvent("slideenter", true, true);
289    window.document.dispatchEvent(event);
290  };
291
292  // dispatch for shiny
293  // they use BS shown and hidden events to trigger rendering
294  const distpatchShinyEvents = (previous, current) => {
295    if (window.jQuery) {
296      if (previous) {
297        window.jQuery(previous).trigger("hidden");
298      }
299      if (current) {
300        window.jQuery(current).trigger("shown");
301      }
302    }
303  };
304
305  function handleSlideChanges(deck) {
306
307    const fireSlideChanged = (previousSlide, currentSlide) => {
308      fireSlideEnter();
309      distpatchShinyEvents(previousSlide, currentSlide);
310    };
311
312    deck.on("slidechanged", function (event) {
313      fireSlideChanged(event.previousSlide, event.currentSlide);
314    });
315  }
316
317  function handleTabbyChanges() {
318    const fireTabChanged = (previousTab, currentTab) => {
319      fireSlideEnter()
320      distpatchShinyEvents(previousTab, currentTab);
321    };
322    document.addEventListener("tabby", function(event) {
323      fireTabChanged(event.detail.previousTab, event.detail.tab);
324    }, false);
325  }
326
327  function workaroundMermaidDistance(deck) {
328    if (window.document.querySelector("pre.mermaid-js")) {
329      const slideCount = deck.getTotalSlides();
330      deck.configure({
331        mobileViewDistance: slideCount,
332        viewDistance: slideCount,
333      });
334    }
335  }
336
337  function handleWhiteSpaceInColumns(deck) {
338    for (const outerDiv of window.document.querySelectorAll("div.columns")) {
339      // remove all whitespace text nodes
340      // whitespace nodes cause the columns to be misaligned
341      // since they have inline-block layout
342      // 
343      // Quarto emits no whitespace nodes, but third-party tooling
344      // has bugs that can cause whitespace nodes to be emitted.
345      // See https://github.com/quarto-dev/quarto-cli/issues/8382
346      for (const node of outerDiv.childNodes) {
347        if (node.nodeType === 3 && node.nodeValue.trim() === "") {
348          outerDiv.removeChild(node);
349        }
350      }
351    }
352  }
353
354  function cleanEmptyAutoGeneratedContent(deck) {
355    const div = document.querySelector('div.quarto-auto-generated-content')
356    if (div && div.textContent.trim() === '') {
357      div.remove()
358    }
359  }
360
361  // FIXME: Possibly remove this wrapper class when upstream trigger is fixed
362  // https://github.com/hakimel/reveal.js/issues/3688
363  // Currently, scrollActivationWidth needs to be unset for toggle to work
364  class ScrollViewToggler {
365    constructor(deck) {
366      this.deck = deck;
367      this.oldScrollActivationWidth = deck.getConfig()['scrollActivationWidth'];
368    }
369  
370    toggleScrollViewWrapper() {
371      if (this.deck.isScrollView() === true) {
372        this.deck.configure({ scrollActivationWidth: this.oldScrollActivationWidth });
373        this.deck.toggleScrollView(false);
374      } else if (this.deck.isScrollView() === false) {
375        this.deck.configure({ scrollActivationWidth: null });
376        this.deck.toggleScrollView(true);
377      }
378    }
379  }
380
381  let scrollViewToggler;
382
383  function installScollViewKeyBindings(deck) {
384		var config = deck.getConfig();
385		var shortcut = config.scrollViewShortcut || 'R';
386		Reveal.addKeyBinding({
387			keyCode: shortcut.toUpperCase().charCodeAt( 0 ),
388			key: shortcut.toUpperCase(),
389			description: 'Scroll View Mode'
390		}, () => { scrollViewToggler.toggleScrollViewWrapper() } );
391	}
392
393  return {
394    id: "quarto-support",
395    init: function (deck) {
396      scrollViewToggler = new ScrollViewToggler(deck);
397      controlsAuto(deck);
398      previewLinksAuto(deck);
399      fixupForPrint(deck);
400      applyGlobalStyles(deck);
401      addLogoImage(deck);
402      tweakSlideNumber(deck);
403      addFooter(deck);
404      addChalkboardButtons(deck);
405      handleTabbyClicks();
406      handleTabbyChanges();
407      handleSlideChanges(deck);
408      workaroundMermaidDistance(deck);
409      handleWhiteSpaceInColumns(deck);
410      installScollViewKeyBindings(deck);
411      // should stay last
412      cleanEmptyAutoGeneratedContent(deck);
413    },
414    // Export for adding in menu
415    toggleScrollView: function() {
416      scrollViewToggler.toggleScrollViewWrapper();
417    }
418  };
419};

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.