mirror of
https://github.com/tiennm99/HugoBlox-kit.git
synced 2026-10-13 14:15:47 +00:00
Add slides feature
Example added to `exampleSite/content/slides/example-slides.md` and linked from `exampleSite/content/talk/example-talk.md`. Close #693
This commit is contained in:
1 parent
a86e35245e
commit
6faf695727
16 files changed
+1268
-7
No files matched your search
@@ -1,3 +1,4 @@
|
||||
# IDE specific
|
||||
.idea/
|
||||
.vscode/
|
||||
resources/
|
||||
@@ -0,0 +1,15 @@
|
||||
+++
|
||||
title = "Slides"
|
||||
|
||||
[slides]
|
||||
# Choose a theme from https://github.com/hakimel/reveal.js#theming
|
||||
theme = "black"
|
||||
+++
|
||||
|
||||
# Title
|
||||
|
||||
Author Name
|
||||
|
||||
---
|
||||
|
||||
## Slide 2
|
||||
@@ -0,0 +1,31 @@
|
||||
/*************************************************
|
||||
* 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;
|
||||
}
|
||||
Whitespace-only changes.
@@ -0,0 +1,159 @@
|
||||
+++
|
||||
title = "Slides"
|
||||
|
||||
[slides]
|
||||
# Choose a theme from https://github.com/hakimel/reveal.js#theming
|
||||
theme = "black"
|
||||
+++
|
||||
|
||||
# Welcome to Slides
|
||||
|
||||
[Academic](https://sourcethemes.com/academic/)
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
- Efficiently write slides in Markdown
|
||||
- 3-in-1: Create, Present, and Publish your slides
|
||||
- Supports speaker notes
|
||||
- Mobile friendly slides
|
||||
|
||||
---
|
||||
|
||||
## Controls
|
||||
|
||||
- Next: `Right Arrow` or `Space`
|
||||
- Previous: `Left Arrow`
|
||||
- Start: `Home`
|
||||
- Finish: `End`
|
||||
- Overview: `Esc`
|
||||
- Speaker notes: `S`
|
||||
- Fullscreen: `F`
|
||||
- Zoom: `Alt + Click`
|
||||
- [PDF Export](https://github.com/hakimel/reveal.js#pdf-export): `E`
|
||||
|
||||
---
|
||||
|
||||
## Code Highlighting
|
||||
|
||||
Inline code: `variable`
|
||||
|
||||
Code block:
|
||||
```python
|
||||
porridge = "blueberry"
|
||||
if porridge == "blueberry":
|
||||
print("Eating...")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Math
|
||||
|
||||
In-line math: $x + y = z$
|
||||
|
||||
Block math:
|
||||
|
||||
$$
|
||||
f\left( x \right) = \;\frac{{2\left( {x + 4} \right)\left( {x - 4} \right)}}{{\left( {x + 4} \right)\left( {x + 1} \right)}}
|
||||
$$
|
||||
|
||||
---
|
||||
|
||||
## Fragments
|
||||
|
||||
Make content appear incrementally
|
||||
|
||||
```
|
||||
{{%/* fragment */%}} One {{%/* /fragment */%}}
|
||||
{{%/* fragment */%}} **Two** {{%/* /fragment */%}}
|
||||
{{%/* fragment */%}} Three {{%/* /fragment */%}}
|
||||
```
|
||||
|
||||
Press `Space` to play!
|
||||
|
||||
{{% fragment %}} One {{% /fragment %}}
|
||||
{{% fragment %}} **Two** {{% /fragment %}}
|
||||
{{% fragment %}} Three {{% /fragment %}}
|
||||
|
||||
---
|
||||
|
||||
A fragment can accept two optional parameters:
|
||||
|
||||
- `class`: use a custom style (requires definition in custom CSS)
|
||||
- `weight`: sets the order in which a fragment appears
|
||||
|
||||
---
|
||||
|
||||
## Speaker Notes
|
||||
|
||||
Add speaker notes to your presentation
|
||||
|
||||
```markdown
|
||||
{{%/* speaker_note */%}}
|
||||
- Only the speaker can read these notes
|
||||
- Press `S` key to view
|
||||
{{%/* /speaker_note */%}}
|
||||
```
|
||||
|
||||
Press the `S` key to view the speaker notes!
|
||||
|
||||
{{< speaker_note >}}
|
||||
- Only the speaker can read these notes
|
||||
- Press `S` key to view
|
||||
{{< /speaker_note >}}
|
||||
|
||||
---
|
||||
|
||||
## Themes
|
||||
|
||||
- black: Black background, white text, blue links (default)
|
||||
- white: White background, black text, blue links
|
||||
- league: Gray background, white text, blue links
|
||||
- beige: Beige background, dark text, brown links
|
||||
- sky: Blue background, thin dark text, blue links
|
||||
|
||||
---
|
||||
|
||||
- night: Black background, thick white text, orange links
|
||||
- serif: Cappuccino background, gray text, brown links
|
||||
- simple: White background, black text, blue links
|
||||
- solarized: Cream-colored background, dark green text, blue links
|
||||
|
||||
---
|
||||
|
||||
{{< slide background-image="/img/boards.jpg" >}}
|
||||
|
||||
## Custom Slide
|
||||
|
||||
Customize the slide style and background
|
||||
|
||||
```markdown
|
||||
{{</* slide background-image="/img/boards.jpg" */>}}
|
||||
{{</* slide background-color="#0000FF" */>}}
|
||||
{{</* slide class="my-style" */>}}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Custom CSS Example
|
||||
|
||||
Let's make headers navy colored.
|
||||
|
||||
Create `assets/css/reveal_custom.css` with:
|
||||
|
||||
```css
|
||||
.reveal section h1,
|
||||
.reveal section h2,
|
||||
.reveal section h3 {
|
||||
color: navy;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Questions?
|
||||
|
||||
[Ask](https://discourse.gohugo.io)
|
||||
|
||||
[Documentation](https://sourcethemes.com/academic/docs/)
|
||||
@@ -38,7 +38,7 @@ tags = []
|
||||
|
||||
# Links (optional).
|
||||
url_pdf = ""
|
||||
url_slides = ""
|
||||
url_slides = "slides/example-slides"
|
||||
url_video = ""
|
||||
url_code = ""
|
||||
|
||||
@@ -56,4 +56,14 @@ caption = "My caption :smile:"
|
||||
|
||||
+++
|
||||
|
||||
Embed your slides or video here using [shortcodes](https://sourcethemes.com/academic/post/writing-markdown-latex/). Further details can easily be added using *Markdown* and $\rm \LaTeX$ math code.
|
||||
{{% alert note %}}
|
||||
Click on the **Slides** button above to view the built-in slides feature.
|
||||
{{% /alert %}}
|
||||
|
||||
Slides can be added in a few ways:
|
||||
|
||||
- **Create** slides using Academic's *Slides* feature and link using `url_slides` parameter in the front matter of the talk file
|
||||
- **Upload** an existing slide deck to `static/` and link using `url_slides` parameter in the front matter of the talk file
|
||||
- **Embed** your slides (e.g. Google Slides) or presentation video on this page using [shortcodes](https://sourcethemes.com/academic/docs/writing-markdown-latex/).
|
||||
|
||||
Further talk details can easily be added to this page using *Markdown* and $\rm \LaTeX$ math code.
|
||||
@@ -0,0 +1,25 @@
|
||||
<div class="reveal">
|
||||
<div class="slides">
|
||||
{{/* Loop over each page. */}}
|
||||
{{ range . }}
|
||||
{{ if ne (len .Content) 0 }}
|
||||
|
||||
{{/* Must remove `<hr />` generated by Blackfriday footnotes as conflicts with slide delimiter. */}}
|
||||
{{ $content := replace .Content "<div class=\"footnotes\">\n\n<hr />" "<div class=\"footnotes\">" }}
|
||||
|
||||
{{/* Let `<hr />` delimit slides. */}}
|
||||
{{ range (split $content "<hr />") }}
|
||||
|
||||
{{/* Each `<section>` defines a new slide. */}}
|
||||
{{/* Only begin new slide `<section>` if not already added by custom `slide` shortcode. */}}
|
||||
{{ if not (in . "data-noprocess") }}
|
||||
<section>
|
||||
{{ end }}
|
||||
{{ . | safeHTML }}
|
||||
</section>
|
||||
{{ end }}
|
||||
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -2,22 +2,22 @@
|
||||
{{ $ := .content }}
|
||||
|
||||
{{ with $.Params.url_pdf }}
|
||||
<a class="btn btn-outline-primary my-1 mr-1{{ if $is_list }} btn-sm{{end}}" href="{{ . | relURL }}">
|
||||
<a class="btn btn-outline-primary my-1 mr-1{{ if $is_list }} btn-sm{{end}}" href="{{ . | relURL }}" target="_blank" rel="noopener">
|
||||
{{ i18n "btn_pdf" }}
|
||||
</a>
|
||||
{{ end }}
|
||||
{{ with $.Params.url_slides }}
|
||||
<a class="btn btn-outline-primary my-1 mr-1{{ if $is_list }} btn-sm{{end}}" href="{{ . | relURL }}">
|
||||
<a class="btn btn-outline-primary my-1 mr-1{{ if $is_list }} btn-sm{{end}}" href="{{ . | relURL }}" target="_blank">
|
||||
{{ i18n "btn_slides" }}
|
||||
</a>
|
||||
{{ end }}
|
||||
{{ with $.Params.url_video }}
|
||||
<a class="btn btn-outline-primary my-1 mr-1{{ if $is_list }} btn-sm{{end}}" href="{{ . | relURL }}">
|
||||
<a class="btn btn-outline-primary my-1 mr-1{{ if $is_list }} btn-sm{{end}}" href="{{ . | relURL }}" target="_blank" rel="noopener">
|
||||
{{ i18n "btn_video" }}
|
||||
</a>
|
||||
{{ end }}
|
||||
{{ with $.Params.url_code }}
|
||||
<a class="btn btn-outline-primary my-1 mr-1{{ if $is_list }} btn-sm{{end}}" href="{{ . | relURL }}">
|
||||
<a class="btn btn-outline-primary my-1 mr-1{{ if $is_list }} btn-sm{{end}}" href="{{ . | relURL }}" target="_blank" rel="noopener">
|
||||
{{ i18n "btn_code" }}
|
||||
</a>
|
||||
{{ end }}
|
||||
@@ -31,7 +31,7 @@
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ range $.Params.url_custom }}
|
||||
<a class="btn btn-outline-primary my-1 mr-1{{ if $is_list }} btn-sm{{end}}" href="{{ .url | relURL }}">
|
||||
<a class="btn btn-outline-primary my-1 mr-1{{ if $is_list }} btn-sm{{end}}" href="{{ .url | relURL }}" target="_blank" rel="noopener">
|
||||
{{ .name }}
|
||||
</a>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,3 @@
|
||||
<span class="fragment {{ .Get "class" }}" {{ with .Get "weight" }}data-fragment-index="{{ . }}"{{ end }}>
|
||||
{{ .Inner }}
|
||||
</span>
|
||||
@@ -0,0 +1,6 @@
|
||||
{{ $non_data_attributes := slice "id" "class" }}
|
||||
<section data-noprocess data-shortcode-slide
|
||||
{{ range $key, $value := $.Params }}
|
||||
{{ $attribute := cond (in $non_data_attributes $key) $key (delimit (slice "data" $key) "-") }}
|
||||
{{ $attribute | safeHTMLAttr }}="{{ $value }}"
|
||||
{{ end }}>
|
||||
@@ -0,0 +1,3 @@
|
||||
<aside class="notes">
|
||||
{{ .Inner | markdownify }}
|
||||
</aside>
|
||||
@@ -0,0 +1,66 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ .Site.LanguageCode | default "en-us" }}">
|
||||
<head>
|
||||
{{ $css := .Site.Data.assets.css }}
|
||||
{{ $cdn_url_reveal := "https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.7.0" }}
|
||||
|
||||
<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 }}">
|
||||
|
||||
<link rel="manifest" href="{{ "site.webmanifest" | relURL }}">
|
||||
<link rel="icon" type="image/png" href="{{ "/img/icon.png" | relURL }}">
|
||||
<link rel="apple-touch-icon" type="image/png" href="{{ "/img/icon-192.png" | relURL }}">
|
||||
|
||||
<link rel="canonical" href="{{ .Permalink }}">
|
||||
|
||||
<title>{{ .Title }} | {{ .Site.Title }}</title>
|
||||
|
||||
<link rel="stylesheet" href="{{ $cdn_url_reveal }}/css/reveal.min.css">
|
||||
{{- $theme := $.Param "slides.theme" | default "black" -}}
|
||||
<link rel="stylesheet" href="{{ $cdn_url_reveal }}/css/theme/{{ $theme }}.min.css">
|
||||
|
||||
{{ printf "<link rel=\"stylesheet\" href=\"%s\" crossorigin=\"anonymous\">" (printf $css.highlight.url $css.highlight.version "dracula") | 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 }}
|
||||
<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 }}/lib/js/head.min.js"></script>
|
||||
<script src="{{ $cdn_url_reveal }}/js/reveal.min.js"></script>
|
||||
|
||||
<script>
|
||||
window.revealPlugins = { dependencies: [
|
||||
{ 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]' ); } },
|
||||
{ src: '{{ $cdn_url_reveal }}/plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } },
|
||||
{ src: '{{ $cdn_url_reveal }}/plugin/zoom-js/zoom.js', async: true },
|
||||
{ src: '{{ $cdn_url_reveal }}/plugin/math/math.js', async: true },
|
||||
{ src: '{{ $cdn_url_reveal }}/plugin/print-pdf/print-pdf.js', async: true },
|
||||
// Notes plugin must be loaded locally as CDN is missing `notes.html`.
|
||||
{ src: '{{ "js/vendor/reveal.js/plugin/notes/notes.js" | relURL }}', async: true }
|
||||
]};
|
||||
|
||||
let revealDefaults = { center: true, controls: true, history: true, progress: true, transition: 'slide', mouseWheel: true };
|
||||
let revealOptions = Object.assign({}, revealDefaults, revealPlugins);
|
||||
Reveal.initialize(revealOptions);
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Whitespace-only changes.
@@ -0,0 +1,3 @@
|
||||
{{ define "main" }}
|
||||
{{ partial "slides" (union (slice .Page) .Pages) }}
|
||||
{{ end }}
|
||||
+792
@@ -0,0 +1,792 @@
|
||||
<!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,
|
||||
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 );
|
||||
}
|
||||
}
|
||||
// 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 ]} ), '*' );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
|
||||
var slides = Reveal.getSlides();
|
||||
var defaultTiming = Reveal.getConfig().defaultTiming;
|
||||
if (defaultTiming == null) {
|
||||
return null;
|
||||
}
|
||||
var timings = [];
|
||||
for ( var i in slides ) {
|
||||
var slide = slides[i];
|
||||
var timing = defaultTiming;
|
||||
if( slide.hasAttribute( 'data-timing' )) {
|
||||
var t = slide.getAttribute( '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);
|
||||
}
|
||||
return timings;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of seconds allocated for presenting
|
||||
* all slides up to and including this one.
|
||||
*/
|
||||
function getTimeAllocated(timings) {
|
||||
|
||||
var slides = Reveal.getSlides();
|
||||
var allocated = 0;
|
||||
var currentSlide = Reveal.getSlidePastCount();
|
||||
for (var i in slides.slice(0, currentSlide + 1)) {
|
||||
allocated += timings[i];
|
||||
}
|
||||
return 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 = getTimings();
|
||||
if (timings !== null) {
|
||||
pacingTitleEl.style.removeProperty('display');
|
||||
pacingEl.style.removeProperty('display');
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
var slideEndTiming = getTimeAllocated(timings) * 1000;
|
||||
var currentSlide = Reveal.getSlidePastCount();
|
||||
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 );
|
||||
|
||||
}
|
||||
|
||||
// Update once directly
|
||||
_updateTimer();
|
||||
|
||||
// Then update every second
|
||||
setInterval( _updateTimer, 1000 );
|
||||
|
||||
function _resetTimer() {
|
||||
|
||||
if (timings == null) {
|
||||
start = new Date();
|
||||
}
|
||||
else {
|
||||
// Reset timer to beginning of current slide
|
||||
var slideEndTiming = getTimeAllocated(timings) * 1000;
|
||||
var currentSlide = Reveal.getSlidePastCount();
|
||||
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;
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
* 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() {
|
||||
|
||||
function openNotes( notesFilePath ) {
|
||||
|
||||
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';
|
||||
}
|
||||
|
||||
var 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;
|
||||
}
|
||||
|
||||
// Allow popup window access to Reveal API
|
||||
notesPopup.Reveal = window.Reveal;
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
}
|
||||
|
||||
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();
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
return { open: openNotes };
|
||||
|
||||
})();
|
||||
Reference in new issue
Block a user