var di = Object.defineProperty; var ci = (c, e, i) => e in c ? di(c, e, { enumerable: !0, configurable: !0, writable: !0, value: i }) : c[e] = i; var Rt = (c, e, i) => ci(c, typeof e != "symbol" ? e + "" : e, i); const ue = (c, e) => { for (let i in e) c[i] = e[i]; return c; }, E = (c, e) => Array.from(c.querySelectorAll(e)), Oe = (c, e, i) => { i ? c.classList.add(e) : c.classList.remove(e); }, he = (c) => { if (typeof c == "string") { if (c === "null") return null; if (c === "true") return !0; if (c === "false") return !1; if (c.match(/^-?[\d\.]+$/)) return parseFloat(c); } return c; }, se = (c, e) => { c.style.transform = e; }, Pe = (c, e) => { let i = c.matches || c.matchesSelector || c.msMatchesSelector; return !!(i && i.call(c, e)); }, V = (c, e) => { if (typeof c.closest == "function") return c.closest(e); for (; c; ) { if (Pe(c, e)) return c; c = c.parentNode; } return null; }, xt = (c) => { c = c || document.documentElement; let e = c.requestFullscreen || c.webkitRequestFullscreen || c.webkitRequestFullScreen || c.mozRequestFullScreen || c.msRequestFullscreen; e && e.apply(c); }, hi = (c, e, i, t = "") => { let s = c.querySelectorAll("." + i); for (let r = 0; r < s.length; r++) { let o = s[r]; if (o.parentNode === c) return o; } let n = document.createElement(e); return n.className = i, n.innerHTML = t, c.appendChild(n), n; }, Ue = (c) => { let e = document.createElement("style"); return e.type = "text/css", c && c.length > 0 && (e.styleSheet ? e.styleSheet.cssText = c : e.appendChild(document.createTextNode(c))), document.head.appendChild(e), e; }, At = () => { let c = {}; location.search.replace(/[A-Z0-9]+?=([\w\.%-]*)/gi, (e) => { c[e.split("=").shift()] = e.split("=").pop(); }); for (let e in c) { let i = c[e]; c[e] = he(unescape(i)); } return typeof c.dependencies < "u" && delete c.dependencies, c; }, ui = (c, e = 0) => { if (c) { let i, t = c.style.height; return c.style.height = "0px", c.parentNode.style.height = "auto", i = e - c.parentNode.offsetHeight, c.style.height = t + "px", c.parentNode.style.removeProperty("height"), i; } return e; }, fi = { mp4: "video/mp4", m4a: "video/mp4", ogv: "video/ogg", mpeg: "video/mpeg", webm: "video/webm" }, gi = (c = "") => fi[c.split(".").pop()], pi = (c = "") => encodeURI(c).replace(/%5B/g, "[").replace(/%5D/g, "]").replace( /[!'()*]/g, (e) => `%${e.charCodeAt(0).toString(16).toUpperCase()}` ), Tt = navigator.userAgent, fe = /(iphone|ipod|ipad|android)/gi.test(Tt) || navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1, It = /android/gi.test(Tt); var vi = function(c) { if (c) { var e = function(f) { return [].slice.call(f); }, i = 0, t = 1, s = 2, n = 3, r = [], o = null, h = "requestAnimationFrame" in c ? function() { var f = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : { sync: !1 }; c.cancelAnimationFrame(o); var S = function() { return g(r.filter(function(x) { return x.dirty && x.active; })); }; if (f.sync) return S(); o = c.requestAnimationFrame(S); } : function() { }, u = function(f) { return function(S) { r.forEach(function(x) { return x.dirty = f; }), h(S); }; }, g = function(f) { f.filter(function(x) { return !x.styleComputed; }).forEach(function(x) { x.styleComputed = l(x); }), f.filter(R).forEach(M); var S = f.filter(b); S.forEach(m), S.forEach(function(x) { M(x), p(x); }), S.forEach(O); }, p = function(f) { return f.dirty = i; }, m = function(f) { f.availableWidth = f.element.parentNode.clientWidth, f.currentWidth = f.element.scrollWidth, f.previousFontSize = f.currentFontSize, f.currentFontSize = Math.min(Math.max(f.minSize, f.availableWidth / f.currentWidth * f.previousFontSize), f.maxSize), f.whiteSpace = f.multiLine && f.currentFontSize === f.minSize ? "normal" : "nowrap"; }, b = function(f) { return f.dirty !== s || f.dirty === s && f.element.parentNode.clientWidth !== f.availableWidth; }, l = function(f) { var S = c.getComputedStyle(f.element, null); return f.currentFontSize = parseFloat(S.getPropertyValue("font-size")), f.display = S.getPropertyValue("display"), f.whiteSpace = S.getPropertyValue("white-space"), !0; }, R = function(f) { var S = !1; return !f.preStyleTestCompleted && (/inline-/.test(f.display) || (S = !0, f.display = "inline-block"), f.whiteSpace !== "nowrap" && (S = !0, f.whiteSpace = "nowrap"), f.preStyleTestCompleted = !0, S); }, M = function(f) { f.element.style.whiteSpace = f.whiteSpace, f.element.style.display = f.display, f.element.style.fontSize = f.currentFontSize + "px"; }, O = function(f) { f.element.dispatchEvent(new CustomEvent("fit", { detail: { oldValue: f.previousFontSize, newValue: f.currentFontSize, scaleFactor: f.currentFontSize / f.previousFontSize } })); }, q = function(f, S) { return function(x) { f.dirty = S, f.active && h(x); }; }, ae = function(f) { return function() { r = r.filter(function(S) { return S.element !== f.element; }), f.observeMutations && f.observer.disconnect(), f.element.style.whiteSpace = f.originalStyle.whiteSpace, f.element.style.display = f.originalStyle.display, f.element.style.fontSize = f.originalStyle.fontSize; }; }, z = function(f) { return function() { f.active || (f.active = !0, h()); }; }, k = function(f) { return function() { return f.active = !1; }; }, B = function(f) { f.observeMutations && (f.observer = new MutationObserver(q(f, t)), f.observer.observe(f.element, f.observeMutations)); }, U = { minSize: 16, maxSize: 512, multiLine: !0, observeMutations: "MutationObserver" in c && { subtree: !0, childList: !0, characterData: !0 } }, W = null, L = function() { c.clearTimeout(W), W = c.setTimeout(u(s), C.observeWindowDelay); }, A = ["resize", "orientationchange"]; return Object.defineProperty(C, "observeWindow", { set: function(f) { var S = "".concat(f ? "add" : "remove", "EventListener"); A.forEach(function(x) { c[S](x, L); }); } }), C.observeWindow = !0, C.observeWindowDelay = 100, C.fitAll = u(n), C; } function F(f, S) { var x = Object.assign({}, U, S), X = f.map(function(j) { var ee = Object.assign({}, x, { element: j, active: !0 }); return function(D) { D.originalStyle = { whiteSpace: D.element.style.whiteSpace, display: D.element.style.display, fontSize: D.element.style.fontSize }, B(D), D.newbie = !0, D.dirty = !0, r.push(D); }(ee), { element: j, fit: q(ee, n), unfreeze: z(ee), freeze: k(ee), unsubscribe: ae(ee) }; }); return h(), X; } function C(f) { var S = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}; return typeof f == "string" ? F(e(document.querySelectorAll(f)), S) : F([f], S)[0]; } }(typeof window > "u" ? null : window); class mi { constructor(e) { this.Reveal = e, this.startEmbeddedIframe = this.startEmbeddedIframe.bind(this); } /** * Should the given element be preloaded? * Decides based on local element attributes and global config. * * @param {HTMLElement} element */ shouldPreload(e) { if (this.Reveal.isScrollView()) return !0; let i = this.Reveal.getConfig().preloadIframes; return typeof i != "boolean" && (i = e.hasAttribute("data-preload")), i; } /** * Called when the given slide is within the configured view * distance. Shows the slide element and loads any content * that is set to load lazily (data-src). * * @param {HTMLElement} slide Slide to show */ load(e, i = {}) { e.style.display = this.Reveal.getConfig().display, E(e, "img[data-src], video[data-src], audio[data-src], iframe[data-src]").forEach((s) => { (s.tagName !== "IFRAME" || this.shouldPreload(s)) && (s.setAttribute("src", s.getAttribute("data-src")), s.setAttribute("data-lazy-loaded", ""), s.removeAttribute("data-src")); }), E(e, "video, audio").forEach((s) => { let n = 0; E(s, "source[data-src]").forEach((r) => { r.setAttribute("src", r.getAttribute("data-src")), r.removeAttribute("data-src"), r.setAttribute("data-lazy-loaded", ""), n += 1; }), fe && s.tagName === "VIDEO" && s.setAttribute("playsinline", ""), n > 0 && s.load(); }); let t = e.slideBackgroundElement; if (t) { t.style.display = "block"; let s = e.slideBackgroundContentElement, n = e.getAttribute("data-background-iframe"); if (t.hasAttribute("data-loaded") === !1) { t.setAttribute("data-loaded", "true"); let o = e.getAttribute("data-background-image"), h = e.getAttribute("data-background-video"), u = e.hasAttribute("data-background-video-loop"), g = e.hasAttribute("data-background-video-muted"); if (o) /^data:/.test(o.trim()) ? s.style.backgroundImage = `url(${o.trim()})` : s.style.backgroundImage = o.split(",").map((p) => { let m = decodeURI(p.trim()); return `url(${pi(m)})`; }).join(","); else if (h) { let p = document.createElement("video"); u && p.setAttribute("loop", ""), (g || this.Reveal.isSpeakerNotes()) && (p.muted = !0), fe && (p.muted = !0, p.setAttribute("playsinline", "")), h.split(",").forEach((m) => { const b = document.createElement("source"); b.setAttribute("src", m); let l = gi(m); l && b.setAttribute("type", l), p.appendChild(b); }), s.appendChild(p); } else if (n && i.excludeIframes !== !0) { let p = document.createElement("iframe"); p.setAttribute("allowfullscreen", ""), p.setAttribute("mozallowfullscreen", ""), p.setAttribute("webkitallowfullscreen", ""), p.setAttribute("allow", "autoplay"), p.setAttribute("data-src", n), p.style.width = "100%", p.style.height = "100%", p.style.maxHeight = "100%", p.style.maxWidth = "100%", s.appendChild(p); } } let r = s.querySelector("iframe[data-src]"); r && this.shouldPreload(t) && !/autoplay=(1|true|yes)/gi.test(n) && r.getAttribute("src") !== n && r.setAttribute("src", n); } this.layout(e); } /** * Applies JS-dependent layout helpers for the scope. */ layout(e) { Array.from(e.querySelectorAll(".r-fit-text")).forEach((i) => { vi(i, { minSize: 24, maxSize: this.Reveal.getConfig().height * 0.8, observeMutations: !1, observeWindow: !1 }); }); } /** * Unloads and hides the given slide. This is called when the * slide is moved outside of the configured view distance. * * @param {HTMLElement} slide */ unload(e) { e.style.display = "none"; let i = this.Reveal.getSlideBackground(e); i && (i.style.display = "none", E(i, "iframe[src]").forEach((t) => { t.removeAttribute("src"); })), E(e, "video[data-lazy-loaded][src], audio[data-lazy-loaded][src], iframe[data-lazy-loaded][src]").forEach((t) => { t.setAttribute("data-src", t.getAttribute("src")), t.removeAttribute("src"); }), E(e, "video[data-lazy-loaded] source[src], audio source[src]").forEach((t) => { t.setAttribute("data-src", t.getAttribute("src")), t.removeAttribute("src"); }); } /** * Enforces origin-specific format rules for embedded media. */ formatEmbeddedContent() { let e = (i, t, s) => { E(this.Reveal.getSlidesElement(), "iframe[" + i + '*="' + t + '"]').forEach((n) => { let r = n.getAttribute(i); r && r.indexOf(s) === -1 && n.setAttribute(i, r + (/\?/.test(r) ? "&" : "?") + s); }); }; e("src", "youtube.com/embed/", "enablejsapi=1"), e("data-src", "youtube.com/embed/", "enablejsapi=1"), e("src", "player.vimeo.com/", "api=1"), e("data-src", "player.vimeo.com/", "api=1"); } /** * Start playback of any embedded content inside of * the given element. * * @param {HTMLElement} element */ startEmbeddedContent(e) { if (e) { const i = this.Reveal.isSpeakerNotes(); E(e, 'img[src$=".gif"]').forEach((t) => { t.setAttribute("src", t.getAttribute("src")); }), E(e, "video, audio").forEach((t) => { if (V(t, ".fragment") && !V(t, ".fragment.visible")) return; let s = this.Reveal.getConfig().autoPlayMedia; if (typeof s != "boolean" && (s = t.hasAttribute("data-autoplay") || !!V(t, ".slide-background")), s && typeof t.play == "function") { if (i && !t.muted) return; if (t.readyState > 1) this.startEmbeddedMedia({ target: t }); else if (fe) { let n = t.play(); n && typeof n.catch == "function" && t.controls === !1 && n.catch(() => { t.controls = !0, t.addEventListener("play", () => { t.controls = !1; }); }); } else t.removeEventListener("loadeddata", this.startEmbeddedMedia), t.addEventListener("loadeddata", this.startEmbeddedMedia); } }), i || (E(e, "iframe[src]").forEach((t) => { V(t, ".fragment") && !V(t, ".fragment.visible") || this.startEmbeddedIframe({ target: t }); }), E(e, "iframe[data-src]").forEach((t) => { V(t, ".fragment") && !V(t, ".fragment.visible") || t.getAttribute("src") !== t.getAttribute("data-src") && (t.removeEventListener("load", this.startEmbeddedIframe), t.addEventListener("load", this.startEmbeddedIframe), t.setAttribute("src", t.getAttribute("data-src"))); })); } } /** * Starts playing an embedded video/audio element after * it has finished loading. * * @param {object} event */ startEmbeddedMedia(e) { let i = !!V(e.target, "html"), t = !!V(e.target, ".present"); i && t && (e.target.paused || e.target.ended) && (e.target.currentTime = 0, e.target.play()), e.target.removeEventListener("loadeddata", this.startEmbeddedMedia); } /** * "Starts" the content of an embedded iframe using the * postMessage API. * * @param {object} event */ startEmbeddedIframe(e) { let i = e.target; if (i && i.contentWindow) { let t = !!V(e.target, "html"), s = !!V(e.target, ".present"); if (t && s) { let n = this.Reveal.getConfig().autoPlayMedia; typeof n != "boolean" && (n = i.hasAttribute("data-autoplay") || !!V(i, ".slide-background")), /youtube\.com\/embed\//.test(i.getAttribute("src")) && n ? i.contentWindow.postMessage('{"event":"command","func":"playVideo","args":""}', "*") : /player\.vimeo\.com\//.test(i.getAttribute("src")) && n ? i.contentWindow.postMessage('{"method":"play"}', "*") : i.contentWindow.postMessage("slide:start", "*"); } } } /** * Stop playback of any embedded content inside of * the targeted slide. * * @param {HTMLElement} element */ stopEmbeddedContent(e, i = {}) { i = ue({ // Defaults unloadIframes: !0 }, i), e && e.parentNode && (E(e, "video, audio").forEach((t) => { !t.hasAttribute("data-ignore") && typeof t.pause == "function" && (t.setAttribute("data-paused-by-reveal", ""), t.pause()); }), E(e, "iframe").forEach((t) => { t.contentWindow && t.contentWindow.postMessage("slide:stop", "*"), t.removeEventListener("load", this.startEmbeddedIframe); }), E(e, 'iframe[src*="youtube.com/embed/"]').forEach((t) => { !t.hasAttribute("data-ignore") && t.contentWindow && typeof t.contentWindow.postMessage == "function" && t.contentWindow.postMessage('{"event":"command","func":"pauseVideo","args":""}', "*"); }), E(e, 'iframe[src*="player.vimeo.com/"]').forEach((t) => { !t.hasAttribute("data-ignore") && t.contentWindow && typeof t.contentWindow.postMessage == "function" && t.contentWindow.postMessage('{"method":"pause"}', "*"); }), i.unloadIframes === !0 && E(e, "iframe[data-src]").forEach((t) => { t.setAttribute("src", "about:blank"), t.removeAttribute("src"); })); } } const oe = ".slides section", ie = ".slides>section", kt = ".slides>section.present>section", yi = ".backgrounds>.slide-background", bi = /registerPlugin|registerKeyboardShortcut|addKeyBinding|addEventListener|showPreview/, wi = "h.v", Ei = "h/v", We = "c", Mt = "c/t"; class Si { constructor(e) { this.Reveal = e; } render() { this.element = document.createElement("div"), this.element.className = "slide-number", this.Reveal.getRevealElement().appendChild(this.element); } /** * Called when the reveal.js config is updated. */ configure(e, i) { let t = "none"; e.slideNumber && !this.Reveal.isPrintView() && (e.showSlideNumber === "all" || e.showSlideNumber === "speaker" && this.Reveal.isSpeakerNotes()) && (t = "block"), this.element.style.display = t; } /** * Updates the slide number to match the current slide. */ update() { this.Reveal.getConfig().slideNumber && this.element && (this.element.innerHTML = this.getSlideNumber()); } /** * Returns the HTML string corresponding to the current slide * number, including formatting. */ getSlideNumber(e = this.Reveal.getCurrentSlide()) { let i = this.Reveal.getConfig(), t, s = wi; if (typeof i.slideNumber == "function") t = i.slideNumber(e); else { typeof i.slideNumber == "string" && (s = i.slideNumber), !/c/.test(s) && this.Reveal.getHorizontalSlides().length === 1 && (s = We); let r = e && e.dataset.visibility === "uncounted" ? 0 : 1; switch (t = [], s) { case We: t.push(this.Reveal.getSlidePastCount(e) + r); break; case Mt: t.push(this.Reveal.getSlidePastCount(e) + r, "/", this.Reveal.getTotalSlides()); break; default: let o = this.Reveal.getIndices(e); t.push(o.h + r); let h = s === Ei ? "/" : "."; this.Reveal.isVerticalSlide(e) && t.push(h, o.v + 1); } } let n = "#" + this.Reveal.location.getHash(e); return this.formatNumber(t[0], t[1], t[2], n); } /** * Applies HTML formatting to a slide number before it's * written to the DOM. * * @param {number} a Current slide * @param {string} delimiter Character to separate slide numbers * @param {(number|*)} b Total slides * @param {HTMLElement} [url='#'+locationHash()] The url to link to * @return {string} HTML string fragment */ formatNumber(e, i, t, s = "#" + this.Reveal.location.getHash()) { return typeof t == "number" && !isNaN(t) ? ` ${e} ${i} ${t} ` : ` ${e} `; } destroy() { this.element.remove(); } } class Ri { constructor(e) { this.Reveal = e, this.onInput = this.onInput.bind(this), this.onBlur = this.onBlur.bind(this), this.onKeyDown = this.onKeyDown.bind(this); } render() { this.element = document.createElement("div"), this.element.className = "jump-to-slide", this.jumpInput = document.createElement("input"), this.jumpInput.type = "text", this.jumpInput.className = "jump-to-slide-input", this.jumpInput.placeholder = "Jump to slide", this.jumpInput.addEventListener("input", this.onInput), this.jumpInput.addEventListener("keydown", this.onKeyDown), this.jumpInput.addEventListener("blur", this.onBlur), this.element.appendChild(this.jumpInput); } show() { this.indicesOnShow = this.Reveal.getIndices(), this.Reveal.getRevealElement().appendChild(this.element), this.jumpInput.focus(); } hide() { this.isVisible() && (this.element.remove(), this.jumpInput.value = "", clearTimeout(this.jumpTimeout), delete this.jumpTimeout); } isVisible() { return !!this.element.parentNode; } /** * Parses the current input and jumps to the given slide. */ jump() { clearTimeout(this.jumpTimeout), delete this.jumpTimeout; let e = this.jumpInput.value.trim(""), i; if (/^\d+$/.test(e)) { const t = this.Reveal.getConfig().slideNumber; if (t === We || t === Mt) { const s = this.Reveal.getSlides()[parseInt(e, 10) - 1]; s && (i = this.Reveal.getIndices(s)); } } return i || (/^\d+\.\d+$/.test(e) && (e = e.replace(".", "/")), i = this.Reveal.location.getIndicesFromHash(e, { oneBasedIndex: !0 })), !i && /\S+/i.test(e) && e.length > 1 && (i = this.search(e)), i && e !== "" ? (this.Reveal.slide(i.h, i.v, i.f), !0) : (this.Reveal.slide(this.indicesOnShow.h, this.indicesOnShow.v, this.indicesOnShow.f), !1); } jumpAfter(e) { clearTimeout(this.jumpTimeout), this.jumpTimeout = setTimeout(() => this.jump(), e); } /** * A lofi search that looks for the given query in all * of our slides and returns the first match. */ search(e) { const i = new RegExp("\\b" + e.trim() + "\\b", "i"), t = this.Reveal.getSlides().find((s) => i.test(s.innerText)); return t ? this.Reveal.getIndices(t) : null; } /** * Reverts back to the slide we were on when jump to slide was * invoked. */ cancel() { this.Reveal.slide(this.indicesOnShow.h, this.indicesOnShow.v, this.indicesOnShow.f), this.hide(); } confirm() { this.jump(), this.hide(); } destroy() { this.jumpInput.removeEventListener("input", this.onInput), this.jumpInput.removeEventListener("keydown", this.onKeyDown), this.jumpInput.removeEventListener("blur", this.onBlur), this.element.remove(); } onKeyDown(e) { e.keyCode === 13 ? this.confirm() : e.keyCode === 27 && (this.cancel(), e.stopImmediatePropagation()); } onInput(e) { this.jumpAfter(200); } onBlur() { setTimeout(() => this.hide(), 1); } } const je = (c) => { let e = c.match(/^#([0-9a-f]{3})$/i); if (e && e[1]) return e = e[1], { r: parseInt(e.charAt(0), 16) * 17, g: parseInt(e.charAt(1), 16) * 17, b: parseInt(e.charAt(2), 16) * 17 }; let i = c.match(/^#([0-9a-f]{6})$/i); if (i && i[1]) return i = i[1], { r: parseInt(i.slice(0, 2), 16), g: parseInt(i.slice(2, 4), 16), b: parseInt(i.slice(4, 6), 16) }; let t = c.match(/^rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i); if (t) return { r: parseInt(t[1], 10), g: parseInt(t[2], 10), b: parseInt(t[3], 10) }; let s = c.match(/^rgba\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*([\d]+|[\d]*.[\d]+)\s*\)$/i); return s ? { r: parseInt(s[1], 10), g: parseInt(s[2], 10), b: parseInt(s[3], 10), a: parseFloat(s[4]) } : null; }, Ai = (c) => (typeof c == "string" && (c = je(c)), c ? (c.r * 299 + c.g * 587 + c.b * 114) / 1e3 : null); class ki { constructor(e) { this.Reveal = e; } render() { this.element = document.createElement("div"), this.element.className = "backgrounds", this.Reveal.getRevealElement().appendChild(this.element); } /** * Creates the slide background elements and appends them * to the background container. One element is created per * slide no matter if the given slide has visible background. */ create() { this.element.innerHTML = "", this.element.classList.add("no-transition"), this.Reveal.getHorizontalSlides().forEach((e) => { let i = this.createBackground(e, this.element); E(e, "section").forEach((t) => { this.createBackground(t, i), i.classList.add("stack"); }); }), this.Reveal.getConfig().parallaxBackgroundImage ? (this.element.style.backgroundImage = 'url("' + this.Reveal.getConfig().parallaxBackgroundImage + '")', this.element.style.backgroundSize = this.Reveal.getConfig().parallaxBackgroundSize, this.element.style.backgroundRepeat = this.Reveal.getConfig().parallaxBackgroundRepeat, this.element.style.backgroundPosition = this.Reveal.getConfig().parallaxBackgroundPosition, setTimeout(() => { this.Reveal.getRevealElement().classList.add("has-parallax-background"); }, 1)) : (this.element.style.backgroundImage = "", this.Reveal.getRevealElement().classList.remove("has-parallax-background")); } /** * Creates a background for the given slide. * * @param {HTMLElement} slide * @param {HTMLElement} container The element that the background * should be appended to * @return {HTMLElement} New background div */ createBackground(e, i) { let t = document.createElement("div"); t.className = "slide-background " + e.className.replace(/present|past|future/, ""); let s = document.createElement("div"); return s.className = "slide-background-content", t.appendChild(s), i.appendChild(t), e.slideBackgroundElement = t, e.slideBackgroundContentElement = s, this.sync(e), t; } /** * Renders all of the visual properties of a slide background * based on the various background attributes. * * @param {HTMLElement} slide */ sync(e) { const i = e.slideBackgroundElement, t = e.slideBackgroundContentElement, s = { background: e.getAttribute("data-background"), backgroundSize: e.getAttribute("data-background-size"), backgroundImage: e.getAttribute("data-background-image"), backgroundVideo: e.getAttribute("data-background-video"), backgroundIframe: e.getAttribute("data-background-iframe"), backgroundColor: e.getAttribute("data-background-color"), backgroundGradient: e.getAttribute("data-background-gradient"), backgroundRepeat: e.getAttribute("data-background-repeat"), backgroundPosition: e.getAttribute("data-background-position"), backgroundTransition: e.getAttribute("data-background-transition"), backgroundOpacity: e.getAttribute("data-background-opacity") }, n = e.hasAttribute("data-preload"); e.classList.remove("has-dark-background"), e.classList.remove("has-light-background"), i.removeAttribute("data-loaded"), i.removeAttribute("data-background-hash"), i.removeAttribute("data-background-size"), i.removeAttribute("data-background-transition"), i.style.backgroundColor = "", t.style.backgroundSize = "", t.style.backgroundRepeat = "", t.style.backgroundPosition = "", t.style.backgroundImage = "", t.style.opacity = "", t.innerHTML = "", s.background && (/^(http|file|\/\/)/gi.test(s.background) || /\.(svg|png|jpg|jpeg|gif|bmp|webp)([?#\s]|$)/gi.test(s.background) ? e.setAttribute("data-background-image", s.background) : i.style.background = s.background), (s.background || s.backgroundColor || s.backgroundGradient || s.backgroundImage || s.backgroundVideo || s.backgroundIframe) && i.setAttribute("data-background-hash", s.background + s.backgroundSize + s.backgroundImage + s.backgroundVideo + s.backgroundIframe + s.backgroundColor + s.backgroundGradient + s.backgroundRepeat + s.backgroundPosition + s.backgroundTransition + s.backgroundOpacity), s.backgroundSize && i.setAttribute("data-background-size", s.backgroundSize), s.backgroundColor && (i.style.backgroundColor = s.backgroundColor), s.backgroundGradient && (i.style.backgroundImage = s.backgroundGradient), s.backgroundTransition && i.setAttribute("data-background-transition", s.backgroundTransition), n && i.setAttribute("data-preload", ""), s.backgroundSize && (t.style.backgroundSize = s.backgroundSize), s.backgroundRepeat && (t.style.backgroundRepeat = s.backgroundRepeat), s.backgroundPosition && (t.style.backgroundPosition = s.backgroundPosition), s.backgroundOpacity && (t.style.opacity = s.backgroundOpacity); const r = this.getContrastClass(e); typeof r == "string" && e.classList.add(r); } /** * Returns a class name that can be applied to a slide to indicate * if it has a light or dark background. * * @param {*} slide * * @returns {string|null} */ getContrastClass(e) { const i = e.slideBackgroundElement; let t = e.getAttribute("data-background-color"); if (!t || !je(t)) { let s = window.getComputedStyle(i); s && s.backgroundColor && (t = s.backgroundColor); } if (t) { const s = je(t); if (s && s.a !== 0) return Ai(t) < 128 ? "has-dark-background" : "has-light-background"; } return null; } /** * Bubble the 'has-light-background'/'has-dark-background' classes. */ bubbleSlideContrastClassToElement(e, i) { ["has-light-background", "has-dark-background"].forEach((t) => { e.classList.contains(t) ? i.classList.add(t) : i.classList.remove(t); }, this); } /** * Updates the background elements to reflect the current * slide. * * @param {boolean} includeAll If true, the backgrounds of * all vertical slides (not just the present) will be updated. */ update(e = !1) { let i = this.Reveal.getConfig(), t = this.Reveal.getCurrentSlide(), s = this.Reveal.getIndices(), n = null, r = i.rtl ? "future" : "past", o = i.rtl ? "past" : "future"; if (Array.from(this.element.childNodes).forEach((u, g) => { u.classList.remove("past", "present", "future"), g < s.h ? u.classList.add(r) : g > s.h ? u.classList.add(o) : (u.classList.add("present"), n = u), (e || g === s.h) && E(u, ".slide-background").forEach((p, m) => { p.classList.remove("past", "present", "future"); const b = typeof s.v == "number" ? s.v : 0; m < b ? p.classList.add("past") : m > b ? p.classList.add("future") : (p.classList.add("present"), g === s.h && (n = p)); }); }), this.previousBackground && !this.previousBackground.closest("body") && (this.previousBackground = null), n && this.previousBackground) { let u = this.previousBackground.getAttribute("data-background-hash"), g = n.getAttribute("data-background-hash"); if (g && g === u && n !== this.previousBackground) { this.element.classList.add("no-transition"); const p = n.querySelector("video"), m = this.previousBackground.querySelector("video"); if (p && m) { const b = p.parentNode; m.parentNode.appendChild(p), b.appendChild(m); } } } const h = n !== this.previousBackground; if (h && this.previousBackground && this.Reveal.slideContent.stopEmbeddedContent(this.previousBackground, { unloadIframes: !this.Reveal.slideContent.shouldPreload(this.previousBackground) }), h && n) { this.Reveal.slideContent.startEmbeddedContent(n); let u = n.querySelector(".slide-background-content"); if (u) { let g = u.style.backgroundImage || ""; /\.gif/i.test(g) && (u.style.backgroundImage = "", window.getComputedStyle(u).opacity, u.style.backgroundImage = g); } this.previousBackground = n; } t && this.bubbleSlideContrastClassToElement(t, this.Reveal.getRevealElement()), setTimeout(() => { this.element.classList.remove("no-transition"); }, 10); } /** * Updates the position of the parallax background based * on the current slide index. */ updateParallax() { let e = this.Reveal.getIndices(); if (this.Reveal.getConfig().parallaxBackgroundImage) { let i = this.Reveal.getHorizontalSlides(), t = this.Reveal.getVerticalSlides(), s = this.element.style.backgroundSize.split(" "), n, r; s.length === 1 ? n = r = parseInt(s[0], 10) : (n = parseInt(s[0], 10), r = parseInt(s[1], 10)); let o = this.element.offsetWidth, h = i.length, u, g; typeof this.Reveal.getConfig().parallaxBackgroundHorizontal == "number" ? u = this.Reveal.getConfig().parallaxBackgroundHorizontal : u = h > 1 ? (n - o) / (h - 1) : 0, g = u * e.h * -1; let p = this.element.offsetHeight, m = t.length, b, l; typeof this.Reveal.getConfig().parallaxBackgroundVertical == "number" ? b = this.Reveal.getConfig().parallaxBackgroundVertical : b = (r - p) / (m - 1), l = m > 0 ? b * e.v : 0, this.element.style.backgroundPosition = g + "px " + -l + "px"; } } destroy() { this.element.remove(); } } let Ct = 0; class Ci { constructor(e) { this.Reveal = e; } /** * Runs an auto-animation between the given slides. * * @param {HTMLElement} fromSlide * @param {HTMLElement} toSlide */ run(e, i) { this.reset(); let t = this.Reveal.getSlides(), s = t.indexOf(i), n = t.indexOf(e); if (e && i && e.hasAttribute("data-auto-animate") && i.hasAttribute("data-auto-animate") && e.getAttribute("data-auto-animate-id") === i.getAttribute("data-auto-animate-id") && !(s > n ? i : e).hasAttribute("data-auto-animate-restart")) { this.autoAnimateStyleSheet = this.autoAnimateStyleSheet || Ue(); let r = this.getAutoAnimateOptions(i); e.dataset.autoAnimate = "pending", i.dataset.autoAnimate = "pending", r.slideDirection = s > n ? "forward" : "backward"; let o = e.style.display === "none"; o && (e.style.display = this.Reveal.getConfig().display); let h = this.getAutoAnimatableElements(e, i).map((u) => this.autoAnimateElements(u.from, u.to, u.options || {}, r, Ct++)); if (o && (e.style.display = "none"), i.dataset.autoAnimateUnmatched !== "false" && this.Reveal.getConfig().autoAnimateUnmatched === !0) { let u = r.duration * 0.8, g = r.duration * 0.2; this.getUnmatchedAutoAnimateElements(i).forEach((p) => { let m = this.getAutoAnimateOptions(p, r), b = "unmatched"; (m.duration !== r.duration || m.delay !== r.delay) && (b = "unmatched-" + Ct++, h.push(`[data-auto-animate="running"] [data-auto-animate-target="${b}"] { transition: opacity ${m.duration}s ease ${m.delay}s; }`)), p.dataset.autoAnimateTarget = b; }, this), h.push(`[data-auto-animate="running"] [data-auto-animate-target="unmatched"] { transition: opacity ${u}s ease ${g}s; }`); } this.autoAnimateStyleSheet.innerHTML = h.join(""), requestAnimationFrame(() => { this.autoAnimateStyleSheet && (getComputedStyle(this.autoAnimateStyleSheet).fontWeight, i.dataset.autoAnimate = "running"); }), this.Reveal.dispatchEvent({ type: "autoanimate", data: { fromSlide: e, toSlide: i, sheet: this.autoAnimateStyleSheet } }); } } /** * Rolls back all changes that we've made to the DOM so * that as part of animating. */ reset() { E(this.Reveal.getRevealElement(), '[data-auto-animate]:not([data-auto-animate=""])').forEach((e) => { e.dataset.autoAnimate = ""; }), E(this.Reveal.getRevealElement(), "[data-auto-animate-target]").forEach((e) => { delete e.dataset.autoAnimateTarget; }), this.autoAnimateStyleSheet && this.autoAnimateStyleSheet.parentNode && (this.autoAnimateStyleSheet.parentNode.removeChild(this.autoAnimateStyleSheet), this.autoAnimateStyleSheet = null); } /** * Creates a FLIP animation where the `to` element starts out * in the `from` element position and animates to its original * state. * * @param {HTMLElement} from * @param {HTMLElement} to * @param {Object} elementOptions Options for this element pair * @param {Object} animationOptions Options set at the slide level * @param {String} id Unique ID that we can use to identify this * auto-animate element in the DOM */ autoAnimateElements(e, i, t, s, n) { e.dataset.autoAnimateTarget = "", i.dataset.autoAnimateTarget = n; let r = this.getAutoAnimateOptions(i, s); typeof t.delay < "u" && (r.delay = t.delay), typeof t.duration < "u" && (r.duration = t.duration), typeof t.easing < "u" && (r.easing = t.easing); let o = this.getAutoAnimatableProperties("from", e, t), h = this.getAutoAnimatableProperties("to", i, t); if (i.classList.contains("fragment") && delete h.styles.opacity, t.translate !== !1 || t.scale !== !1) { let p = this.Reveal.getScale(), m = { x: (o.x - h.x) / p, y: (o.y - h.y) / p, scaleX: o.width / h.width, scaleY: o.height / h.height }; m.x = Math.round(m.x * 1e3) / 1e3, m.y = Math.round(m.y * 1e3) / 1e3, m.scaleX = Math.round(m.scaleX * 1e3) / 1e3, m.scaleX = Math.round(m.scaleX * 1e3) / 1e3; let b = t.translate !== !1 && (m.x !== 0 || m.y !== 0), l = t.scale !== !1 && (m.scaleX !== 0 || m.scaleY !== 0); if (b || l) { let R = []; b && R.push(`translate(${m.x}px, ${m.y}px)`), l && R.push(`scale(${m.scaleX}, ${m.scaleY})`), o.styles.transform = R.join(" "), o.styles["transform-origin"] = "top left", h.styles.transform = "none"; } } for (let p in h.styles) { const m = h.styles[p], b = o.styles[p]; m === b ? delete h.styles[p] : (m.explicitValue === !0 && (h.styles[p] = m.value), b.explicitValue === !0 && (o.styles[p] = b.value)); } let u = "", g = Object.keys(h.styles); if (g.length > 0) { o.styles.transition = "none", h.styles.transition = `all ${r.duration}s ${r.easing} ${r.delay}s`, h.styles["transition-property"] = g.join(", "), h.styles["will-change"] = g.join(", "); let p = Object.keys(o.styles).map((b) => b + ": " + o.styles[b] + " !important;").join(""), m = Object.keys(h.styles).map((b) => b + ": " + h.styles[b] + " !important;").join(""); u = '[data-auto-animate-target="' + n + '"] {' + p + '}[data-auto-animate="running"] [data-auto-animate-target="' + n + '"] {' + m + "}"; } return u; } /** * Returns the auto-animate options for the given element. * * @param {HTMLElement} element Element to pick up options * from, either a slide or an animation target * @param {Object} [inheritedOptions] Optional set of existing * options */ getAutoAnimateOptions(e, i) { let t = { easing: this.Reveal.getConfig().autoAnimateEasing, duration: this.Reveal.getConfig().autoAnimateDuration, delay: 0 }; if (t = ue(t, i), e.parentNode) { let s = V(e.parentNode, "[data-auto-animate-target]"); s && (t = this.getAutoAnimateOptions(s, t)); } return e.dataset.autoAnimateEasing && (t.easing = e.dataset.autoAnimateEasing), e.dataset.autoAnimateDuration && (t.duration = parseFloat(e.dataset.autoAnimateDuration)), e.dataset.autoAnimateDelay && (t.delay = parseFloat(e.dataset.autoAnimateDelay)), t; } /** * Returns an object containing all of the properties * that can be auto-animated for the given element and * their current computed values. * * @param {String} direction 'from' or 'to' */ getAutoAnimatableProperties(e, i, t) { let s = this.Reveal.getConfig(), n = { styles: [] }; if (t.translate !== !1 || t.scale !== !1) { let o; if (typeof t.measure == "function") o = t.measure(i); else if (s.center) o = i.getBoundingClientRect(); else { let h = this.Reveal.getScale(); o = { x: i.offsetLeft * h, y: i.offsetTop * h, width: i.offsetWidth * h, height: i.offsetHeight * h }; } n.x = o.x, n.y = o.y, n.width = o.width, n.height = o.height; } const r = getComputedStyle(i); return (t.styles || s.autoAnimateStyles).forEach((o) => { let h; typeof o == "string" && (o = { property: o }), typeof o.from < "u" && e === "from" ? h = { value: o.from, explicitValue: !0 } : typeof o.to < "u" && e === "to" ? h = { value: o.to, explicitValue: !0 } : (o.property === "line-height" && (h = parseFloat(r["line-height"]) / parseFloat(r["font-size"])), isNaN(h) && (h = r[o.property])), h !== "" && (n.styles[o.property] = h); }), n; } /** * Get a list of all element pairs that we can animate * between the given slides. * * @param {HTMLElement} fromSlide * @param {HTMLElement} toSlide * * @return {Array} Each value is an array where [0] is * the element we're animating from and [1] is the * element we're animating to */ getAutoAnimatableElements(e, i) { let s = (typeof this.Reveal.getConfig().autoAnimateMatcher == "function" ? this.Reveal.getConfig().autoAnimateMatcher : this.getAutoAnimatePairs).call(this, e, i), n = []; return s.filter((r, o) => { if (n.indexOf(r.to) === -1) return n.push(r.to), !0; }); } /** * Identifies matching elements between slides. * * You can specify a custom matcher function by using * the `autoAnimateMatcher` config option. */ getAutoAnimatePairs(e, i) { let t = []; const s = "pre", n = "h1, h2, h3, h4, h5, h6, p, li", r = "img, video, iframe"; return this.findAutoAnimateMatches(t, e, i, "[data-id]", (o) => o.nodeName + ":::" + o.getAttribute("data-id")), this.findAutoAnimateMatches(t, e, i, n, (o) => o.nodeName + ":::" + o.textContent.trim()), this.findAutoAnimateMatches(t, e, i, r, (o) => o.nodeName + ":::" + (o.getAttribute("src") || o.getAttribute("data-src"))), this.findAutoAnimateMatches(t, e, i, s, (o) => o.nodeName + ":::" + o.textContent.trim()), t.forEach((o) => { Pe(o.from, n) ? o.options = { scale: !1 } : Pe(o.from, s) && (o.options = { scale: !1, styles: ["width", "height"] }, this.findAutoAnimateMatches(t, o.from, o.to, ".hljs .hljs-ln-code", (h) => h.textContent, { scale: !1, styles: [], measure: this.getLocalBoundingBox.bind(this) }), this.findAutoAnimateMatches(t, o.from, o.to, ".hljs .hljs-ln-numbers[data-line-number]", (h) => h.getAttribute("data-line-number"), { scale: !1, styles: ["width"], measure: this.getLocalBoundingBox.bind(this) })); }, this), t; } /** * Helper method which returns a bounding box based on * the given elements offset coordinates. * * @param {HTMLElement} element * @return {Object} x, y, width, height */ getLocalBoundingBox(e) { const i = this.Reveal.getScale(); return { x: Math.round(e.offsetLeft * i * 100) / 100, y: Math.round(e.offsetTop * i * 100) / 100, width: Math.round(e.offsetWidth * i * 100) / 100, height: Math.round(e.offsetHeight * i * 100) / 100 }; } /** * Finds matching elements between two slides. * * @param {Array} pairs List of pairs to push matches to * @param {HTMLElement} fromScope Scope within the from element exists * @param {HTMLElement} toScope Scope within the to element exists * @param {String} selector CSS selector of the element to match * @param {Function} serializer A function that accepts an element and returns * a stringified ID based on its contents * @param {Object} animationOptions Optional config options for this pair */ findAutoAnimateMatches(e, i, t, s, n, r) { let o = {}, h = {}; [].slice.call(i.querySelectorAll(s)).forEach((u, g) => { const p = n(u); typeof p == "string" && p.length && (o[p] = o[p] || [], o[p].push(u)); }), [].slice.call(t.querySelectorAll(s)).forEach((u, g) => { const p = n(u); h[p] = h[p] || [], h[p].push(u); let m; if (o[p]) { const b = h[p].length - 1, l = o[p].length - 1; o[p][b] ? (m = o[p][b], o[p][b] = null) : o[p][l] && (m = o[p][l], o[p][l] = null); } m && e.push({ from: m, to: u, options: r }); }); } /** * Returns a all elements within the given scope that should * be considered unmatched in an auto-animate transition. If * fading of unmatched elements is turned on, these elements * will fade when going between auto-animate slides. * * Note that parents of auto-animate targets are NOT considered * unmatched since fading them would break the auto-animation. * * @param {HTMLElement} rootElement * @return {Array} */ getUnmatchedAutoAnimateElements(e) { return [].slice.call(e.children).reduce((i, t) => { const s = t.querySelector("[data-auto-animate-target]"); return !t.hasAttribute("data-auto-animate-target") && !s && i.push(t), t.querySelector("[data-auto-animate-target]") && (i = i.concat(this.getUnmatchedAutoAnimateElements(t))), i; }, []); } } const Li = 500, Pi = 4, xi = 6, Ti = 8; class Ii { constructor(e) { this.Reveal = e, this.active = !1, this.activatedCallbacks = [], this.onScroll = this.onScroll.bind(this); } /** * Activates the scroll view. This rearranges the presentation DOM * by—among other things—wrapping each slide in a page element. */ activate() { if (this.active) return; const e = this.Reveal.getState(); this.active = !0, this.slideHTMLBeforeActivation = this.Reveal.getSlidesElement().innerHTML; const i = E(this.Reveal.getRevealElement(), ie), t = E(this.Reveal.getRevealElement(), yi); this.viewportElement.classList.add("loading-scroll-mode", "reveal-scroll"); let s; const n = window.getComputedStyle(this.viewportElement); n && n.background && (s = n.background); const r = [], o = i[0].parentNode; let h; const u = (g, p, m, b) => { let l; if (h && this.Reveal.shouldAutoAnimateBetween(h, g)) l = document.createElement("div"), l.className = "scroll-page-content scroll-auto-animate-page", l.style.display = "none", h.closest(".scroll-page-content").parentNode.appendChild(l); else { const R = document.createElement("div"); if (R.className = "scroll-page", r.push(R), b && t.length > p) { const O = t[p], q = window.getComputedStyle(O); q && q.background ? R.style.background = q.background : s && (R.style.background = s); } else s && (R.style.background = s); const M = document.createElement("div"); M.className = "scroll-page-sticky", R.appendChild(M), l = document.createElement("div"), l.className = "scroll-page-content", M.appendChild(l); } l.appendChild(g), g.classList.remove("past", "future"), g.setAttribute("data-index-h", p), g.setAttribute("data-index-v", m), g.slideBackgroundElement && (g.slideBackgroundElement.remove("past", "future"), l.insertBefore(g.slideBackgroundElement, g)), h = g; }; i.forEach((g, p) => { this.Reveal.isVerticalStack(g) ? g.querySelectorAll("section").forEach((m, b) => { u(m, p, b, !0); }) : u(g, p, 0); }, this), this.createProgressBar(), E(this.Reveal.getRevealElement(), ".stack").forEach((g) => g.remove()), r.forEach((g) => o.appendChild(g)), this.Reveal.slideContent.layout(this.Reveal.getSlidesElement()), this.Reveal.layout(), this.Reveal.setState(e), this.activatedCallbacks.forEach((g) => g()), this.activatedCallbacks = [], this.restoreScrollPosition(), this.viewportElement.classList.remove("loading-scroll-mode"), this.viewportElement.addEventListener("scroll", this.onScroll, { passive: !0 }); } /** * Deactivates the scroll view and restores the standard slide-based * presentation. */ deactivate() { if (!this.active) return; const e = this.Reveal.getState(); this.active = !1, this.viewportElement.removeEventListener("scroll", this.onScroll), this.viewportElement.classList.remove("reveal-scroll"), this.removeProgressBar(), this.Reveal.getSlidesElement().innerHTML = this.slideHTMLBeforeActivation, this.Reveal.sync(), this.Reveal.setState(e), this.slideHTMLBeforeActivation = null; } toggle(e) { typeof e == "boolean" ? e ? this.activate() : this.deactivate() : this.isActive() ? this.deactivate() : this.activate(); } /** * Checks if the scroll view is currently active. */ isActive() { return this.active; } /** * Renders the progress bar component. */ createProgressBar() { this.progressBar = document.createElement("div"), this.progressBar.className = "scrollbar", this.progressBarInner = document.createElement("div"), this.progressBarInner.className = "scrollbar-inner", this.progressBar.appendChild(this.progressBarInner), this.progressBarPlayhead = document.createElement("div"), this.progressBarPlayhead.className = "scrollbar-playhead", this.progressBarInner.appendChild(this.progressBarPlayhead), this.viewportElement.insertBefore(this.progressBar, this.viewportElement.firstChild); const e = (s) => { let n = (s.clientY - this.progressBarInner.getBoundingClientRect().top) / this.progressBarHeight; n = Math.max(Math.min(n, 1), 0), this.viewportElement.scrollTop = n * (this.viewportElement.scrollHeight - this.viewportElement.offsetHeight); }, i = (s) => { this.draggingProgressBar = !1, this.showProgressBar(), document.removeEventListener("mousemove", e), document.removeEventListener("mouseup", i); }, t = (s) => { s.preventDefault(), this.draggingProgressBar = !0, document.addEventListener("mousemove", e), document.addEventListener("mouseup", i), e(s); }; this.progressBarInner.addEventListener("mousedown", t); } removeProgressBar() { this.progressBar && (this.progressBar.remove(), this.progressBar = null); } layout() { this.isActive() && (this.syncPages(), this.syncScrollPosition()); } /** * Updates our pages to match the latest configuration and * presentation size. */ syncPages() { const e = this.Reveal.getConfig(), i = this.Reveal.getComputedSlideSize(window.innerWidth, window.innerHeight), t = this.Reveal.getScale(), s = e.scrollLayout === "compact", n = this.viewportElement.offsetHeight, r = i.height * t, o = s ? r : n; this.scrollTriggerHeight = s ? r : n, this.viewportElement.style.setProperty("--page-height", o + "px"), this.viewportElement.style.scrollSnapType = typeof e.scrollSnap == "string" ? `y ${e.scrollSnap}` : "", this.slideTriggers = []; const h = Array.from(this.Reveal.getRevealElement().querySelectorAll(".scroll-page")); this.pages = h.map((u) => { const g = this.createPage({ pageElement: u, slideElement: u.querySelector("section"), stickyElement: u.querySelector(".scroll-page-sticky"), contentElement: u.querySelector(".scroll-page-content"), backgroundElement: u.querySelector(".slide-background"), autoAnimateElements: u.querySelectorAll(".scroll-auto-animate-page"), autoAnimatePages: [] }); g.pageElement.style.setProperty("--slide-height", e.center === !0 ? "auto" : i.height + "px"), this.slideTriggers.push({ page: g, activate: () => this.activatePage(g), deactivate: () => this.deactivatePage(g) }), this.createFragmentTriggersForPage(g), g.autoAnimateElements.length > 0 && this.createAutoAnimateTriggersForPage(g); let p = Math.max(g.scrollTriggers.length - 1, 0); p += g.autoAnimatePages.reduce((m, b) => m + Math.max(b.scrollTriggers.length - 1, 0), g.autoAnimatePages.length), g.pageElement.querySelectorAll(".scroll-snap-point").forEach((m) => m.remove()); for (let m = 0; m < p + 1; m++) { const b = document.createElement("div"); b.className = "scroll-snap-point", b.style.height = this.scrollTriggerHeight + "px", b.style.scrollSnapAlign = s ? "center" : "start", g.pageElement.appendChild(b), m === 0 && (b.style.marginTop = -this.scrollTriggerHeight + "px"); } return s && g.scrollTriggers.length > 0 ? (g.pageHeight = n, g.pageElement.style.setProperty("--page-height", n + "px")) : (g.pageHeight = o, g.pageElement.style.removeProperty("--page-height")), g.scrollPadding = this.scrollTriggerHeight * p, g.totalHeight = g.pageHeight + g.scrollPadding, g.pageElement.style.setProperty("--page-scroll-padding", g.scrollPadding + "px"), p > 0 ? (g.stickyElement.style.position = "sticky", g.stickyElement.style.top = Math.max((n - g.pageHeight) / 2, 0) + "px") : (g.stickyElement.style.position = "relative", g.pageElement.style.scrollSnapAlign = g.pageHeight < n ? "center" : "start"), g; }), this.setTriggerRanges(), this.viewportElement.setAttribute("data-scrollbar", e.scrollProgress), e.scrollProgress && this.totalScrollTriggerCount > 1 ? (this.progressBar || this.createProgressBar(), this.syncProgressBar()) : this.removeProgressBar(); } /** * Calculates and sets the scroll range for all of our scroll * triggers. */ setTriggerRanges() { this.totalScrollTriggerCount = this.slideTriggers.reduce((i, t) => i + Math.max(t.page.scrollTriggers.length, 1), 0); let e = 0; this.slideTriggers.forEach((i, t) => { i.range = [ e, e + Math.max(i.page.scrollTriggers.length, 1) / this.totalScrollTriggerCount ]; const s = (i.range[1] - i.range[0]) / i.page.scrollTriggers.length; i.page.scrollTriggers.forEach((n, r) => { n.range = [ e + r * s, e + (r + 1) * s ]; }), e = i.range[1]; }), this.slideTriggers[this.slideTriggers.length - 1].range[1] = 1; } /** * Creates one scroll trigger for each fragments in the given page. * * @param {*} page */ createFragmentTriggersForPage(e, i) { i = i || e.slideElement; const t = this.Reveal.fragments.sort(i.querySelectorAll(".fragment"), !0); return t.length && (e.fragments = this.Reveal.fragments.sort(i.querySelectorAll(".fragment:not(.disabled)")), e.scrollTriggers.push( // Trigger for the initial state with no fragments visible { activate: () => { this.Reveal.fragments.update(-1, e.fragments, i); } } ), t.forEach((s, n) => { e.scrollTriggers.push({ activate: () => { this.Reveal.fragments.update(n, e.fragments, i); } }); })), e.scrollTriggers.length; } /** * Creates scroll triggers for the auto-animate steps in the * given page. * * @param {*} page */ createAutoAnimateTriggersForPage(e) { e.autoAnimateElements.length > 0 && this.slideTriggers.push(...Array.from(e.autoAnimateElements).map((i, t) => { let s = this.createPage({ slideElement: i.querySelector("section"), contentElement: i, backgroundElement: i.querySelector(".slide-background") }); return this.createFragmentTriggersForPage(s, s.slideElement), e.autoAnimatePages.push(s), { page: s, activate: () => this.activatePage(s), deactivate: () => this.deactivatePage(s) }; })); } /** * Helper method for creating a page definition and adding * required fields. A "page" is a slide or auto-animate step. */ createPage(e) { return e.scrollTriggers = [], e.indexh = parseInt(e.slideElement.getAttribute("data-index-h"), 10), e.indexv = parseInt(e.slideElement.getAttribute("data-index-v"), 10), e; } /** * Rerenders progress bar segments so that they match the current * reveal.js config and size. */ syncProgressBar() { this.progressBarInner.querySelectorAll(".scrollbar-slide").forEach((r) => r.remove()); const e = this.viewportElement.scrollHeight, i = this.viewportElement.offsetHeight, t = i / e; this.progressBarHeight = this.progressBarInner.offsetHeight, this.playheadHeight = Math.max(t * this.progressBarHeight, Ti), this.progressBarScrollableHeight = this.progressBarHeight - this.playheadHeight; const s = i / e * this.progressBarHeight, n = Math.min(s / 8, Pi); this.progressBarPlayhead.style.height = this.playheadHeight - n + "px", s > xi ? this.slideTriggers.forEach((r) => { const { page: o } = r; o.progressBarSlide = document.createElement("div"), o.progressBarSlide.className = "scrollbar-slide", o.progressBarSlide.style.top = r.range[0] * this.progressBarHeight + "px", o.progressBarSlide.style.height = (r.range[1] - r.range[0]) * this.progressBarHeight - n + "px", o.progressBarSlide.classList.toggle("has-triggers", o.scrollTriggers.length > 0), this.progressBarInner.appendChild(o.progressBarSlide), o.scrollTriggerElements = o.scrollTriggers.map((h, u) => { const g = document.createElement("div"); return g.className = "scrollbar-trigger", g.style.top = (h.range[0] - r.range[0]) * this.progressBarHeight + "px", g.style.height = (h.range[1] - h.range[0]) * this.progressBarHeight - n + "px", o.progressBarSlide.appendChild(g), u === 0 && (g.style.display = "none"), g; }); }) : this.pages.forEach((r) => r.progressBarSlide = null); } /** * Reads the current scroll position and updates our active * trigger states accordingly. */ syncScrollPosition() { const e = this.viewportElement.offsetHeight, i = e / this.viewportElement.scrollHeight, t = this.viewportElement.scrollTop, s = this.viewportElement.scrollHeight - e, n = Math.max(Math.min(t / s, 1), 0), r = Math.max(Math.min((t + e / 2) / this.viewportElement.scrollHeight, 1), 0); let o; this.slideTriggers.forEach((h) => { const { page: u } = h; n >= h.range[0] - i * 2 && n <= h.range[1] + i * 2 && !u.loaded ? (u.loaded = !0, this.Reveal.slideContent.load(u.slideElement)) : u.loaded && (u.loaded = !1, this.Reveal.slideContent.unload(u.slideElement)), n >= h.range[0] && n <= h.range[1] ? (this.activateTrigger(h), o = h.page) : h.active && this.deactivateTrigger(h); }), o && o.scrollTriggers.forEach((h) => { r >= h.range[0] && r <= h.range[1] ? this.activateTrigger(h) : h.active && this.deactivateTrigger(h); }), this.setProgressBarValue(t / (this.viewportElement.scrollHeight - e)); } /** * Moves the progress bar playhead to the specified position. * * @param {number} progress 0-1 */ setProgressBarValue(e) { this.progressBar && (this.progressBarPlayhead.style.transform = `translateY(${e * this.progressBarScrollableHeight}px)`, this.getAllPages().filter((i) => i.progressBarSlide).forEach((i) => { i.progressBarSlide.classList.toggle("active", i.active === !0), i.scrollTriggers.forEach((t, s) => { i.scrollTriggerElements[s].classList.toggle("active", i.active === !0 && t.active === !0); }); }), this.showProgressBar()); } /** * Show the progress bar and, if configured, automatically hide * it after a delay. */ showProgressBar() { this.progressBar.classList.add("visible"), clearTimeout(this.hideProgressBarTimeout), this.Reveal.getConfig().scrollProgress === "auto" && !this.draggingProgressBar && (this.hideProgressBarTimeout = setTimeout(() => { this.progressBar && this.progressBar.classList.remove("visible"); }, Li)); } /** * Scroll to the previous page. */ prev() { this.viewportElement.scrollTop -= this.scrollTriggerHeight; } /** * Scroll to the next page. */ next() { this.viewportElement.scrollTop += this.scrollTriggerHeight; } /** * Scrolls the given slide element into view. * * @param {HTMLElement} slideElement */ scrollToSlide(e) { if (!this.active) this.activatedCallbacks.push(() => this.scrollToSlide(e)); else { const i = this.getScrollTriggerBySlide(e); i && (this.viewportElement.scrollTop = i.range[0] * (this.viewportElement.scrollHeight - this.viewportElement.offsetHeight)); } } /** * Persists the current scroll position to session storage * so that it can be restored. */ storeScrollPosition() { clearTimeout(this.storeScrollPositionTimeout), this.storeScrollPositionTimeout = setTimeout(() => { sessionStorage.setItem("reveal-scroll-top", this.viewportElement.scrollTop), sessionStorage.setItem("reveal-scroll-origin", location.origin + location.pathname), this.storeScrollPositionTimeout = null; }, 50); } /** * Restores the scroll position when a deck is reloader. */ restoreScrollPosition() { const e = sessionStorage.getItem("reveal-scroll-top"), i = sessionStorage.getItem("reveal-scroll-origin"); e && i === location.origin + location.pathname && (this.viewportElement.scrollTop = parseInt(e, 10)); } /** * Activates the given page and starts its embedded content * if there is any. * * @param {object} page */ activatePage(e) { if (!e.active) { e.active = !0; const { slideElement: i, backgroundElement: t, contentElement: s, indexh: n, indexv: r } = e; s.style.display = "block", i.classList.add("present"), t && t.classList.add("present"), this.Reveal.setCurrentScrollPage(i, n, r), this.Reveal.backgrounds.bubbleSlideContrastClassToElement(i, this.viewportElement), Array.from(s.parentNode.querySelectorAll(".scroll-page-content")).forEach((o) => { o !== s && (o.style.display = "none"); }); } } /** * Deactivates the page after it has been visible. * * @param {object} page */ deactivatePage(e) { e.active && (e.active = !1, e.slideElement && e.slideElement.classList.remove("present"), e.backgroundElement && e.backgroundElement.classList.remove("present")); } activateTrigger(e) { e.active || (e.active = !0, e.activate()); } deactivateTrigger(e) { e.active && (e.active = !1, e.deactivate && e.deactivate()); } /** * Retrieve a slide by its original h/v index (i.e. the indices the * slide had before being linearized). * * @param {number} h * @param {number} v * @returns {HTMLElement} */ getSlideByIndices(e, i) { const t = this.getAllPages().find((s) => s.indexh === e && s.indexv === i); return t ? t.slideElement : null; } /** * Retrieve a list of all scroll triggers for the given slide * DOM element. * * @param {HTMLElement} slide * @returns {Array} */ getScrollTriggerBySlide(e) { return this.slideTriggers.find((i) => i.page.slideElement === e); } /** * Get a list of all pages in the scroll view. This includes * both top-level slides and auto-animate steps. * * @returns {Array} */ getAllPages() { return this.pages.flatMap((e) => [e, ...e.autoAnimatePages || []]); } onScroll() { this.syncScrollPosition(), this.storeScrollPosition(); } get viewportElement() { return this.Reveal.getViewportElement(); } } class Mi { constructor(e) { this.Reveal = e; } /** * Configures the presentation for printing to a static * PDF. */ async activate() { const e = this.Reveal.getConfig(), i = E(this.Reveal.getRevealElement(), oe), t = e.slideNumber && /all|print/i.test(e.showSlideNumber), s = this.Reveal.getComputedSlideSize(window.innerWidth, window.innerHeight), n = Math.floor(s.width * (1 + e.margin)), r = Math.floor(s.height * (1 + e.margin)), o = s.width, h = s.height; await new Promise(requestAnimationFrame), Ue("@page{size:" + n + "px " + r + "px; margin: 0px;}"), Ue(".reveal section>img, .reveal section>video, .reveal section>iframe{max-width: " + o + "px; max-height:" + h + "px}"), document.documentElement.classList.add("reveal-print", "print-pdf"), document.body.style.width = n + "px", document.body.style.height = r + "px"; const u = this.Reveal.getViewportElement(); let g; if (u) { const R = window.getComputedStyle(u); R && R.background && (g = R.background); } await new Promise(requestAnimationFrame), this.Reveal.layoutSlideContents(o, h), await new Promise(requestAnimationFrame); const p = i.map((R) => R.scrollHeight), m = [], b = i[0].parentNode; let l = 1; i.forEach(function(R, M) { if (R.classList.contains("stack") === !1) { let O = (n - o) / 2, q = (r - h) / 2; const ae = p[M]; let z = Math.max(Math.ceil(ae / r), 1); z = Math.min(z, e.pdfMaxPagesPerSlide), (z === 1 && e.center || R.classList.contains("center")) && (q = Math.max((r - ae) / 2, 0)); const k = document.createElement("div"); if (m.push(k), k.className = "pdf-page", k.style.height = (r + e.pdfPageHeightOffset) * z + "px", g && (k.style.background = g), k.appendChild(R), R.style.left = O + "px", R.style.top = q + "px", R.style.width = o + "px", this.Reveal.slideContent.layout(R), R.slideBackgroundElement && k.insertBefore(R.slideBackgroundElement, R), e.showNotes) { const B = this.Reveal.getSlideNotes(R); if (B) { const W = typeof e.showNotes == "string" ? e.showNotes : "inline", L = document.createElement("div"); L.classList.add("speaker-notes"), L.classList.add("speaker-notes-pdf"), L.setAttribute("data-layout", W), L.innerHTML = B, W === "separate-page" ? m.push(L) : (L.style.left = "8px", L.style.bottom = "8px", L.style.width = n - 8 * 2 + "px", k.appendChild(L)); } } if (t) { const B = document.createElement("div"); B.classList.add("slide-number"), B.classList.add("slide-number-pdf"), B.innerHTML = l++, k.appendChild(B); } if (e.pdfSeparateFragments) { const B = this.Reveal.fragments.sort(k.querySelectorAll(".fragment"), !0); let U; B.forEach(function(W, L) { U && U.forEach(function(F) { F.classList.remove("current-fragment"); }), W.forEach(function(F) { F.classList.add("visible", "current-fragment"); }, this); const A = k.cloneNode(!0); if (t) { const F = A.querySelector(".slide-number-pdf"), C = L + 1; F.innerHTML += "." + C; } m.push(A), U = W; }, this), B.forEach(function(W) { W.forEach(function(L) { L.classList.remove("visible", "current-fragment"); }); }); } else E(k, ".fragment:not(.fade-out)").forEach(function(B) { B.classList.add("visible"); }); } }, this), await new Promise(requestAnimationFrame), m.forEach((R) => b.appendChild(R)), this.Reveal.slideContent.layout(this.Reveal.getSlidesElement()), this.Reveal.dispatchEvent({ type: "pdf-ready" }), u.classList.remove("loading-scroll-mode"); } /** * Checks if the print mode is/should be activated. */ isActive() { return this.Reveal.getConfig().view === "print"; } } class Ni { constructor(e) { this.Reveal = e; } /** * Called when the reveal.js config is updated. */ configure(e, i) { e.fragments === !1 ? this.disable() : i.fragments === !1 && this.enable(); } /** * If fragments are disabled in the deck, they should all be * visible rather than stepped through. */ disable() { E(this.Reveal.getSlidesElement(), ".fragment").forEach((e) => { e.classList.add("visible"), e.classList.remove("current-fragment"); }); } /** * Reverse of #disable(). Only called if fragments have * previously been disabled. */ enable() { E(this.Reveal.getSlidesElement(), ".fragment").forEach((e) => { e.classList.remove("visible"), e.classList.remove("current-fragment"); }); } /** * Returns an object describing the available fragment * directions. * * @return {{prev: boolean, next: boolean}} */ availableRoutes() { let e = this.Reveal.getCurrentSlide(); if (e && this.Reveal.getConfig().fragments) { let i = e.querySelectorAll(".fragment:not(.disabled)"), t = e.querySelectorAll(".fragment:not(.disabled):not(.visible)"); return { prev: i.length - t.length > 0, next: !!t.length }; } else return { prev: !1, next: !1 }; } /** * Return a sorted fragments list, ordered by an increasing * "data-fragment-index" attribute. * * Fragments will be revealed in the order that they are returned by * this function, so you can use the index attributes to control the * order of fragment appearance. * * To maintain a sensible default fragment order, fragments are presumed * to be passed in document order. This function adds a "fragment-index" * attribute to each node if such an attribute is not already present, * and sets that attribute to an integer value which is the position of * the fragment within the fragments list. * * @param {object[]|*} fragments * @param {boolean} grouped If true the returned array will contain * nested arrays for all fragments with the same index * @return {object[]} sorted Sorted array of fragments */ sort(e, i = !1) { e = Array.from(e); let t = [], s = [], n = []; e.forEach((o) => { if (o.hasAttribute("data-fragment-index")) { let h = parseInt(o.getAttribute("data-fragment-index"), 10); t[h] || (t[h] = []), t[h].push(o); } else s.push([o]); }), t = t.concat(s); let r = 0; return t.forEach((o) => { o.forEach((h) => { n.push(h), h.setAttribute("data-fragment-index", r); }), r++; }), i === !0 ? t : n; } /** * Sorts and formats all of fragments in the * presentation. */ sortAll() { this.Reveal.getHorizontalSlides().forEach((e) => { let i = E(e, "section"); i.forEach((t, s) => { this.sort(t.querySelectorAll(".fragment")); }, this), i.length === 0 && this.sort(e.querySelectorAll(".fragment")); }); } /** * Refreshes the fragments on the current slide so that they * have the appropriate classes (.visible + .current-fragment). * * @param {number} [index] The index of the current fragment * @param {array} [fragments] Array containing all fragments * in the current slide * * @return {{shown: array, hidden: array}} */ update(e, i, t = this.Reveal.getCurrentSlide()) { let s = { shown: [], hidden: [] }; if (t && this.Reveal.getConfig().fragments && (i = i || this.sort(t.querySelectorAll(".fragment")), i.length)) { let n = 0; if (typeof e != "number") { let r = this.sort(t.querySelectorAll(".fragment.visible")).pop(); r && (e = parseInt(r.getAttribute("data-fragment-index") || 0, 10)); } Array.from(i).forEach((r, o) => { if (r.hasAttribute("data-fragment-index") && (o = parseInt(r.getAttribute("data-fragment-index"), 10)), n = Math.max(n, o), o <= e) { let h = r.classList.contains("visible"); r.classList.add("visible"), r.classList.remove("current-fragment"), o === e && (this.Reveal.announceStatus(this.Reveal.getStatusText(r)), r.classList.add("current-fragment"), this.Reveal.slideContent.startEmbeddedContent(r)), h || (s.shown.push(r), this.Reveal.dispatchEvent({ target: r, type: "visible", bubbles: !1 })); } else { let h = r.classList.contains("visible"); r.classList.remove("visible"), r.classList.remove("current-fragment"), h && (this.Reveal.slideContent.stopEmbeddedContent(r), s.hidden.push(r), this.Reveal.dispatchEvent({ target: r, type: "hidden", bubbles: !1 })); } }), e = typeof e == "number" ? e : -1, e = Math.max(Math.min(e, n), -1), t.setAttribute("data-fragment", e); } return s.hidden.length && this.Reveal.dispatchEvent({ type: "fragmenthidden", data: { fragment: s.hidden[0], fragments: s.hidden } }), s.shown.length && this.Reveal.dispatchEvent({ type: "fragmentshown", data: { fragment: s.shown[0], fragments: s.shown } }), s; } /** * Formats the fragments on the given slide so that they have * valid indices. Call this if fragments are changed in the DOM * after reveal.js has already initialized. * * @param {HTMLElement} slide * @return {Array} a list of the HTML fragments that were synced */ sync(e = this.Reveal.getCurrentSlide()) { return this.sort(e.querySelectorAll(".fragment")); } /** * Navigate to the specified slide fragment. * * @param {?number} index The index of the fragment that * should be shown, -1 means all are invisible * @param {number} offset Integer offset to apply to the * fragment index * * @return {boolean} true if a change was made in any * fragments visibility as part of this call */ goto(e, i = 0) { let t = this.Reveal.getCurrentSlide(); if (t && this.Reveal.getConfig().fragments) { let s = this.sort(t.querySelectorAll(".fragment:not(.disabled)")); if (s.length) { if (typeof e != "number") { let r = this.sort(t.querySelectorAll(".fragment:not(.disabled).visible")).pop(); r ? e = parseInt(r.getAttribute("data-fragment-index") || 0, 10) : e = -1; } e += i; let n = this.update(e, s); return this.Reveal.controls.update(), this.Reveal.progress.update(), this.Reveal.getConfig().fragmentInURL && this.Reveal.location.writeURL(), !!(n.shown.length || n.hidden.length); } } return !1; } /** * Navigate to the next slide fragment. * * @return {boolean} true if there was a next fragment, * false otherwise */ next() { return this.goto(null, 1); } /** * Navigate to the previous slide fragment. * * @return {boolean} true if there was a previous fragment, * false otherwise */ prev() { return this.goto(null, -1); } } class Bi { constructor(e) { this.Reveal = e, this.active = !1, this.onSlideClicked = this.onSlideClicked.bind(this); } /** * Displays the overview of slides (quick nav) by scaling * down and arranging all slide elements. */ activate() { if (this.Reveal.getConfig().overview && !this.Reveal.isScrollView() && !this.isActive()) { this.active = !0, this.Reveal.getRevealElement().classList.add("overview"), this.Reveal.cancelAutoSlide(), this.Reveal.getSlidesElement().appendChild(this.Reveal.getBackgroundsElement()), E(this.Reveal.getRevealElement(), oe).forEach((s) => { s.classList.contains("stack") || s.addEventListener("click", this.onSlideClicked, !0); }); const e = 70, i = this.Reveal.getComputedSlideSize(); this.overviewSlideWidth = i.width + e, this.overviewSlideHeight = i.height + e, this.Reveal.getConfig().rtl && (this.overviewSlideWidth = -this.overviewSlideWidth), this.Reveal.updateSlidesVisibility(), this.layout(), this.update(), this.Reveal.layout(); const t = this.Reveal.getIndices(); this.Reveal.dispatchEvent({ type: "overviewshown", data: { indexh: t.h, indexv: t.v, currentSlide: this.Reveal.getCurrentSlide() } }); } } /** * Uses CSS transforms to position all slides in a grid for * display inside of the overview mode. */ layout() { this.Reveal.getHorizontalSlides().forEach((e, i) => { e.setAttribute("data-index-h", i), se(e, "translate3d(" + i * this.overviewSlideWidth + "px, 0, 0)"), e.classList.contains("stack") && E(e, "section").forEach((t, s) => { t.setAttribute("data-index-h", i), t.setAttribute("data-index-v", s), se(t, "translate3d(0, " + s * this.overviewSlideHeight + "px, 0)"); }); }), Array.from(this.Reveal.getBackgroundsElement().childNodes).forEach((e, i) => { se(e, "translate3d(" + i * this.overviewSlideWidth + "px, 0, 0)"), E(e, ".slide-background").forEach((t, s) => { se(t, "translate3d(0, " + s * this.overviewSlideHeight + "px, 0)"); }); }); } /** * Moves the overview viewport to the current slides. * Called each time the current slide changes. */ update() { const e = Math.min(window.innerWidth, window.innerHeight), i = Math.max(e / 5, 150) / e, t = this.Reveal.getIndices(); this.Reveal.transformSlides({ overview: [ "scale(" + i + ")", "translateX(" + -t.h * this.overviewSlideWidth + "px)", "translateY(" + -t.v * this.overviewSlideHeight + "px)" ].join(" ") }); } /** * Exits the slide overview and enters the currently * active slide. */ deactivate() { if (this.Reveal.getConfig().overview) { this.active = !1, this.Reveal.getRevealElement().classList.remove("overview"), this.Reveal.getRevealElement().classList.add("overview-deactivating"), setTimeout(() => { this.Reveal.getRevealElement().classList.remove("overview-deactivating"); }, 1), this.Reveal.getRevealElement().appendChild(this.Reveal.getBackgroundsElement()), E(this.Reveal.getRevealElement(), oe).forEach((i) => { se(i, ""), i.removeEventListener("click", this.onSlideClicked, !0); }), E(this.Reveal.getBackgroundsElement(), ".slide-background").forEach((i) => { se(i, ""); }), this.Reveal.transformSlides({ overview: "" }); const e = this.Reveal.getIndices(); this.Reveal.slide(e.h, e.v), this.Reveal.layout(), this.Reveal.cueAutoSlide(), this.Reveal.dispatchEvent({ type: "overviewhidden", data: { indexh: e.h, indexv: e.v, currentSlide: this.Reveal.getCurrentSlide() } }); } } /** * Toggles the slide overview mode on and off. * * @param {Boolean} [override] Flag which overrides the * toggle logic and forcibly sets the desired state. True means * overview is open, false means it's closed. */ toggle(e) { typeof e == "boolean" ? e ? this.activate() : this.deactivate() : this.isActive() ? this.deactivate() : this.activate(); } /** * Checks if the overview is currently active. * * @return {Boolean} true if the overview is active, * false otherwise */ isActive() { return this.active; } /** * Invoked when a slide is and we're in the overview. * * @param {object} event */ onSlideClicked(e) { if (this.isActive()) { e.preventDefault(); let i = e.target; for (; i && !i.nodeName.match(/section/gi); ) i = i.parentNode; if (i && !i.classList.contains("disabled") && (this.deactivate(), i.nodeName.match(/section/gi))) { let t = parseInt(i.getAttribute("data-index-h"), 10), s = parseInt(i.getAttribute("data-index-v"), 10); this.Reveal.slide(t, s); } } } } class Hi { constructor(e) { this.Reveal = e, this.shortcuts = {}, this.bindings = {}, this.onDocumentKeyDown = this.onDocumentKeyDown.bind(this); } /** * Called when the reveal.js config is updated. */ configure(e, i) { e.navigationMode === "linear" ? (this.shortcuts["→ , ↓ , SPACE , N , L , J"] = "Next slide", this.shortcuts["← , ↑ , P , H , K"] = "Previous slide") : (this.shortcuts["N , SPACE"] = "Next slide", this.shortcuts["P , Shift SPACE"] = "Previous slide", this.shortcuts["← , H"] = "Navigate left", this.shortcuts["→ , L"] = "Navigate right", this.shortcuts["↑ , K"] = "Navigate up", this.shortcuts["↓ , J"] = "Navigate down"), this.shortcuts["Alt + ←/↑/→/↓"] = "Navigate without fragments", this.shortcuts["Shift + ←/↑/→/↓"] = "Jump to first/last slide", this.shortcuts["B , ."] = "Pause", this.shortcuts.F = "Fullscreen", this.shortcuts.G = "Jump to slide", this.shortcuts["ESC, O"] = "Slide overview"; } /** * Starts listening for keyboard events. */ bind() { document.addEventListener("keydown", this.onDocumentKeyDown, !1); } /** * Stops listening for keyboard events. */ unbind() { document.removeEventListener("keydown", this.onDocumentKeyDown, !1); } /** * Add a custom key binding with optional description to * be added to the help screen. */ addKeyBinding(e, i) { typeof e == "object" && e.keyCode ? this.bindings[e.keyCode] = { callback: i, key: e.key, description: e.description } : this.bindings[e] = { callback: i, key: null, description: null }; } /** * Removes the specified custom key binding. */ removeKeyBinding(e) { delete this.bindings[e]; } /** * Programmatically triggers a keyboard event * * @param {int} keyCode */ triggerKey(e) { this.onDocumentKeyDown({ keyCode: e }); } /** * Registers a new shortcut to include in the help overlay * * @param {String} key * @param {String} value */ registerKeyboardShortcut(e, i) { this.shortcuts[e] = i; } getShortcuts() { return this.shortcuts; } getBindings() { return this.bindings; } /** * Handler for the document level 'keydown' event. * * @param {object} event */ onDocumentKeyDown(e) { let i = this.Reveal.getConfig(); if (typeof i.keyboardCondition == "function" && i.keyboardCondition(e) === !1 || i.keyboardCondition === "focused" && !this.Reveal.isFocused()) return !0; let t = e.keyCode, s = !this.Reveal.isAutoSliding(); this.Reveal.onUserInput(e); let n = document.activeElement && document.activeElement.isContentEditable === !0, r = document.activeElement && document.activeElement.tagName && /input|textarea/i.test(document.activeElement.tagName), o = document.activeElement && document.activeElement.className && /speaker-notes/i.test(document.activeElement.className), u = !([32, 37, 38, 39, 40, 63, 78, 80, 191].indexOf(e.keyCode) !== -1 && e.shiftKey || e.altKey) && (e.shiftKey || e.altKey || e.ctrlKey || e.metaKey); if (n || r || o || u) return; let g = [66, 86, 190, 191, 112], p; if (typeof i.keyboard == "object") for (p in i.keyboard) i.keyboard[p] === "togglePause" && g.push(parseInt(p, 10)); if (this.Reveal.isOverlayOpen() && !["Escape", "f", "c", "b", "."].includes(e.key) || this.Reveal.isPaused() && g.indexOf(t) === -1) return !1; let m = i.navigationMode === "linear" || !this.Reveal.hasHorizontalSlides() || !this.Reveal.hasVerticalSlides(), b = !1; if (typeof i.keyboard == "object") { for (p in i.keyboard) if (parseInt(p, 10) === t) { let l = i.keyboard[p]; typeof l == "function" ? l.apply(null, [e]) : typeof l == "string" && typeof this.Reveal[l] == "function" && this.Reveal[l].call(), b = !0; } } if (b === !1) { for (p in this.bindings) if (parseInt(p, 10) === t) { let l = this.bindings[p].callback; typeof l == "function" ? l.apply(null, [e]) : typeof l == "string" && typeof this.Reveal[l] == "function" && this.Reveal[l].call(), b = !0; } } b === !1 && (b = !0, t === 80 || t === 33 ? this.Reveal.prev({ skipFragments: e.altKey }) : t === 78 || t === 34 ? this.Reveal.next({ skipFragments: e.altKey }) : t === 72 || t === 37 ? e.shiftKey ? this.Reveal.slide(0) : !this.Reveal.overview.isActive() && m ? i.rtl ? this.Reveal.next({ skipFragments: e.altKey }) : this.Reveal.prev({ skipFragments: e.altKey }) : this.Reveal.left({ skipFragments: e.altKey }) : t === 76 || t === 39 ? e.shiftKey ? this.Reveal.slide(this.Reveal.getHorizontalSlides().length - 1) : !this.Reveal.overview.isActive() && m ? i.rtl ? this.Reveal.prev({ skipFragments: e.altKey }) : this.Reveal.next({ skipFragments: e.altKey }) : this.Reveal.right({ skipFragments: e.altKey }) : t === 75 || t === 38 ? e.shiftKey ? this.Reveal.slide(void 0, 0) : !this.Reveal.overview.isActive() && m ? this.Reveal.prev({ skipFragments: e.altKey }) : this.Reveal.up({ skipFragments: e.altKey }) : t === 74 || t === 40 ? e.shiftKey ? this.Reveal.slide(void 0, Number.MAX_VALUE) : !this.Reveal.overview.isActive() && m ? this.Reveal.next({ skipFragments: e.altKey }) : this.Reveal.down({ skipFragments: e.altKey }) : t === 36 ? this.Reveal.slide(0) : t === 35 ? this.Reveal.slide(this.Reveal.getHorizontalSlides().length - 1) : t === 32 ? (this.Reveal.overview.isActive() && this.Reveal.overview.deactivate(), e.shiftKey ? this.Reveal.prev({ skipFragments: e.altKey }) : this.Reveal.next({ skipFragments: e.altKey })) : [58, 59, 66, 86, 190].includes(t) || t === 191 && !e.shiftKey ? this.Reveal.togglePause() : t === 70 ? xt(i.embedded ? this.Reveal.getViewportElement() : document.documentElement) : t === 65 ? i.autoSlideStoppable && this.Reveal.toggleAutoSlide(s) : t === 71 ? i.jumpToSlide && this.Reveal.toggleJumpToSlide() : t === 67 && this.Reveal.isOverlayOpen() ? this.Reveal.closeOverlay() : (t === 63 || t === 191) && e.shiftKey ? this.Reveal.toggleHelp() : t === 112 ? this.Reveal.toggleHelp() : b = !1), b ? e.preventDefault && e.preventDefault() : t === 27 || t === 79 ? (this.Reveal.closeOverlay() === !1 && this.Reveal.overview.toggle(), e.preventDefault && e.preventDefault()) : t === 13 && this.Reveal.overview.isActive() && (this.Reveal.overview.deactivate(), e.preventDefault && e.preventDefault()), this.Reveal.cueAutoSlide(); } } class Di { constructor(e) { // The minimum number of milliseconds that must pass between // calls to history.replaceState Rt(this, "MAX_REPLACE_STATE_FREQUENCY", 1e3); this.Reveal = e, this.writeURLTimeout = 0, this.replaceStateTimestamp = 0, this.onWindowHashChange = this.onWindowHashChange.bind(this); } bind() { window.addEventListener("hashchange", this.onWindowHashChange, !1); } unbind() { window.removeEventListener("hashchange", this.onWindowHashChange, !1); } /** * Returns the slide indices for the given hash link. * * @param {string} [hash] the hash string that we want to * find the indices for * * @returns slide indices or null */ getIndicesFromHash(e = window.location.hash, i = {}) { let t = e.replace(/^#\/?/, ""), s = t.split("/"); if (!/^[0-9]*$/.test(s[0]) && t.length) { let n, r; /\/[-\d]+$/g.test(t) && (r = parseInt(t.split("/").pop(), 10), r = isNaN(r) ? void 0 : r, t = t.split("/").shift()); try { n = document.getElementById(decodeURIComponent(t)).closest(".slides section"); } catch { } if (n) return { ...this.Reveal.getIndices(n), f: r }; } else { const n = this.Reveal.getConfig(); let r = n.hashOneBasedIndex || i.oneBasedIndex ? 1 : 0, o = parseInt(s[0], 10) - r || 0, h = parseInt(s[1], 10) - r || 0, u; return n.fragmentInURL && (u = parseInt(s[2], 10), isNaN(u) && (u = void 0)), { h: o, v: h, f: u }; } return null; } /** * Reads the current URL (hash) and navigates accordingly. */ readURL() { const e = this.Reveal.getIndices(), i = this.getIndicesFromHash(); i ? (i.h !== e.h || i.v !== e.v || i.f !== void 0) && this.Reveal.slide(i.h, i.v, i.f) : this.Reveal.slide(e.h || 0, e.v || 0); } /** * Updates the page URL (hash) to reflect the current * state. * * @param {number} delay The time in ms to wait before * writing the hash */ writeURL(e) { let i = this.Reveal.getConfig(), t = this.Reveal.getCurrentSlide(); if (clearTimeout(this.writeURLTimeout), typeof e == "number") this.writeURLTimeout = setTimeout(this.writeURL, e); else if (t) { let s = this.getHash(); i.history ? window.location.hash = s : i.hash && (s === "/" ? this.debouncedReplaceState(window.location.pathname + window.location.search) : this.debouncedReplaceState("#" + s)); } } replaceState(e) { window.history.replaceState(null, null, e), this.replaceStateTimestamp = Date.now(); } debouncedReplaceState(e) { clearTimeout(this.replaceStateTimeout), Date.now() - this.replaceStateTimestamp > this.MAX_REPLACE_STATE_FREQUENCY ? this.replaceState(e) : this.replaceStateTimeout = setTimeout(() => this.replaceState(e), this.MAX_REPLACE_STATE_FREQUENCY); } /** * Return a hash URL that will resolve to the given slide location. * * @param {HTMLElement} [slide=currentSlide] The slide to link to */ getHash(e) { let i = "/", t = e || this.Reveal.getCurrentSlide(), s = t ? t.getAttribute("id") : null; s && (s = encodeURIComponent(s)); let n = this.Reveal.getIndices(e); if (this.Reveal.getConfig().fragmentInURL || (n.f = void 0), typeof s == "string" && s.length) i = "/" + s, n.f >= 0 && (i += "/" + n.f); else { let r = this.Reveal.getConfig().hashOneBasedIndex ? 1 : 0; (n.h > 0 || n.v > 0 || n.f >= 0) && (i += n.h + r), (n.v > 0 || n.f >= 0) && (i += "/" + (n.v + r)), n.f >= 0 && (i += "/" + n.f); } return i; } /** * Handler for the window level 'hashchange' event. * * @param {object} [event] */ onWindowHashChange(e) { this.readURL(); } } class Fi { constructor(e) { this.Reveal = e, this.onNavigateLeftClicked = this.onNavigateLeftClicked.bind(this), this.onNavigateRightClicked = this.onNavigateRightClicked.bind(this), this.onNavigateUpClicked = this.onNavigateUpClicked.bind(this), this.onNavigateDownClicked = this.onNavigateDownClicked.bind(this), this.onNavigatePrevClicked = this.onNavigatePrevClicked.bind(this), this.onNavigateNextClicked = this.onNavigateNextClicked.bind(this), this.onEnterFullscreen = this.onEnterFullscreen.bind(this); } render() { const e = this.Reveal.getConfig().rtl, i = this.Reveal.getRevealElement(); this.element = document.createElement("aside"), this.element.className = "controls", this.element.innerHTML = ` `, this.Reveal.getRevealElement().appendChild(this.element), this.controlsLeft = E(i, ".navigate-left"), this.controlsRight = E(i, ".navigate-right"), this.controlsUp = E(i, ".navigate-up"), this.controlsDown = E(i, ".navigate-down"), this.controlsPrev = E(i, ".navigate-prev"), this.controlsNext = E(i, ".navigate-next"), this.controlsFullscreen = E(i, ".enter-fullscreen"), this.controlsRightArrow = this.element.querySelector(".navigate-right"), this.controlsLeftArrow = this.element.querySelector(".navigate-left"), this.controlsDownArrow = this.element.querySelector(".navigate-down"); } /** * Called when the reveal.js config is updated. */ configure(e, i) { this.element.style.display = e.controls && (e.controls !== "speaker-only" || this.Reveal.isSpeakerNotes()) ? "block" : "none", this.element.setAttribute("data-controls-layout", e.controlsLayout), this.element.setAttribute("data-controls-back-arrows", e.controlsBackArrows); } bind() { let e = ["touchstart", "click"]; It && (e = ["touchstart"]), e.forEach((i) => { this.controlsLeft.forEach((t) => t.addEventListener(i, this.onNavigateLeftClicked, !1)), this.controlsRight.forEach((t) => t.addEventListener(i, this.onNavigateRightClicked, !1)), this.controlsUp.forEach((t) => t.addEventListener(i, this.onNavigateUpClicked, !1)), this.controlsDown.forEach((t) => t.addEventListener(i, this.onNavigateDownClicked, !1)), this.controlsPrev.forEach((t) => t.addEventListener(i, this.onNavigatePrevClicked, !1)), this.controlsNext.forEach((t) => t.addEventListener(i, this.onNavigateNextClicked, !1)), this.controlsFullscreen.forEach((t) => t.addEventListener(i, this.onEnterFullscreen, !1)); }); } unbind() { ["touchstart", "click"].forEach((e) => { this.controlsLeft.forEach((i) => i.removeEventListener(e, this.onNavigateLeftClicked, !1)), this.controlsRight.forEach((i) => i.removeEventListener(e, this.onNavigateRightClicked, !1)), this.controlsUp.forEach((i) => i.removeEventListener(e, this.onNavigateUpClicked, !1)), this.controlsDown.forEach((i) => i.removeEventListener(e, this.onNavigateDownClicked, !1)), this.controlsPrev.forEach((i) => i.removeEventListener(e, this.onNavigatePrevClicked, !1)), this.controlsNext.forEach((i) => i.removeEventListener(e, this.onNavigateNextClicked, !1)), this.controlsFullscreen.forEach((i) => i.removeEventListener(e, this.onEnterFullscreen, !1)); }); } /** * Updates the state of all control/navigation arrows. */ update() { let e = this.Reveal.availableRoutes(); [...this.controlsLeft, ...this.controlsRight, ...this.controlsUp, ...this.controlsDown, ...this.controlsPrev, ...this.controlsNext].forEach((t) => { t.classList.remove("enabled", "fragmented"), t.setAttribute("disabled", "disabled"); }), e.left && this.controlsLeft.forEach((t) => { t.classList.add("enabled"), t.removeAttribute("disabled"); }), e.right && this.controlsRight.forEach((t) => { t.classList.add("enabled"), t.removeAttribute("disabled"); }), e.up && this.controlsUp.forEach((t) => { t.classList.add("enabled"), t.removeAttribute("disabled"); }), e.down && this.controlsDown.forEach((t) => { t.classList.add("enabled"), t.removeAttribute("disabled"); }), (e.left || e.up) && this.controlsPrev.forEach((t) => { t.classList.add("enabled"), t.removeAttribute("disabled"); }), (e.right || e.down) && this.controlsNext.forEach((t) => { t.classList.add("enabled"), t.removeAttribute("disabled"); }); let i = this.Reveal.getCurrentSlide(); if (i) { let t = this.Reveal.fragments.availableRoutes(); t.prev && this.controlsPrev.forEach((r) => { r.classList.add("fragmented", "enabled"), r.removeAttribute("disabled"); }), t.next && this.controlsNext.forEach((r) => { r.classList.add("fragmented", "enabled"), r.removeAttribute("disabled"); }); const s = this.Reveal.isVerticalSlide(i), n = s && i.parentElement && i.parentElement.querySelectorAll(":scope > section").length > 1; s && n ? (t.prev && this.controlsUp.forEach((r) => { r.classList.add("fragmented", "enabled"), r.removeAttribute("disabled"); }), t.next && this.controlsDown.forEach((r) => { r.classList.add("fragmented", "enabled"), r.removeAttribute("disabled"); })) : (t.prev && this.controlsLeft.forEach((r) => { r.classList.add("fragmented", "enabled"), r.removeAttribute("disabled"); }), t.next && this.controlsRight.forEach((r) => { r.classList.add("fragmented", "enabled"), r.removeAttribute("disabled"); })); } if (this.Reveal.getConfig().controlsTutorial) { let t = this.Reveal.getIndices(); !this.Reveal.hasNavigatedVertically() && e.down ? this.controlsDownArrow.classList.add("highlight") : (this.controlsDownArrow.classList.remove("highlight"), this.Reveal.getConfig().rtl ? !this.Reveal.hasNavigatedHorizontally() && e.left && t.v === 0 ? this.controlsLeftArrow.classList.add("highlight") : this.controlsLeftArrow.classList.remove("highlight") : !this.Reveal.hasNavigatedHorizontally() && e.right && t.v === 0 ? this.controlsRightArrow.classList.add("highlight") : this.controlsRightArrow.classList.remove("highlight")); } } destroy() { this.unbind(), this.element.remove(); } /** * Event handlers for navigation control buttons. */ onNavigateLeftClicked(e) { e.preventDefault(), this.Reveal.onUserInput(), this.Reveal.getConfig().navigationMode === "linear" ? this.Reveal.prev() : this.Reveal.left(); } onNavigateRightClicked(e) { e.preventDefault(), this.Reveal.onUserInput(), this.Reveal.getConfig().navigationMode === "linear" ? this.Reveal.next() : this.Reveal.right(); } onNavigateUpClicked(e) { e.preventDefault(), this.Reveal.onUserInput(), this.Reveal.up(); } onNavigateDownClicked(e) { e.preventDefault(), this.Reveal.onUserInput(), this.Reveal.down(); } onNavigatePrevClicked(e) { e.preventDefault(), this.Reveal.onUserInput(), this.Reveal.prev(); } onNavigateNextClicked(e) { e.preventDefault(), this.Reveal.onUserInput(), this.Reveal.next(); } onEnterFullscreen(e) { const i = this.Reveal.getConfig(), t = this.Reveal.getViewportElement(); xt(i.embedded ? t : t.parentElement); } } class zi { constructor(e) { this.Reveal = e, this.onProgressClicked = this.onProgressClicked.bind(this); } render() { this.element = document.createElement("div"), this.element.className = "progress", this.Reveal.getRevealElement().appendChild(this.element), this.bar = document.createElement("span"), this.element.appendChild(this.bar); } /** * Called when the reveal.js config is updated. */ configure(e, i) { this.element.style.display = e.progress ? "block" : "none"; } bind() { this.Reveal.getConfig().progress && this.element && this.element.addEventListener("click", this.onProgressClicked, !1); } unbind() { this.Reveal.getConfig().progress && this.element && this.element.removeEventListener("click", this.onProgressClicked, !1); } /** * Updates the progress bar to reflect the current slide. */ update() { if (this.Reveal.getConfig().progress && this.bar) { let e = this.Reveal.getProgress(); this.Reveal.getTotalSlides() < 2 && (e = 0), this.bar.style.transform = "scaleX(" + e + ")"; } } getMaxWidth() { return this.Reveal.getRevealElement().offsetWidth; } /** * Clicking on the progress bar results in a navigation to the * closest approximate horizontal slide using this equation: * * ( clickX / presentationWidth ) * numberOfSlides * * @param {object} event */ onProgressClicked(e) { this.Reveal.onUserInput(e), e.preventDefault(); let i = this.Reveal.getSlides(), t = i.length, s = Math.floor(e.clientX / this.getMaxWidth() * t); this.Reveal.getConfig().rtl && (s = t - s); let n = this.Reveal.getIndices(i[s]); this.Reveal.slide(n.h, n.v); } destroy() { this.element.remove(); } } class Vi { constructor(e) { this.Reveal = e, this.lastMouseWheelStep = 0, this.cursorHidden = !1, this.cursorInactiveTimeout = 0, this.onDocumentCursorActive = this.onDocumentCursorActive.bind(this), this.onDocumentMouseScroll = this.onDocumentMouseScroll.bind(this); } /** * Called when the reveal.js config is updated. */ configure(e, i) { e.mouseWheel ? document.addEventListener("wheel", this.onDocumentMouseScroll, !1) : document.removeEventListener("wheel", this.onDocumentMouseScroll, !1), e.hideInactiveCursor ? (document.addEventListener("mousemove", this.onDocumentCursorActive, !1), document.addEventListener("mousedown", this.onDocumentCursorActive, !1)) : (this.showCursor(), document.removeEventListener("mousemove", this.onDocumentCursorActive, !1), document.removeEventListener("mousedown", this.onDocumentCursorActive, !1)); } /** * Shows the mouse pointer after it has been hidden with * #hideCursor. */ showCursor() { this.cursorHidden && (this.cursorHidden = !1, this.Reveal.getRevealElement().style.cursor = ""); } /** * Hides the mouse pointer when it's on top of the .reveal * container. */ hideCursor() { this.cursorHidden === !1 && (this.cursorHidden = !0, this.Reveal.getRevealElement().style.cursor = "none"); } destroy() { this.showCursor(), document.removeEventListener("wheel", this.onDocumentMouseScroll, !1), document.removeEventListener("mousemove", this.onDocumentCursorActive, !1), document.removeEventListener("mousedown", this.onDocumentCursorActive, !1); } /** * Called whenever there is mouse input at the document level * to determine if the cursor is active or not. * * @param {object} event */ onDocumentCursorActive(e) { this.showCursor(), clearTimeout(this.cursorInactiveTimeout), this.cursorInactiveTimeout = setTimeout(this.hideCursor.bind(this), this.Reveal.getConfig().hideCursorTime); } /** * Handles mouse wheel scrolling, throttled to avoid skipping * multiple slides. * * @param {object} event */ onDocumentMouseScroll(e) { if (Date.now() - this.lastMouseWheelStep > 1e3) { this.lastMouseWheelStep = Date.now(); let i = e.detail || -e.wheelDelta; i > 0 ? this.Reveal.next() : i < 0 && this.Reveal.prev(); } } } const Lt = (c, e) => { const i = document.createElement("script"); i.type = "text/javascript", i.async = !1, i.defer = !1, i.src = c, typeof e == "function" && (i.onload = i.onreadystatechange = (s) => { (s.type === "load" || /loaded|complete/.test(i.readyState)) && (i.onload = i.onreadystatechange = i.onerror = null, e()); }, i.onerror = (s) => { i.onload = i.onreadystatechange = i.onerror = null, e(new Error("Failed loading script: " + i.src + ` ` + s)); }); const t = document.querySelector("head"); t.insertBefore(i, t.lastChild); }; class Oi { constructor(e) { this.Reveal = e, this.state = "idle", this.registeredPlugins = {}, this.asyncDependencies = []; } /** * Loads reveal.js dependencies, registers and * initializes plugins. * * Plugins are direct references to a reveal.js plugin * object that we register and initialize after any * synchronous dependencies have loaded. * * Dependencies are defined via the 'dependencies' config * option and will be loaded prior to starting reveal.js. * Some dependencies may have an 'async' flag, if so they * will load after reveal.js has been started up. */ load(e, i) { return this.state = "loading", e.forEach(this.registerPlugin.bind(this)), new Promise((t) => { let s = [], n = 0; if (i.forEach((r) => { (!r.condition || r.condition()) && (r.async ? this.asyncDependencies.push(r) : s.push(r)); }), s.length) { n = s.length; const r = (o) => { o && typeof o.callback == "function" && o.callback(), --n === 0 && this.initPlugins().then(t); }; s.forEach((o) => { typeof o.id == "string" ? (this.registerPlugin(o), r(o)) : typeof o.src == "string" ? Lt(o.src, () => r(o)) : (console.warn("Unrecognized plugin format", o), r()); }); } else this.initPlugins().then(t); }); } /** * Initializes our plugins and waits for them to be ready * before proceeding. */ initPlugins() { return new Promise((e) => { let i = Object.values(this.registeredPlugins), t = i.length; if (t === 0) this.loadAsync().then(e); else { let s, n = () => { --t === 0 ? this.loadAsync().then(e) : s(); }, r = 0; s = () => { let o = i[r++]; if (typeof o.init == "function") { let h = o.init(this.Reveal); h && typeof h.then == "function" ? h.then(n) : n(); } else n(); }, s(); } }); } /** * Loads all async reveal.js dependencies. */ loadAsync() { return this.state = "loaded", this.asyncDependencies.length && this.asyncDependencies.forEach((e) => { Lt(e.src, e.callback); }), Promise.resolve(); } /** * Registers a new plugin with this reveal.js instance. * * reveal.js waits for all registered plugins to initialize * before considering itself ready, as long as the plugin * is registered before calling `Reveal.initialize()`. */ registerPlugin(e) { arguments.length === 2 && typeof arguments[0] == "string" ? (e = arguments[1], e.id = arguments[0]) : typeof e == "function" && (e = e()); let i = e.id; typeof i != "string" ? console.warn("Unrecognized plugin format; can't find plugin.id", e) : this.registeredPlugins[i] === void 0 ? (this.registeredPlugins[i] = e, this.state === "loaded" && typeof e.init == "function" && e.init(this.Reveal)) : console.warn('reveal.js: "' + i + '" plugin has already been registered'); } /** * Checks if a specific plugin has been registered. * * @param {String} id Unique plugin identifier */ hasPlugin(e) { return !!this.registeredPlugins[e]; } /** * Returns the specific plugin instance, if a plugin * with the given ID has been registered. * * @param {String} id Unique plugin identifier */ getPlugin(e) { return this.registeredPlugins[e]; } getRegisteredPlugins() { return this.registeredPlugins; } destroy() { Object.values(this.registeredPlugins).forEach((e) => { typeof e.destroy == "function" && e.destroy(); }), this.registeredPlugins = {}, this.asyncDependencies = []; } } class qi { constructor(e) { this.Reveal = e, this.onSlidesClicked = this.onSlidesClicked.bind(this), this.iframeTriggerSelector = null, this.mediaTriggerSelector = "[data-preview-image], [data-preview-video]", this.stateProps = ["previewIframe", "previewImage", "previewVideo", "previewFit"], this.state = {}; } update() { this.Reveal.getConfig().previewLinks ? this.iframeTriggerSelector = "a[href]:not([data-preview-link=false]), [data-preview-link]:not(a):not([data-preview-link=false])" : this.iframeTriggerSelector = "[data-preview-link]:not([data-preview-link=false])"; const e = this.Reveal.getSlidesElement().querySelectorAll(this.iframeTriggerSelector).length > 0, i = this.Reveal.getSlidesElement().querySelectorAll(this.mediaTriggerSelector).length > 0; e || i ? this.Reveal.getSlidesElement().addEventListener("click", this.onSlidesClicked, !1) : this.Reveal.getSlidesElement().removeEventListener("click", this.onSlidesClicked, !1); } createOverlay(e) { this.dom = document.createElement("div"), this.dom.classList.add("r-overlay"), this.dom.classList.add(e), this.viewport = document.createElement("div"), this.viewport.classList.add("r-overlay-viewport"), this.dom.appendChild(this.viewport), this.Reveal.getRevealElement().appendChild(this.dom); } /** * Opens a lightbox that previews the target URL. * * @param {string} url - url for lightbox iframe src */ previewIframe(e) { this.close(), this.state = { previewIframe: e }, this.createOverlay("r-overlay-preview"), this.dom.dataset.state = "loading", this.viewport.innerHTML = `
Unable to load iframe. This is likely due to the site's policy (x-frame-options).
`, this.dom.querySelector("iframe").addEventListener("load", (i) => { this.dom.dataset.state = "loaded"; }, !1), this.dom.querySelector(".r-overlay-close").addEventListener("click", (i) => { this.close(), i.preventDefault(); }, !1), this.dom.querySelector(".r-overlay-external").addEventListener("click", (i) => { this.close(); }, !1), this.Reveal.dispatchEvent({ type: "previewiframe", data: { url: e } }); } /** * Opens a lightbox window that provides a larger view of the * given image/video. * * @param {string} url - url to the image/video to preview * @param {image|video} mediaType * @param {string} [fitMode] - the fit mode to use for the preview */ previewMedia(e, i, t) { if (i !== "image" && i !== "video") { console.warn("Please specify a valid media type to preview (image|video)"); return; } this.close(), t = t || "scale-down", this.createOverlay("r-overlay-preview"), this.dom.dataset.state = "loading", this.dom.dataset.previewFit = t, this.viewport.innerHTML = `
`; const s = this.dom.querySelector(".r-overlay-content"); if (i === "image") { this.state = { previewImage: e, previewFit: t }; const n = document.createElement("img", {}); n.src = e, s.appendChild(n), n.addEventListener("load", () => { this.dom.dataset.state = "loaded"; }, !1), n.addEventListener("error", () => { this.dom.dataset.state = "error", s.innerHTML = 'Unable to load image.'; }, !1), this.dom.style.cursor = "zoom-out", this.dom.addEventListener("click", (r) => { this.close(); }, !1), this.Reveal.dispatchEvent({ type: "previewimage", data: { url: e } }); } else if (i === "video") { this.state = { previewVideo: e, previewFit: t }; const n = document.createElement("video"); n.autoplay = this.dom.dataset.previewAutoplay !== "false", n.controls = this.dom.dataset.previewControls !== "false", n.loop = this.dom.dataset.previewLoop === "true", n.muted = this.dom.dataset.previewMuted === "true", n.playsInline = !0, n.src = e, s.appendChild(n), n.addEventListener("loadeddata", () => { this.dom.dataset.state = "loaded"; }, !1), n.addEventListener("error", () => { this.dom.dataset.state = "error", s.innerHTML = 'Unable to load video.'; }, !1), this.Reveal.dispatchEvent({ type: "previewvideo", data: { url: e } }); } else throw new Error("Please specify a valid media type to preview"); this.dom.querySelector(".r-overlay-close").addEventListener("click", (n) => { this.close(), n.preventDefault(); }, !1); } previewImage(e, i) { this.previewMedia(e, "image", i); } previewVideo(e, i) { this.previewMedia(e, "video", i); } /** * Open or close help overlay window. * * @param {Boolean} [override] Flag which overrides the * toggle logic and forcibly sets the desired state. True means * help is open, false means it's closed. */ toggleHelp(e) { typeof e == "boolean" ? e ? this.showHelp() : this.close() : this.dom ? this.close() : this.showHelp(); } /** * Opens an overlay window with help material. */ showHelp() { if (this.Reveal.getConfig().help) { this.close(), this.createOverlay("r-overlay-help"); let e = '

Keyboard Shortcuts

', i = this.Reveal.keyboard.getShortcuts(), t = this.Reveal.keyboard.getBindings(); e += ""; for (let s in i) e += ``; for (let s in t) t[s].key && t[s].description && (e += ``); e += "
KEYACTION
${s}${i[s]}
${t[s].key}${t[s].description}
", this.viewport.innerHTML = `
${e}
`, this.dom.querySelector(".r-overlay-close").addEventListener("click", (s) => { this.close(), s.preventDefault(); }, !1), this.Reveal.dispatchEvent({ type: "showhelp" }); } } isOpen() { return !!this.dom; } /** * Closes any currently open overlay. */ close() { return this.dom ? (this.dom.remove(), this.dom = null, this.state = {}, this.Reveal.dispatchEvent({ type: "closeoverlay" }), !0) : !1; } getState() { return this.state; } setState(e) { this.stateProps.every((i) => this.state[i] === e[i]) || (e.previewIframe ? this.previewIframe(e.previewIframe) : e.previewImage ? this.previewImage(e.previewImage, e.previewFit) : e.previewVideo ? this.previewVideo(e.previewVideo, e.previewFit) : this.close()); } onSlidesClicked(e) { const i = e.target, t = i.closest(this.iframeTriggerSelector), s = i.closest(this.mediaTriggerSelector); if (t) { if (e.metaKey || e.shiftKey || e.altKey) return; let n = t.getAttribute("href") || t.getAttribute("data-preview-link"); n && (this.previewIframe(n), e.preventDefault()); } else if (s) { if (s.hasAttribute("data-preview-image")) { let n = s.dataset.previewImage || s.getAttribute("src"); n && (this.previewImage(n, s.dataset.previewFit), e.preventDefault()); } else if (s.hasAttribute("data-preview-video")) { let n = s.dataset.previewVideo || s.getAttribute("src"); if (!n) { let r = s.querySelector("source"); r && (n = r.getAttribute("src")); } n && (this.previewVideo(n, s.dataset.previewFit), e.preventDefault()); } } } destroy() { this.close(); } } const Le = 40; class Ui { constructor(e) { this.Reveal = e, this.touchStartX = 0, this.touchStartY = 0, this.touchStartCount = 0, this.touchCaptured = !1, this.onPointerDown = this.onPointerDown.bind(this), this.onPointerMove = this.onPointerMove.bind(this), this.onPointerUp = this.onPointerUp.bind(this), this.onTouchStart = this.onTouchStart.bind(this), this.onTouchMove = this.onTouchMove.bind(this), this.onTouchEnd = this.onTouchEnd.bind(this); } /** * */ bind() { let e = this.Reveal.getRevealElement(); "onpointerdown" in window ? (e.addEventListener("pointerdown", this.onPointerDown, !1), e.addEventListener("pointermove", this.onPointerMove, !1), e.addEventListener("pointerup", this.onPointerUp, !1)) : window.navigator.msPointerEnabled ? (e.addEventListener("MSPointerDown", this.onPointerDown, !1), e.addEventListener("MSPointerMove", this.onPointerMove, !1), e.addEventListener("MSPointerUp", this.onPointerUp, !1)) : (e.addEventListener("touchstart", this.onTouchStart, !1), e.addEventListener("touchmove", this.onTouchMove, !1), e.addEventListener("touchend", this.onTouchEnd, !1)); } /** * */ unbind() { let e = this.Reveal.getRevealElement(); e.removeEventListener("pointerdown", this.onPointerDown, !1), e.removeEventListener("pointermove", this.onPointerMove, !1), e.removeEventListener("pointerup", this.onPointerUp, !1), e.removeEventListener("MSPointerDown", this.onPointerDown, !1), e.removeEventListener("MSPointerMove", this.onPointerMove, !1), e.removeEventListener("MSPointerUp", this.onPointerUp, !1), e.removeEventListener("touchstart", this.onTouchStart, !1), e.removeEventListener("touchmove", this.onTouchMove, !1), e.removeEventListener("touchend", this.onTouchEnd, !1); } /** * Checks if the target element prevents the triggering of * swipe navigation. */ isSwipePrevented(e) { if (Pe(e, "video[controls], audio[controls]")) return !0; for (; e && typeof e.hasAttribute == "function"; ) { if (e.hasAttribute("data-prevent-swipe")) return !0; e = e.parentNode; } return !1; } /** * Handler for the 'touchstart' event, enables support for * swipe and pinch gestures. * * @param {object} event */ onTouchStart(e) { if (this.touchCaptured = !1, this.isSwipePrevented(e.target)) return !0; this.touchStartX = e.touches[0].clientX, this.touchStartY = e.touches[0].clientY, this.touchStartCount = e.touches.length; } /** * Handler for the 'touchmove' event. * * @param {object} event */ onTouchMove(e) { if (this.isSwipePrevented(e.target)) return !0; let i = this.Reveal.getConfig(); if (this.touchCaptured) It && e.preventDefault(); else { this.Reveal.onUserInput(e); let t = e.touches[0].clientX, s = e.touches[0].clientY; if (e.touches.length === 1 && this.touchStartCount !== 2) { let n = this.Reveal.availableRoutes({ includeFragments: !0 }), r = t - this.touchStartX, o = s - this.touchStartY; r > Le && Math.abs(r) > Math.abs(o) ? (this.touchCaptured = !0, i.navigationMode === "linear" ? i.rtl ? this.Reveal.next() : this.Reveal.prev() : this.Reveal.left()) : r < -Le && Math.abs(r) > Math.abs(o) ? (this.touchCaptured = !0, i.navigationMode === "linear" ? i.rtl ? this.Reveal.prev() : this.Reveal.next() : this.Reveal.right()) : o > Le && n.up ? (this.touchCaptured = !0, i.navigationMode === "linear" ? this.Reveal.prev() : this.Reveal.up()) : o < -Le && n.down && (this.touchCaptured = !0, i.navigationMode === "linear" ? this.Reveal.next() : this.Reveal.down()), i.embedded ? (this.touchCaptured || this.Reveal.isVerticalSlide()) && e.preventDefault() : e.preventDefault(); } } } /** * Handler for the 'touchend' event. * * @param {object} event */ onTouchEnd(e) { this.touchCaptured = !1; } /** * Convert pointer down to touch start. * * @param {object} event */ onPointerDown(e) { (e.pointerType === e.MSPOINTER_TYPE_TOUCH || e.pointerType === "touch") && (e.touches = [{ clientX: e.clientX, clientY: e.clientY }], this.onTouchStart(e)); } /** * Convert pointer move to touch move. * * @param {object} event */ onPointerMove(e) { (e.pointerType === e.MSPOINTER_TYPE_TOUCH || e.pointerType === "touch") && (e.touches = [{ clientX: e.clientX, clientY: e.clientY }], this.onTouchMove(e)); } /** * Convert pointer up to touch end. * * @param {object} event */ onPointerUp(e) { (e.pointerType === e.MSPOINTER_TYPE_TOUCH || e.pointerType === "touch") && (e.touches = [{ clientX: e.clientX, clientY: e.clientY }], this.onTouchEnd(e)); } } const qe = "focus", Pt = "blur"; class Wi { constructor(e) { this.Reveal = e, this.onRevealPointerDown = this.onRevealPointerDown.bind(this), this.onDocumentPointerDown = this.onDocumentPointerDown.bind(this); } /** * Called when the reveal.js config is updated. */ configure(e, i) { e.embedded ? this.blur() : (this.focus(), this.unbind()); } bind() { this.Reveal.getConfig().embedded && this.Reveal.getRevealElement().addEventListener("pointerdown", this.onRevealPointerDown, !1); } unbind() { this.Reveal.getRevealElement().removeEventListener("pointerdown", this.onRevealPointerDown, !1), document.removeEventListener("pointerdown", this.onDocumentPointerDown, !1); } focus() { this.state !== qe && (this.Reveal.getRevealElement().classList.add("focused"), document.addEventListener("pointerdown", this.onDocumentPointerDown, !1)), this.state = qe; } blur() { this.state !== Pt && (this.Reveal.getRevealElement().classList.remove("focused"), document.removeEventListener("pointerdown", this.onDocumentPointerDown, !1)), this.state = Pt; } isFocused() { return this.state === qe; } destroy() { this.Reveal.getRevealElement().classList.remove("focused"); } onRevealPointerDown(e) { this.focus(); } onDocumentPointerDown(e) { let i = V(e.target, ".reveal"); (!i || i !== this.Reveal.getRevealElement()) && this.blur(); } } class ji { constructor(e) { this.Reveal = e; } render() { this.element = document.createElement("div"), this.element.className = "speaker-notes", this.element.setAttribute("data-prevent-swipe", ""), this.element.setAttribute("tabindex", "0"), this.Reveal.getRevealElement().appendChild(this.element); } /** * Called when the reveal.js config is updated. */ configure(e, i) { e.showNotes && this.element.setAttribute("data-layout", typeof e.showNotes == "string" ? e.showNotes : "inline"); } /** * Pick up notes from the current slide and display them * to the viewer. * * @see {@link config.showNotes} */ update() { this.Reveal.getConfig().showNotes && this.element && this.Reveal.getCurrentSlide() && !this.Reveal.isScrollView() && !this.Reveal.isPrintView() && (this.element.innerHTML = this.getSlideNotes() || 'No notes on this slide.'); } /** * Updates the visibility of the speaker notes sidebar that * is used to share annotated slides. The notes sidebar is * only visible if showNotes is true and there are notes on * one or more slides in the deck. */ updateVisibility() { this.Reveal.getConfig().showNotes && this.hasNotes() && !this.Reveal.isScrollView() && !this.Reveal.isPrintView() ? this.Reveal.getRevealElement().classList.add("show-notes") : this.Reveal.getRevealElement().classList.remove("show-notes"); } /** * Checks if there are speaker notes for ANY slide in the * presentation. */ hasNotes() { return this.Reveal.getSlidesElement().querySelectorAll("[data-notes], aside.notes").length > 0; } /** * Checks if this presentation is running inside of the * speaker notes window. * * @return {boolean} */ isSpeakerNotesWindow() { return !!window.location.search.match(/receiver/gi); } /** * Retrieves the speaker notes from a slide. Notes can be * defined in two ways: * 1. As a data-notes attribute on the slide
* 2. With