Explorar o código

feat(slides): upgrade reveal.js from v3.8.0 to v4.1.0 (#2018)

koutbo6 %!s(int64=4) %!d(string=hai) anos
pai
achega
de6860b8b3

+ 3 - 0
.gitignore

@@ -10,3 +10,6 @@ node_modules/
 
 # Hugo
 resources/
+
+# Mac
+.DS_Store

+ 23 - 0
wowchemy/archetypes/slides/index.md

@@ -12,7 +12,30 @@ slides:
   theme: black
   # Choose a code highlighting style (if highlighting enabled in `params.toml`)
   #   Light style: github. Dark style: dracula (default).
+  #   Available highlight themes listed in: https://highlightjs.org/static/demo/
+  #   Use lower case names and replace space with hyphen '-'
   highlight_style: dracula
+
+  diagram: true
+  diagram_options:
+    # Mermaid diagram themes include: default,base,dark,neutral,forest
+    theme: base
+
+  # RevealJS slide options.
+  # Options are named using the snake case equivalent of those in the RevealJS docs.
+  reveal_options:
+    controls: true
+    progress: true
+    slide_number: c/t  # true | false | h.v | h/v | c | c/t
+    center: true
+    rtl: false
+    mouse_wheel: true
+    transition: fade  # none/fade/slide/convex/concave/zoom
+    transitionSpeed: default  # default/fast/slow
+    background_transition: slide  # none/fade/slide/convex/concave/zoom
+    touch: true
+    loop: false
+    menu_enabled: true
 ---
 
 # Title

+ 0 - 31
wowchemy/assets/css/reveal.css

@@ -1,31 +0,0 @@
-/*************************************************
- *  Reveal JS
- **************************************************/
-
-/* This is a copy of MathJax's `.mjx-chtml` with font-family added to override `.reveal span`. */
-/* See https://github.com/hakimel/reveal.js/issues/1924 */
-.reveal span.mjx-chtml {
-  display: inline-block;
-  line-height: 0;
-  text-indent: 0;
-  text-align: left;
-  text-transform: none;
-  font-style: normal;
-  font-weight: normal;
-  font-size: 100%;
-  font-size-adjust: none;
-  letter-spacing: normal;
-  word-wrap: normal;
-  word-spacing: normal;
-  white-space: nowrap;
-  float: none;
-  direction: ltr;
-  max-width: none;
-  max-height: none;
-  min-width: 0;
-  min-height: 0;
-  border: 0;
-  margin: 0;
-  padding: 1px 0;
-  font-family: MJXc-TeX-math-I,MJXc-TeX-math-Ix,MJXc-TeX-math-Iw;
-}

+ 148 - 0
wowchemy/assets/js/wowchemy-slides.js

@@ -0,0 +1,148 @@
+/*
+  global RevealMarkdown, RevealHighlight, RevealSearch, RevealNotes, RevealMath, RevealZoom, Reveal, mermaid, RevealMenu
+*/
+
+import * as params from '@params';
+
+import {fixMermaid} from './wowchemy-utils';
+
+// Enable core slide features.
+var enabledPlugins = [RevealMarkdown, RevealHighlight, RevealSearch, RevealNotes, RevealMath, RevealZoom];
+
+const isObject = function (o) {
+  return o === Object(o) && !isArray(o) && typeof o !== 'function';
+};
+
+const isArray = function (a) {
+  return Array.isArray(a);
+};
+
+const toCamelCase = function (s) {
+  return s.replace(/([-_][a-z])/gi, function (term) {
+    return term.toUpperCase().replace('-', '').replace('_', '');
+  });
+};
+
+const keysToCamelCase = function (o) {
+  if (isObject(o)) {
+    const n = {};
+
+    Object.keys(o).forEach(function (k) {
+      n[toCamelCase(k)] = keysToCamelCase(o[k]);
+    });
+
+    return n;
+  } else if (isArray(o)) {
+    return o.map(function (i) {
+      return keysToCamelCase(i);
+    });
+  }
+
+  return o;
+};
+
+// reveal configurations can be included in front matter under slides.reveal
+var pluginOptions = {};
+if (typeof params.slides.reveal_options !== 'undefined') {
+  pluginOptions = params.slides.reveal_options;
+}
+
+pluginOptions = keysToCamelCase(pluginOptions);
+
+//enable menu by default if not set
+if (typeof pluginOptions.menu_enabled !== 'undefined') {
+  pluginOptions.menu_enabled = true;
+}
+
+// configure menu if enabled
+if (pluginOptions.menu_enabled) {
+  enabledPlugins.push(RevealMenu);
+}
+
+pluginOptions['plugins'] = enabledPlugins;
+
+Reveal.initialize(pluginOptions);
+
+// The following functions are used to render Mermaid diagrams
+// after Reveal slides have been successfully loaded
+// since content of slides is lazy loaded, if diagrams are
+// rendered at start of presentation their sizes will be off
+// get all slides that are:
+// 1- data loaded
+// 2- display set to block
+// 3- has a mermaid element that is not processed (data-processed dne)
+function mermaidSlidesReadyToRender(mslide) {
+  var diag = mslide.querySelector('.mermaid');
+  if (diag) {
+    var background = mslide.slideBackgroundElement;
+    // render if we are 1 slide away horizontally
+    // current visible slide index
+    var currentHorizontalIndex = Reveal.getState()['indexh'];
+
+    // mermaid slide index
+    var diagramSlideIndex = Reveal.getIndices(mslide)['h'];
+    if (
+      // find slides with non-rendered mermaid tags
+      // these will not have the attribute data-processed
+      !diag.hasAttribute('data-processed') &&
+      // check also that reveal slide is already loaded
+      // reveal slides seem to be lazily loaded
+      // things could be easier if reveal had a slide-loaded event
+      background.hasAttribute('data-loaded') &&
+      // loaded slides must also have the display attribute set to block
+      background.style.display === 'block' &&
+      // render diagrams that are 1 slide away
+      diagramSlideIndex - currentHorizontalIndex <= 1
+    )
+      return mslide;
+  }
+  return null;
+}
+
+function renderMermaidSlides() {
+  // find all slides with diagrams that are ready to render
+  var diagramSlides = Reveal.getSlides().filter(mermaidSlidesReadyToRender);
+
+  // render the diagram for each slide with ready to render diagrams
+  diagramSlides.forEach(function (item) {
+    mermaid.init(item.querySelector('.mermaid'));
+  });
+}
+
+// render mermaid slides for slides that are ready
+Reveal.on('slidechanged', function () {
+  renderMermaidSlides();
+});
+
+// render mermaid slides for slides that are ready on startup
+Reveal.on('Ready', function () {
+  if (Reveal.isReady()) {
+    renderMermaidSlides();
+  }
+});
+
+// Disable Mermaid by default.
+if (typeof params.slides.diagram === 'undefined') {
+  params.slides.diagram = false;
+}
+
+// Configure Mermaid only if diagrams are enabled.
+if (params.slides.diagram) {
+  //mermaid options
+  // mermaid: front matter configuration can be used to set mermaid options
+  // You can also use directives (see mermaid documentation)
+  var mermaidOptions = {};
+  if (typeof params.slides.diagram_options !== 'undefined') {
+    mermaidOptions = params.slides.diagram_options;
+  }
+
+  // `startOnLoad` must be false since diagrams are lazily rendered.
+  mermaidOptions['startOnLoad'] = false;
+
+  mermaid.initialize(mermaidOptions);
+
+  // Fix Mermaid conflict with Hightlight JS.
+  document.addEventListener('DOMContentLoaded', function () {
+    fixMermaid();
+  });
+}

+ 2 - 2
wowchemy/assets/js/wowchemy-theming.js

@@ -187,7 +187,7 @@ function renderThemeVariation(isDarkTheme, themeMode = 2, init = false) {
       console.debug('Initializing Mermaid with light theme');
       if (init) {
         /** @namespace window.mermaid **/
-        window.mermaid.initialize({theme: 'default', securityLevel: 'loose'});
+        window.mermaid.initialize({startOnLoad: true, theme: 'default', securityLevel: 'loose'});
       } else {
         // Have to reload to re-initialise Mermaid with the new theme and re-parse the Mermaid code blocks.
         location.reload();
@@ -213,7 +213,7 @@ function renderThemeVariation(isDarkTheme, themeMode = 2, init = false) {
       console.debug('Initializing Mermaid with dark theme');
       if (init) {
         /** @namespace window.mermaid **/
-        window.mermaid.initialize({theme: 'dark', securityLevel: 'loose'});
+        window.mermaid.initialize({startOnLoad: true, theme: 'dark', securityLevel: 'loose'});
       } else {
         // Have to reload to re-initialise Mermaid with the new theme and re-parse the Mermaid code blocks.
         location.reload();

+ 2 - 2
wowchemy/data/assets.toml

@@ -57,8 +57,8 @@
   sri = "sha512-I7w3ZdSFzw5j3jU3ZkNikBNeIrl3i+hEuEdwNmqUJvwNcaBUNcijnP2gd9DtGlgVYDplfjGoD8vTNsID+lCjqg=="
   url = "https://cdnjs.cloudflare.com/ajax/libs/anchor-js/%s/anchor.min.js"
 [js.mermaid]
-  version = "8.8.0"
-  sri = "sha512-ja+hSBi4JDtjSqc4LTBsSwuBT3tdZ3oKYKd07lTVYmCnTCor56AnRql00ssqnTOR9Ss4gOP/ROGB3SfcJnZkeg=="
+  version = "8.8.4"
+  sri = "sha512-as1BF4+iHZ3BVO6LLDQ7zrbvTXM+c/1iZ1qII/c3c4L8Rn5tHLpFUtpaEtBNS92f+xGsCzsD7b62XP3XYap6oA=="
   url = "https://cdnjs.cloudflare.com/ajax/libs/mermaid/%s/mermaid.min.js"
 [js.lazysizes]
   version = "5.2.2"

+ 26 - 38
wowchemy/layouts/slides/baseof.html

@@ -5,12 +5,13 @@
   {{ .Scratch.Set "media_dir" $media_dir }}
 
   {{ $css := site.Data.assets.css }}
-  {{ $cdn_url_reveal := "https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.8.0" }}
+  {{ $cdn_url_reveal := "https://cdnjs.cloudflare.com/ajax/libs/reveal.js/4.1.0" }}
+  {{ $js := site.Data.assets.js }}
 
   <meta charset="utf-8">
   <meta name="viewport" content="width=device-width, initial-scale=1">
   <meta http-equiv="X-UA-Compatible" content="IE=edge">
-  <meta name="generator" content="Source Themes Academic {{ site.Data.academic.version }}">
+  <meta name="generator" content="Wowchemy {{ site.Data.wowchemy.version }} for Hugo">
 
   {{ if site.Home.OutputFormats.Get "WebAppManifest" }}
   <link rel="manifest" href="{{ "index.webmanifest" | relLangURL }}">
@@ -23,55 +24,42 @@
 
   <title>{{ .Title }} | {{ site.Title }}</title>
 
-  <link rel="stylesheet" href="{{ $cdn_url_reveal }}/css/reveal.min.css">
+  <link rel="stylesheet" href="{{ $cdn_url_reveal }}/reveal.min.css">
   {{- $theme := $.Param "slides.theme" | default "black" -}}
-  <link rel="stylesheet" href="{{ $cdn_url_reveal }}/css/theme/{{ $theme }}.min.css">
+  <link rel="stylesheet" href="{{ $cdn_url_reveal }}/theme/{{ $theme }}.min.css">
 
   {{- $highlight_style := $.Param "slides.highlight_style" | default "dracula" -}}
-  {{ printf "<link rel=\"stylesheet\" href=\"%s\" crossorigin=\"anonymous\">" (printf $css.highlight.url $css.highlight.version $highlight_style) | safeHTML }}
+  {{ printf "<link rel=\"stylesheet\" href=\"%s\" crossorigin=\"anonymous\" id=\"highlight-theme\">" (printf $css.highlight.url $css.highlight.version $highlight_style) | safeHTML }}
 
-  {{ $css := resources.Get "css/reveal.css" }}
   {{ $css_custom := resources.Get "css/reveal_custom.css" }}
-  {{ $style := slice $css $css_custom | resources.Concat "css/reveal_custom.css" | resources.Minify }}
+  {{ $style := slice $css_custom | resources.Concat "css/reveal_custom.css" | resources.Minify }}
   <link rel="stylesheet" href="{{ $style.RelPermalink }}">
-
-  <!-- Printing and PDF exports -->
-  <script>
-    var link = document.createElement( 'link' );
-    link.rel = 'stylesheet';
-    link.type = 'text/css';
-    link.href = window.location.search.match( /print-pdf/gi ) ? '{{ $cdn_url_reveal }}/css/print/pdf.css' : '{{ $cdn_url_reveal }}/css/print/paper.css';
-    document.getElementsByTagName( 'head' )[0].appendChild( link );
-  </script>
-
 </head>
 <body>
 
   {{ block "main" . }}{{ end }}
 
-  <script src="{{ $cdn_url_reveal }}/js/reveal.min.js"></script>
+  <script src="{{ $cdn_url_reveal }}/reveal.min.js" integrity="sha512-Xu/cezKABTI81MGnaBm64vdiS7XkttHeYGOgr2Mdga0bTplSBGongLq2lhK2HwL79wefKM0u4uTCLD0ha1sRzQ==" crossorigin="anonymous"></script>
+  <script src="{{ $cdn_url_reveal }}/plugin/markdown/markdown.min.js" integrity="sha512-eZZqO4ECmVvGhCt+6VZ7ian2bCu4S6yrjSFH9fXLY1zTokpAWsxAxQwM4x6+7G+G4ha5tFIe0jY0XjpBUqS49Q==" crossorigin="anonymous"></script>
+  <script src="{{ $cdn_url_reveal }}/plugin/highlight/highlight.min.js" integrity="sha512-NA5UCab7xDKQPXGsmIp8iEuId5BAKGPiqHZsZQcBuySfp1n3dZrwBDKpPNL23Db5upay1nULxU14JV1ggFOD2A==" crossorigin="anonymous"></script>
+  <script src="{{ $cdn_url_reveal }}/plugin/notes/notes.min.js" integrity="sha512-FYeeQscKqibmYKr0+nE2+fN5prBsFwgjsBVwkrA88O6mN2+ai6EvRkSi6txuhXlWsyK1MUfoV+94+q6HLouJSQ==" crossorigin="anonymous"></script>
+  <script src="{{ $cdn_url_reveal }}/plugin/search/search.min.js" integrity="sha512-2yh3Y2gEdboEnZb9d0QZP05N3e0jTkcjhbG9xYL97mbnZ53IXzF5R2TCTmSrtuspDyJ5FWBSh+8izjiGjVdLWw==" crossorigin="anonymous"></script>
+  <script src="{{ $cdn_url_reveal }}/plugin/math/math.min.js" integrity="sha512-FUzQmRJDLL111zqJg/vN1YzQFQtZNWfBH2VaOiv30dXRXgaTRn3F/Ibda92klSAVjfz3Q9UqS88R4RF4Ip01fQ==" crossorigin="anonymous"></script>
+  <script src="{{ $cdn_url_reveal }}/plugin/zoom/zoom.min.js" integrity="sha512-zPYOPjR7Hg9BPUYkfNlvVtrC37QlYwq/7mI42VTuXKTcNBp7QvMfuqUTMesOf74OrZ3AEdxJGndGSrJG9O2j5Q==" crossorigin="anonymous"></script>
 
-  <script>
-    window.revealPlugins = { dependencies: [
-      // Interpret Markdown in <section> elements.
-      { src: '{{ $cdn_url_reveal }}/plugin/markdown/marked.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
-      { src: '{{ $cdn_url_reveal }}/plugin/markdown/markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
-      // Enable code highlighting.
-      { src: '{{ $cdn_url_reveal }}/plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } },
-      // Enable zooming.
-      { src: '{{ $cdn_url_reveal }}/plugin/zoom-js/zoom.js', async: true },
-      // Enable math.
-      { src: '{{ $cdn_url_reveal }}/plugin/math/math.js', async: true },
-      // Enable exporting deck to PDF.
-      { src: '{{ $cdn_url_reveal }}/plugin/print-pdf/print-pdf.js', async: true },
-      // Enable speaker notes. Notes plugin must be loaded locally as CDN is missing `notes.html`.
-      { src: '{{ "js/vendor/reveal.js/plugin/notes/notes.js" | relURL }}', async: true }
-    ]};
+  {{/* Third-party Reveal plugins. */}}
+  {{ if $.Param "slides.reveal_options.menu_enabled" | default true}}
+    <script src="https://cdn.jsdelivr.net/npm/reveal.js-menu@2.1.0/plugin.js" integrity="sha256-M6JwAjnRAWmi+sbXURR/yAhWZKYhAw7YXnnLvIxrdGs=" crossorigin="anonymous"></script>
+    <script src="https://cdn.jsdelivr.net/npm/reveal.js-menu@2.1.0/menu.js" integrity="sha256-l14dklFcW5mWar6w/9KaW0fWVerf3mYr7Wt0+rXzFAA=" crossorigin="anonymous"></script>
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/reveal.js-menu@2.1.0/menu.css" integrity="sha256-0fU8HKLaTjgzfaV9CgSqbsN8ilA3zo6zK1M6rlgULd8=" crossorigin="anonymous">
+  {{ end }}
+
+  {{ if $.Param "slides.diagram" | default false}}
+    {{ printf "<script src=\"%s\" integrity=\"%s\" crossorigin=\"anonymous\" title=\"mermaid\"></script>" (printf $js.mermaid.url $js.mermaid.version) $js.mermaid.sri | safeHTML }}
+  {{ end }}
 
-    let revealDefaults = { center: true, controls: true, history: true, progress: true, transition: 'slide', mouseWheel: true };
-    let revealOptions = Object.assign({}, revealDefaults, revealPlugins);
-    Reveal.initialize(revealOptions);
-  </script>
+  {{ $slidejs := resources.Get "js/wowchemy-slides.js" | js.Build (dict "params" (dict "slides" $.Params.slides )) }}
+  <script src="{{ $slidejs.RelPermalink }}"></script>
 
 </body>
 </html>

+ 0 - 834
wowchemy/static/js/vendor/reveal.js/plugin/notes/notes.html

@@ -1,834 +0,0 @@
-<!doctype html>
-<html lang="en">
-<head>
-  <meta charset="utf-8">
-
-  <title>Speaker Notes</title>
-
-  <style>
-    body {
-      font-family: Helvetica;
-      font-size: 18px;
-    }
-
-    #current-slide,
-    #upcoming-slide,
-    #speaker-controls {
-      padding: 6px;
-      box-sizing: border-box;
-      -moz-box-sizing: border-box;
-    }
-
-    #current-slide iframe,
-    #upcoming-slide iframe {
-      width: 100%;
-      height: 100%;
-      border: 1px solid #ddd;
-    }
-
-    #current-slide .label,
-    #upcoming-slide .label {
-      position: absolute;
-      top: 10px;
-      left: 10px;
-      z-index: 2;
-    }
-
-    #connection-status {
-      position: absolute;
-      top: 0;
-      left: 0;
-      width: 100%;
-      height: 100%;
-      z-index: 20;
-      padding: 30% 20% 20% 20%;
-      font-size: 18px;
-      color: #222;
-      background: #fff;
-      text-align: center;
-      box-sizing: border-box;
-      line-height: 1.4;
-    }
-
-    .overlay-element {
-      height: 34px;
-      line-height: 34px;
-      padding: 0 10px;
-      text-shadow: none;
-      background: rgba( 220, 220, 220, 0.8 );
-      color: #222;
-      font-size: 14px;
-    }
-
-    .overlay-element.interactive:hover {
-      background: rgba( 220, 220, 220, 1 );
-    }
-
-    #current-slide {
-      position: absolute;
-      width: 60%;
-      height: 100%;
-      top: 0;
-      left: 0;
-      padding-right: 0;
-    }
-
-    #upcoming-slide {
-      position: absolute;
-      width: 40%;
-      height: 40%;
-      right: 0;
-      top: 0;
-    }
-
-    /* Speaker controls */
-    #speaker-controls {
-      position: absolute;
-      top: 40%;
-      right: 0;
-      width: 40%;
-      height: 60%;
-      overflow: auto;
-      font-size: 18px;
-    }
-
-    .speaker-controls-time.hidden,
-    .speaker-controls-notes.hidden {
-      display: none;
-    }
-
-    .speaker-controls-time .label,
-    .speaker-controls-pace .label,
-    .speaker-controls-notes .label {
-      text-transform: uppercase;
-      font-weight: normal;
-      font-size: 0.66em;
-      color: #666;
-      margin: 0;
-    }
-
-    .speaker-controls-time, .speaker-controls-pace {
-      border-bottom: 1px solid rgba( 200, 200, 200, 0.5 );
-      margin-bottom: 10px;
-      padding: 10px 16px;
-      padding-bottom: 20px;
-      cursor: pointer;
-    }
-
-    .speaker-controls-time .reset-button {
-      opacity: 0;
-      float: right;
-      color: #666;
-      text-decoration: none;
-    }
-    .speaker-controls-time:hover .reset-button {
-      opacity: 1;
-    }
-
-    .speaker-controls-time .timer,
-    .speaker-controls-time .clock {
-      width: 50%;
-    }
-
-    .speaker-controls-time .timer,
-    .speaker-controls-time .clock,
-    .speaker-controls-time .pacing .hours-value,
-    .speaker-controls-time .pacing .minutes-value,
-    .speaker-controls-time .pacing .seconds-value {
-      font-size: 1.9em;
-    }
-
-    .speaker-controls-time .timer {
-      float: left;
-    }
-
-    .speaker-controls-time .clock {
-      float: right;
-      text-align: right;
-    }
-
-    .speaker-controls-time span.mute {
-      opacity: 0.3;
-    }
-
-    .speaker-controls-time .pacing-title {
-      margin-top: 5px;
-    }
-
-    .speaker-controls-time .pacing.ahead {
-      color: blue;
-    }
-
-    .speaker-controls-time .pacing.on-track {
-      color: green;
-    }
-
-    .speaker-controls-time .pacing.behind {
-      color: red;
-    }
-
-    .speaker-controls-notes {
-      padding: 10px 16px;
-    }
-
-    .speaker-controls-notes .value {
-      margin-top: 5px;
-      line-height: 1.4;
-      font-size: 1.2em;
-    }
-
-    /* Layout selector */
-    #speaker-layout {
-      position: absolute;
-      top: 10px;
-      right: 10px;
-      color: #222;
-      z-index: 10;
-    }
-    #speaker-layout select {
-      position: absolute;
-      width: 100%;
-      height: 100%;
-      top: 0;
-      left: 0;
-      border: 0;
-      box-shadow: 0;
-      cursor: pointer;
-      opacity: 0;
-
-      font-size: 1em;
-      background-color: transparent;
-
-      -moz-appearance: none;
-      -webkit-appearance: none;
-      -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
-    }
-
-    #speaker-layout select:focus {
-      outline: none;
-      box-shadow: none;
-    }
-
-    .clear {
-      clear: both;
-    }
-
-    /* Speaker layout: Wide */
-    body[data-speaker-layout="wide"] #current-slide,
-    body[data-speaker-layout="wide"] #upcoming-slide {
-      width: 50%;
-      height: 45%;
-      padding: 6px;
-    }
-
-    body[data-speaker-layout="wide"] #current-slide {
-      top: 0;
-      left: 0;
-    }
-
-    body[data-speaker-layout="wide"] #upcoming-slide {
-      top: 0;
-      left: 50%;
-    }
-
-    body[data-speaker-layout="wide"] #speaker-controls {
-      top: 45%;
-      left: 0;
-      width: 100%;
-      height: 50%;
-      font-size: 1.25em;
-    }
-
-    /* Speaker layout: Tall */
-    body[data-speaker-layout="tall"] #current-slide,
-    body[data-speaker-layout="tall"] #upcoming-slide {
-      width: 45%;
-      height: 50%;
-      padding: 6px;
-    }
-
-    body[data-speaker-layout="tall"] #current-slide {
-      top: 0;
-      left: 0;
-    }
-
-    body[data-speaker-layout="tall"] #upcoming-slide {
-      top: 50%;
-      left: 0;
-    }
-
-    body[data-speaker-layout="tall"] #speaker-controls {
-      padding-top: 40px;
-      top: 0;
-      left: 45%;
-      width: 55%;
-      height: 100%;
-      font-size: 1.25em;
-    }
-
-    /* Speaker layout: Notes only */
-    body[data-speaker-layout="notes-only"] #current-slide,
-    body[data-speaker-layout="notes-only"] #upcoming-slide {
-      display: none;
-    }
-
-    body[data-speaker-layout="notes-only"] #speaker-controls {
-      padding-top: 40px;
-      top: 0;
-      left: 0;
-      width: 100%;
-      height: 100%;
-      font-size: 1.25em;
-    }
-
-    @media screen and (max-width: 1080px) {
-      body[data-speaker-layout="default"] #speaker-controls {
-        font-size: 16px;
-      }
-    }
-
-    @media screen and (max-width: 900px) {
-      body[data-speaker-layout="default"] #speaker-controls {
-        font-size: 14px;
-      }
-    }
-
-    @media screen and (max-width: 800px) {
-      body[data-speaker-layout="default"] #speaker-controls {
-        font-size: 12px;
-      }
-    }
-
-  </style>
-</head>
-
-<body>
-
-<div id="connection-status">Loading speaker view...</div>
-
-<div id="current-slide"></div>
-<div id="upcoming-slide"><span class="overlay-element label">Upcoming</span></div>
-<div id="speaker-controls">
-  <div class="speaker-controls-time">
-    <h4 class="label">Time <span class="reset-button">Click to Reset</span></h4>
-    <div class="clock">
-      <span class="clock-value">0:00 AM</span>
-    </div>
-    <div class="timer">
-      <span class="hours-value">00</span><span class="minutes-value">:00</span><span class="seconds-value">:00</span>
-    </div>
-    <div class="clear"></div>
-
-    <h4 class="label pacing-title" style="display: none">Pacing – Time to finish current slide</h4>
-    <div class="pacing" style="display: none">
-      <span class="hours-value">00</span><span class="minutes-value">:00</span><span class="seconds-value">:00</span>
-    </div>
-  </div>
-
-  <div class="speaker-controls-notes hidden">
-    <h4 class="label">Notes</h4>
-    <div class="value"></div>
-  </div>
-</div>
-<div id="speaker-layout" class="overlay-element interactive">
-  <span class="speaker-layout-label"></span>
-  <select class="speaker-layout-dropdown"></select>
-</div>
-
-<script src="../../plugin/markdown/marked.js"></script>
-<script>
-
-  (function() {
-
-    var notes,
-      notesValue,
-      currentState,
-      currentSlide,
-      upcomingSlide,
-      layoutLabel,
-      layoutDropdown,
-      pendingCalls = {},
-      lastRevealApiCallId = 0,
-      connected = false;
-
-    var SPEAKER_LAYOUTS = {
-      'default': 'Default',
-      'wide': 'Wide',
-      'tall': 'Tall',
-      'notes-only': 'Notes only'
-    };
-
-    setupLayout();
-
-    var connectionStatus = document.querySelector( '#connection-status' );
-    var connectionTimeout = setTimeout( function() {
-      connectionStatus.innerHTML = 'Error connecting to main window.<br>Please try closing and reopening the speaker view.';
-    }, 5000 );
-
-    window.addEventListener( 'message', function( event ) {
-
-      clearTimeout( connectionTimeout );
-      connectionStatus.style.display = 'none';
-
-      var data = JSON.parse( event.data );
-
-      // The overview mode is only useful to the reveal.js instance
-      // where navigation occurs so we don't sync it
-      if( data.state ) delete data.state.overview;
-
-      // Messages sent by the notes plugin inside of the main window
-      if( data && data.namespace === 'reveal-notes' ) {
-        if( data.type === 'connect' ) {
-          handleConnectMessage( data );
-        }
-        else if( data.type === 'state' ) {
-          handleStateMessage( data );
-        }
-        else if( data.type === 'return' ) {
-          pendingCalls[data.callId](data.result);
-          delete pendingCalls[data.callId];
-        }
-      }
-      // Messages sent by the reveal.js inside of the current slide preview
-      else if( data && data.namespace === 'reveal' ) {
-        if( /ready/.test( data.eventName ) ) {
-          // Send a message back to notify that the handshake is complete
-          window.opener.postMessage( JSON.stringify({ namespace: 'reveal-notes', type: 'connected'} ), '*' );
-        }
-        else if( /slidechanged|fragmentshown|fragmenthidden|paused|resumed/.test( data.eventName ) && currentState !== JSON.stringify( data.state ) ) {
-
-          window.opener.postMessage( JSON.stringify({ method: 'setState', args: [ data.state ]} ), '*' );
-
-        }
-      }
-
-    } );
-
-    /**
-     * Asynchronously calls the Reveal.js API of the main frame.
-     */
-    function callRevealApi( methodName, methodArguments, callback ) {
-
-      var callId = ++lastRevealApiCallId;
-      pendingCalls[callId] = callback;
-      window.opener.postMessage( JSON.stringify( {
-        namespace: 'reveal-notes',
-        type: 'call',
-        callId: callId,
-        methodName: methodName,
-        arguments: methodArguments
-      } ), '*' );
-
-    }
-
-    /**
-     * Called when the main window is trying to establish a
-     * connection.
-     */
-    function handleConnectMessage( data ) {
-
-      if( connected === false ) {
-        connected = true;
-
-        setupIframes( data );
-        setupKeyboard();
-        setupNotes();
-        setupTimer();
-      }
-
-    }
-
-    /**
-     * Called when the main window sends an updated state.
-     */
-    function handleStateMessage( data ) {
-
-      // Store the most recently set state to avoid circular loops
-      // applying the same state
-      currentState = JSON.stringify( data.state );
-
-      // No need for updating the notes in case of fragment changes
-      if ( data.notes ) {
-        notes.classList.remove( 'hidden' );
-        notesValue.style.whiteSpace = data.whitespace;
-        if( data.markdown ) {
-          notesValue.innerHTML = marked( data.notes );
-        }
-        else {
-          notesValue.innerHTML = data.notes;
-        }
-      }
-      else {
-        notes.classList.add( 'hidden' );
-      }
-
-      // Update the note slides
-      currentSlide.contentWindow.postMessage( JSON.stringify({ method: 'setState', args: [ data.state ] }), '*' );
-      upcomingSlide.contentWindow.postMessage( JSON.stringify({ method: 'setState', args: [ data.state ] }), '*' );
-      upcomingSlide.contentWindow.postMessage( JSON.stringify({ method: 'next' }), '*' );
-
-    }
-
-    // Limit to max one state update per X ms
-    handleStateMessage = debounce( handleStateMessage, 200 );
-
-    /**
-     * Forward keyboard events to the current slide window.
-     * This enables keyboard events to work even if focus
-     * isn't set on the current slide iframe.
-     *
-     * Block F5 default handling, it reloads and disconnects
-     * the speaker notes window.
-     */
-    function setupKeyboard() {
-
-      document.addEventListener( 'keydown', function( event ) {
-        if( event.keyCode === 116 || ( event.metaKey && event.keyCode === 82 ) ) {
-          event.preventDefault();
-          return false;
-        }
-        currentSlide.contentWindow.postMessage( JSON.stringify({ method: 'triggerKey', args: [ event.keyCode ] }), '*' );
-      } );
-
-    }
-
-    /**
-     * Creates the preview iframes.
-     */
-    function setupIframes( data ) {
-
-      var params = [
-        'receiver',
-        'progress=false',
-        'history=false',
-        'transition=none',
-        'autoSlide=0',
-        'backgroundTransition=none'
-      ].join( '&' );
-
-      var urlSeparator = /\?/.test(data.url) ? '&' : '?';
-      var hash = '#/' + data.state.indexh + '/' + data.state.indexv;
-      var currentURL = data.url + urlSeparator + params + '&postMessageEvents=true' + hash;
-      var upcomingURL = data.url + urlSeparator + params + '&controls=false' + hash;
-
-      currentSlide = document.createElement( 'iframe' );
-      currentSlide.setAttribute( 'width', 1280 );
-      currentSlide.setAttribute( 'height', 1024 );
-      currentSlide.setAttribute( 'src', currentURL );
-      document.querySelector( '#current-slide' ).appendChild( currentSlide );
-
-      upcomingSlide = document.createElement( 'iframe' );
-      upcomingSlide.setAttribute( 'width', 640 );
-      upcomingSlide.setAttribute( 'height', 512 );
-      upcomingSlide.setAttribute( 'src', upcomingURL );
-      document.querySelector( '#upcoming-slide' ).appendChild( upcomingSlide );
-
-    }
-
-    /**
-     * Setup the notes UI.
-     */
-    function setupNotes() {
-
-      notes = document.querySelector( '.speaker-controls-notes' );
-      notesValue = document.querySelector( '.speaker-controls-notes .value' );
-
-    }
-
-    function getTimings( callback ) {
-
-      callRevealApi( 'getSlidesAttributes', [], function ( slideAttributes ) {
-        callRevealApi( 'getConfig', [], function ( config ) {
-          var defaultTiming = config.defaultTiming;
-          if (defaultTiming == null) {
-            callback(null);
-            return;
-          }
-
-          var timings = [];
-          for ( var i in slideAttributes ) {
-            var slide = slideAttributes[ i ];
-            var timing = defaultTiming;
-            if( slide.hasOwnProperty( 'data-timing' )) {
-              var t = slide[ 'data-timing' ];
-              timing = parseInt(t);
-              if( isNaN(timing) ) {
-                console.warn("Could not parse timing '" + t + "' of slide " + i + "; using default of " + defaultTiming);
-                timing = defaultTiming;
-              }
-            }
-            timings.push(timing);
-          }
-
-          callback( timings );
-        } );
-      } );
-
-    }
-
-    /**
-     * Return the number of seconds allocated for presenting
-     * all slides up to and including this one.
-     */
-    function getTimeAllocated( timings, callback ) {
-
-      callRevealApi( 'getSlidePastCount', [], function ( currentSlide ) {
-        var allocated = 0;
-        for (var i in timings.slice(0, currentSlide + 1)) {
-          allocated += timings[i];
-        }
-        callback( allocated );
-      } );
-
-    }
-
-    /**
-     * Create the timer and clock and start updating them
-     * at an interval.
-     */
-    function setupTimer() {
-
-      var start = new Date(),
-        timeEl = document.querySelector( '.speaker-controls-time' ),
-        clockEl = timeEl.querySelector( '.clock-value' ),
-        hoursEl = timeEl.querySelector( '.hours-value' ),
-        minutesEl = timeEl.querySelector( '.minutes-value' ),
-        secondsEl = timeEl.querySelector( '.seconds-value' ),
-        pacingTitleEl = timeEl.querySelector( '.pacing-title' ),
-        pacingEl = timeEl.querySelector( '.pacing' ),
-        pacingHoursEl = pacingEl.querySelector( '.hours-value' ),
-        pacingMinutesEl = pacingEl.querySelector( '.minutes-value' ),
-        pacingSecondsEl = pacingEl.querySelector( '.seconds-value' );
-
-      var timings = null;
-      getTimings( function ( _timings ) {
-
-        timings = _timings;
-        if (_timings !== null) {
-          pacingTitleEl.style.removeProperty('display');
-          pacingEl.style.removeProperty('display');
-        }
-
-        // Update once directly
-        _updateTimer();
-
-        // Then update every second
-        setInterval( _updateTimer, 1000 );
-
-      } );
-
-
-      function _resetTimer() {
-
-        if (timings == null) {
-          start = new Date();
-          _updateTimer();
-        }
-        else {
-          // Reset timer to beginning of current slide
-          getTimeAllocated( timings, function ( slideEndTimingSeconds ) {
-            var slideEndTiming = slideEndTimingSeconds * 1000;
-            callRevealApi( 'getSlidePastCount', [], function ( currentSlide ) {
-              var currentSlideTiming = timings[currentSlide] * 1000;
-              var previousSlidesTiming = slideEndTiming - currentSlideTiming;
-              var now = new Date();
-              start = new Date(now.getTime() - previousSlidesTiming);
-              _updateTimer();
-            } );
-          } );
-        }
-
-      }
-
-      timeEl.addEventListener( 'click', function() {
-        _resetTimer();
-        return false;
-      } );
-
-      function _displayTime( hrEl, minEl, secEl, time) {
-
-        var sign = Math.sign(time) == -1 ? "-" : "";
-        time = Math.abs(Math.round(time / 1000));
-        var seconds = time % 60;
-        var minutes = Math.floor( time / 60 ) % 60 ;
-        var hours = Math.floor( time / ( 60 * 60 )) ;
-        hrEl.innerHTML = sign + zeroPadInteger( hours );
-        if (hours == 0) {
-          hrEl.classList.add( 'mute' );
-        }
-        else {
-          hrEl.classList.remove( 'mute' );
-        }
-        minEl.innerHTML = ':' + zeroPadInteger( minutes );
-        if (hours == 0 && minutes == 0) {
-          minEl.classList.add( 'mute' );
-        }
-        else {
-          minEl.classList.remove( 'mute' );
-        }
-        secEl.innerHTML = ':' + zeroPadInteger( seconds );
-      }
-
-      function _updateTimer() {
-
-        var diff, hours, minutes, seconds,
-          now = new Date();
-
-        diff = now.getTime() - start.getTime();
-
-        clockEl.innerHTML = now.toLocaleTimeString( 'en-US', { hour12: true, hour: '2-digit', minute:'2-digit' } );
-        _displayTime( hoursEl, minutesEl, secondsEl, diff );
-        if (timings !== null) {
-          _updatePacing(diff);
-        }
-
-      }
-
-      function _updatePacing(diff) {
-
-        getTimeAllocated( timings, function ( slideEndTimingSeconds ) {
-          var slideEndTiming = slideEndTimingSeconds * 1000;
-
-          callRevealApi( 'getSlidePastCount', [], function ( currentSlide ) {
-            var currentSlideTiming = timings[currentSlide] * 1000;
-            var timeLeftCurrentSlide = slideEndTiming - diff;
-            if (timeLeftCurrentSlide < 0) {
-              pacingEl.className = 'pacing behind';
-            }
-            else if (timeLeftCurrentSlide < currentSlideTiming) {
-              pacingEl.className = 'pacing on-track';
-            }
-            else {
-              pacingEl.className = 'pacing ahead';
-            }
-            _displayTime( pacingHoursEl, pacingMinutesEl, pacingSecondsEl, timeLeftCurrentSlide );
-          } );
-        } );
-      }
-
-    }
-
-    /**
-     * Sets up the speaker view layout and layout selector.
-     */
-    function setupLayout() {
-
-      layoutDropdown = document.querySelector( '.speaker-layout-dropdown' );
-      layoutLabel = document.querySelector( '.speaker-layout-label' );
-
-      // Render the list of available layouts
-      for( var id in SPEAKER_LAYOUTS ) {
-        var option = document.createElement( 'option' );
-        option.setAttribute( 'value', id );
-        option.textContent = SPEAKER_LAYOUTS[ id ];
-        layoutDropdown.appendChild( option );
-      }
-
-      // Monitor the dropdown for changes
-      layoutDropdown.addEventListener( 'change', function( event ) {
-
-        setLayout( layoutDropdown.value );
-
-      }, false );
-
-      // Restore any currently persisted layout
-      setLayout( getLayout() );
-
-    }
-
-    /**
-     * Sets a new speaker view layout. The layout is persisted
-     * in local storage.
-     */
-    function setLayout( value ) {
-
-      var title = SPEAKER_LAYOUTS[ value ];
-
-      layoutLabel.innerHTML = 'Layout' + ( title ? ( ': ' + title ) : '' );
-      layoutDropdown.value = value;
-
-      document.body.setAttribute( 'data-speaker-layout', value );
-
-      // Persist locally
-      if( supportsLocalStorage() ) {
-        window.localStorage.setItem( 'reveal-speaker-layout', value );
-      }
-
-    }
-
-    /**
-     * Returns the ID of the most recently set speaker layout
-     * or our default layout if none has been set.
-     */
-    function getLayout() {
-
-      if( supportsLocalStorage() ) {
-        var layout = window.localStorage.getItem( 'reveal-speaker-layout' );
-        if( layout ) {
-          return layout;
-        }
-      }
-
-      // Default to the first record in the layouts hash
-      for( var id in SPEAKER_LAYOUTS ) {
-        return id;
-      }
-
-    }
-
-    function supportsLocalStorage() {
-
-      try {
-        localStorage.setItem('test', 'test');
-        localStorage.removeItem('test');
-        return true;
-      }
-      catch( e ) {
-        return false;
-      }
-
-    }
-
-    function zeroPadInteger( num ) {
-
-      var str = '00' + parseInt( num );
-      return str.substring( str.length - 2 );
-
-    }
-
-    /**
-     * Limits the frequency at which a function can be called.
-     */
-    function debounce( fn, ms ) {
-
-      var lastTime = 0,
-        timeout;
-
-      return function() {
-
-        var args = arguments;
-        var context = this;
-
-        clearTimeout( timeout );
-
-        var timeSinceLastCall = Date.now() - lastTime;
-        if( timeSinceLastCall > ms ) {
-          fn.apply( context, args );
-          lastTime = Date.now();
-        }
-        else {
-          timeout = setTimeout( function() {
-            fn.apply( context, args );
-            lastTime = Date.now();
-          }, ms - timeSinceLastCall );
-        }
-
-      }
-
-    }
-
-  })();
-
-</script>
-</body>
-</html>

+ 0 - 178
wowchemy/static/js/vendor/reveal.js/plugin/notes/notes.js

@@ -1,178 +0,0 @@
-/**
- * Handles opening of and synchronization with the reveal.js
- * notes window.
- *
- * Handshake process:
- * 1. This window posts 'connect' to notes window
- *    - Includes URL of presentation to show
- * 2. Notes window responds with 'connected' when it is available
- * 3. This window proceeds to send the current presentation state
- *    to the notes window
- */
-var RevealNotes = (function() {
-
-  var notesPopup = null;
-
-  function openNotes( notesFilePath ) {
-
-    if (notesPopup && !notesPopup.closed) {
-      notesPopup.focus();
-      return;
-    }
-
-    if( !notesFilePath ) {
-      var jsFileLocation = document.querySelector('script[src$="notes.js"]').src;  // this js file path
-      jsFileLocation = jsFileLocation.replace(/notes\.js(\?.*)?$/, '');   // the js folder path
-      notesFilePath = jsFileLocation + 'notes.html';
-    }
-
-    notesPopup = window.open( notesFilePath, 'reveal.js - Notes', 'width=1100,height=700' );
-
-    if( !notesPopup ) {
-      alert( 'Speaker view popup failed to open. Please make sure popups are allowed and reopen the speaker view.' );
-      return;
-    }
-
-    /**
-     * Connect to the notes window through a postmessage handshake.
-     * Using postmessage enables us to work in situations where the
-     * origins differ, such as a presentation being opened from the
-     * file system.
-     */
-    function connect() {
-      // Keep trying to connect until we get a 'connected' message back
-      var connectInterval = setInterval( function() {
-        notesPopup.postMessage( JSON.stringify( {
-          namespace: 'reveal-notes',
-          type: 'connect',
-          url: window.location.protocol + '//' + window.location.host + window.location.pathname + window.location.search,
-          state: Reveal.getState()
-        } ), '*' );
-      }, 500 );
-
-      window.addEventListener( 'message', function( event ) {
-        var data = JSON.parse( event.data );
-        if( data && data.namespace === 'reveal-notes' && data.type === 'connected' ) {
-          clearInterval( connectInterval );
-          onConnected();
-        }
-        if( data && data.namespace === 'reveal-notes' && data.type === 'call' ) {
-          callRevealApi( data.methodName, data.arguments, data.callId );
-        }
-      } );
-    }
-
-    /**
-     * Calls the specified Reveal.js method with the provided argument
-     * and then pushes the result to the notes frame.
-     */
-    function callRevealApi( methodName, methodArguments, callId ) {
-
-      var result = Reveal[methodName].apply( Reveal, methodArguments );
-      notesPopup.postMessage( JSON.stringify( {
-        namespace: 'reveal-notes',
-        type: 'return',
-        result: result,
-        callId: callId
-      } ), '*' );
-
-    }
-
-    /**
-     * Posts the current slide data to the notes window
-     */
-    function post( event ) {
-
-      var slideElement = Reveal.getCurrentSlide(),
-        notesElement = slideElement.querySelector( 'aside.notes' ),
-        fragmentElement = slideElement.querySelector( '.current-fragment' );
-
-      var messageData = {
-        namespace: 'reveal-notes',
-        type: 'state',
-        notes: '',
-        markdown: false,
-        whitespace: 'normal',
-        state: Reveal.getState()
-      };
-
-      // Look for notes defined in a slide attribute
-      if( slideElement.hasAttribute( 'data-notes' ) ) {
-        messageData.notes = slideElement.getAttribute( 'data-notes' );
-        messageData.whitespace = 'pre-wrap';
-      }
-
-      // Look for notes defined in a fragment
-      if( fragmentElement ) {
-        var fragmentNotes = fragmentElement.querySelector( 'aside.notes' );
-        if( fragmentNotes ) {
-          notesElement = fragmentNotes;
-        }
-        else if( fragmentElement.hasAttribute( 'data-notes' ) ) {
-          messageData.notes = fragmentElement.getAttribute( 'data-notes' );
-          messageData.whitespace = 'pre-wrap';
-
-          // In case there are slide notes
-          notesElement = null;
-        }
-      }
-
-      // Look for notes defined in an aside element
-      if( notesElement ) {
-        messageData.notes = notesElement.innerHTML;
-        messageData.markdown = typeof notesElement.getAttribute( 'data-markdown' ) === 'string';
-      }
-
-      notesPopup.postMessage( JSON.stringify( messageData ), '*' );
-
-    }
-
-    /**
-     * Called once we have established a connection to the notes
-     * window.
-     */
-    function onConnected() {
-
-      // Monitor events that trigger a change in state
-      Reveal.addEventListener( 'slidechanged', post );
-      Reveal.addEventListener( 'fragmentshown', post );
-      Reveal.addEventListener( 'fragmenthidden', post );
-      Reveal.addEventListener( 'overviewhidden', post );
-      Reveal.addEventListener( 'overviewshown', post );
-      Reveal.addEventListener( 'paused', post );
-      Reveal.addEventListener( 'resumed', post );
-
-      // Post the initial state
-      post();
-
-    }
-
-    connect();
-
-  }
-
-  return {
-    init: function() {
-
-      if( !/receiver/i.test( window.location.search ) ) {
-
-        // If the there's a 'notes' query set, open directly
-        if( window.location.search.match( /(\?|\&)notes/gi ) !== null ) {
-          openNotes();
-        }
-
-        // Open the notes when the 's' key is hit
-        Reveal.addKeyBinding({keyCode: 83, key: 'S', description: 'Speaker notes view'}, function() {
-          openNotes();
-        } );
-
-      }
-
-    },
-
-    open: openNotes
-  };
-
-})();
-
-Reveal.registerPlugin( 'notes', RevealNotes );