reveal-ck 3.9.2 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/features/step_definitions/serve_steps.rb +1 -1
- data/files/reveal-ck/templates/index.html/body.html.erb +0 -2
- data/files/reveal-ck/templates/index.html/head.html.erb +6 -6
- data/files/reveal-ck/templates/index.html/index.html.erb +2 -2
- data/files/reveal-ck/templates/index.html/script.js.erb +4 -6
- data/files/reveal.js/LICENSE +1 -1
- data/files/reveal.js/README.md +268 -82
- data/files/reveal.js/bower.json +1 -4
- data/files/reveal.js/css/reset.css +30 -0
- data/files/reveal.js/css/reveal.css +114 -99
- data/files/reveal.js/css/reveal.scss +108 -95
- data/files/reveal.js/css/theme/README.md +1 -1
- data/files/reveal.js/css/theme/beige.css +1 -1
- data/files/reveal.js/css/theme/black.css +4 -4
- data/files/reveal.js/css/theme/blood.css +1 -1
- data/files/reveal.js/css/theme/league.css +1 -1
- data/files/reveal.js/css/theme/moon.css +1 -1
- data/files/reveal.js/css/theme/night.css +1 -1
- data/files/reveal.js/css/theme/serif.css +1 -1
- data/files/reveal.js/css/theme/simple.css +1 -1
- data/files/reveal.js/css/theme/sky.css +1 -1
- data/files/reveal.js/css/theme/solarized.css +1 -1
- data/files/reveal.js/css/theme/source/black.scss +1 -1
- data/files/reveal.js/css/theme/template/settings.scss +3 -1
- data/files/reveal.js/css/theme/template/theme.scss +3 -3
- data/files/reveal.js/css/theme/white.css +1 -1
- data/files/reveal.js/demo.html +35 -26
- data/files/reveal.js/{Gruntfile.js → gruntfile.js} +21 -25
- data/files/reveal.js/index.html +5 -4
- data/files/reveal.js/js/reveal.js +961 -347
- data/files/reveal.js/lib/css/monokai.css +71 -0
- data/files/reveal.js/lib/js/promise.js +2 -0
- data/files/reveal.js/package-lock.json +5703 -0
- data/files/reveal.js/package.json +13 -12
- data/files/reveal.js/plugin/highlight/highlight.js +247 -21
- data/files/reveal.js/plugin/markdown/example.html +1 -3
- data/files/reveal.js/plugin/markdown/markdown.js +107 -73
- data/files/reveal.js/plugin/markdown/marked.js +3 -3
- data/files/reveal.js/plugin/math/math.js +51 -26
- data/files/reveal.js/plugin/notes/notes.html +138 -76
- data/files/reveal.js/plugin/notes/notes.js +46 -15
- data/files/reveal.js/plugin/search/search.js +1 -1
- data/files/reveal.js/plugin/zoom-js/zoom.js +22 -17
- data/files/reveal.js/test/assets/external-script-a.js +1 -0
- data/files/reveal.js/test/assets/external-script-b.js +1 -0
- data/files/reveal.js/test/assets/external-script-c.js +1 -0
- data/files/reveal.js/test/assets/external-script-d.js +1 -0
- data/files/reveal.js/test/examples/assets/beeping.txt +2 -0
- data/files/reveal.js/test/examples/assets/beeping.wav +0 -0
- data/files/reveal.js/test/examples/embedded-media.html +5 -1
- data/files/reveal.js/test/examples/math.html +23 -3
- data/files/reveal.js/test/examples/slide-backgrounds.html +0 -1
- data/files/reveal.js/test/examples/slide-transitions.html +0 -1
- data/files/reveal.js/test/test-dependencies-async.html +78 -0
- data/files/reveal.js/test/test-dependencies.html +54 -0
- data/files/reveal.js/test/test-grid-navigation.html +74 -0
- data/files/reveal.js/test/test-iframe-backgrounds.html +104 -0
- data/files/reveal.js/test/test-iframes.html +108 -0
- data/files/reveal.js/test/test-markdown-element-attributes.html +1 -3
- data/files/reveal.js/test/test-markdown-external.html +5 -4
- data/files/reveal.js/test/test-markdown-options.html +0 -1
- data/files/reveal.js/test/test-markdown-slide-attributes.html +0 -1
- data/files/reveal.js/test/test-markdown.html +0 -1
- data/files/reveal.js/test/test-pdf.html +1 -2
- data/files/reveal.js/test/test-plugins.html +105 -0
- data/files/reveal.js/test/test-state.html +139 -0
- data/files/reveal.js/test/test.html +3 -4
- data/files/reveal.js/test/test.js +21 -0
- data/lib/reveal-ck/builders/create_index_html.rb +0 -1
- data/lib/reveal-ck/builders/index_html.rb +2 -4
- data/lib/reveal-ck/commands/listen_to_reload_browser.rb +4 -1
- data/lib/reveal-ck/commands/serve.rb +2 -2
- data/lib/reveal-ck/commands/start_web_server.rb +1 -1
- data/lib/reveal-ck/commands/thread_waker.rb +1 -1
- data/lib/reveal-ck/config.rb +1 -1
- data/lib/reveal-ck/version.rb +1 -1
- data/spec/lib/reveal-ck/builders/index_html_spec.rb +1 -11
- data/spec/lib/reveal-ck/commands/thread_waker_spec.rb +2 -0
- metadata +94 -80
- data/files/reveal.js/lib/js/classList.js +0 -2
- data/files/reveal.js/lib/js/head.min.js +0 -6
@@ -4,7 +4,7 @@ Themes are written using Sass to keep things modular and reduce the need for rep
|
|
4
4
|
|
5
5
|
## Creating a Theme
|
6
6
|
|
7
|
-
To create your own theme, start by duplicating a ```.scss``` file in [/css/theme/source](https://github.com/hakimel/reveal.js/blob/master/css/theme/source). It will be automatically compiled by Grunt from Sass to CSS (see the [Gruntfile](https://github.com/hakimel/reveal.js/blob/master/
|
7
|
+
To create your own theme, start by duplicating a ```.scss``` file in [/css/theme/source](https://github.com/hakimel/reveal.js/blob/master/css/theme/source). It will be automatically compiled by Grunt from Sass to CSS (see the [Gruntfile](https://github.com/hakimel/reveal.js/blob/master/gruntfile.js)) when you run `npm run build -- css-themes`.
|
8
8
|
|
9
9
|
Each theme file does four things in the following order:
|
10
10
|
|
@@ -11,8 +11,8 @@ section.has-light-background, section.has-light-background h1, section.has-light
|
|
11
11
|
* GLOBAL STYLES
|
12
12
|
*********************************************/
|
13
13
|
body {
|
14
|
-
background: #
|
15
|
-
background-color: #
|
14
|
+
background: #191919;
|
15
|
+
background-color: #191919; }
|
16
16
|
|
17
17
|
.reveal {
|
18
18
|
font-family: "Source Sans Pro", Helvetica, sans-serif;
|
@@ -149,7 +149,7 @@ body {
|
|
149
149
|
font-family: monospace;
|
150
150
|
line-height: 1.2em;
|
151
151
|
word-wrap: break-word;
|
152
|
-
box-shadow: 0px
|
152
|
+
box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); }
|
153
153
|
|
154
154
|
.reveal code {
|
155
155
|
font-family: monospace;
|
@@ -270,4 +270,4 @@ body {
|
|
270
270
|
*********************************************/
|
271
271
|
@media print {
|
272
272
|
.backgrounds {
|
273
|
-
background-color: #
|
273
|
+
background-color: #191919; } }
|
@@ -28,6 +28,8 @@ $heading2Size: 2.11em;
|
|
28
28
|
$heading3Size: 1.55em;
|
29
29
|
$heading4Size: 1.00em;
|
30
30
|
|
31
|
+
$codeFont: monospace;
|
32
|
+
|
31
33
|
// Links and actions
|
32
34
|
$linkColor: #13DAEC;
|
33
35
|
$linkColorHover: lighten( $linkColor, 20% );
|
@@ -40,4 +42,4 @@ $selectionColor: #fff;
|
|
40
42
|
// to return a background image or gradient
|
41
43
|
@mixin bodyBackground() {
|
42
44
|
background: $backgroundColor;
|
43
|
-
}
|
45
|
+
}
|
@@ -162,16 +162,16 @@ body {
|
|
162
162
|
|
163
163
|
text-align: left;
|
164
164
|
font-size: 0.55em;
|
165
|
-
font-family:
|
165
|
+
font-family: $codeFont;
|
166
166
|
line-height: 1.2em;
|
167
167
|
|
168
168
|
word-wrap: break-word;
|
169
169
|
|
170
|
-
box-shadow: 0px
|
170
|
+
box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15);
|
171
171
|
}
|
172
172
|
|
173
173
|
.reveal code {
|
174
|
-
font-family:
|
174
|
+
font-family: $codeFont;
|
175
175
|
text-transform: none;
|
176
176
|
}
|
177
177
|
|
data/files/reveal.js/demo.html
CHANGED
@@ -12,13 +12,14 @@
|
|
12
12
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
13
13
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
14
14
|
|
15
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0
|
15
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
16
16
|
|
17
|
+
<link rel="stylesheet" href="css/reset.css">
|
17
18
|
<link rel="stylesheet" href="css/reveal.css">
|
18
19
|
<link rel="stylesheet" href="css/theme/black.css" id="theme">
|
19
20
|
|
20
21
|
<!-- Theme used for syntax highlighting of code -->
|
21
|
-
<link rel="stylesheet" href="lib/css/
|
22
|
+
<link rel="stylesheet" href="lib/css/monokai.css">
|
22
23
|
|
23
24
|
<!-- Printing and PDF exports -->
|
24
25
|
<script>
|
@@ -93,7 +94,10 @@
|
|
93
94
|
Press <strong>ESC</strong> to enter the slide overview.
|
94
95
|
</p>
|
95
96
|
<p>
|
96
|
-
Hold down alt and click on any element to zoom
|
97
|
+
Hold down the <strong>alt</strong> key (<strong>ctrl</strong> in Linux) and click on any element to zoom towards it using <a href="http://lab.hakim.se/zoom-js">zoom.js</a>. Click again to zoom back out.
|
98
|
+
</p>
|
99
|
+
<p>
|
100
|
+
(NOTE: Use ctrl + click in Linux.)
|
97
101
|
</p>
|
98
102
|
</section>
|
99
103
|
|
@@ -195,16 +199,16 @@
|
|
195
199
|
</section>
|
196
200
|
<section data-background="https://s3.amazonaws.com/hakim-static/reveal-js/image-placeholder.png">
|
197
201
|
<h2>Image Backgrounds</h2>
|
198
|
-
<pre><code class="hljs"><section data-background="image.png"></code></pre>
|
202
|
+
<pre><code class="hljs html"><section data-background="image.png"></code></pre>
|
199
203
|
</section>
|
200
204
|
<section data-background="https://s3.amazonaws.com/hakim-static/reveal-js/image-placeholder.png" data-background-repeat="repeat" data-background-size="100px">
|
201
205
|
<h2>Tiled Backgrounds</h2>
|
202
|
-
<pre><code class="hljs" style="word-wrap: break-word;"><section data-background="image.png" data-background-repeat="repeat" data-background-size="100px"></code></pre>
|
206
|
+
<pre><code class="hljs html" style="word-wrap: break-word;"><section data-background="image.png" data-background-repeat="repeat" data-background-size="100px"></code></pre>
|
203
207
|
</section>
|
204
208
|
<section data-background-video="https://s3.amazonaws.com/static.slid.es/site/homepage/v1/homepage-video-editor.mp4,https://s3.amazonaws.com/static.slid.es/site/homepage/v1/homepage-video-editor.webm" data-background-color="#000000">
|
205
209
|
<div style="background-color: rgba(0, 0, 0, 0.9); color: #fff; padding: 20px;">
|
206
210
|
<h2>Video Backgrounds</h2>
|
207
|
-
<pre><code class="hljs" style="word-wrap: break-word;"><section data-background-video="video.mp4,video.webm"></code></pre>
|
211
|
+
<pre><code class="hljs html" style="word-wrap: break-word;"><section data-background-video="video.mp4,video.webm"></code></pre>
|
208
212
|
</div>
|
209
213
|
</section>
|
210
214
|
<section data-background="http://i.giphy.com/90F8aUepslB84.gif">
|
@@ -217,7 +221,7 @@
|
|
217
221
|
<p>
|
218
222
|
Different background transitions are available via the backgroundTransition option. This one's called "zoom".
|
219
223
|
</p>
|
220
|
-
<pre><code class="hljs">Reveal.configure({ backgroundTransition: 'zoom' })</code></pre>
|
224
|
+
<pre><code class="hljs javascript">Reveal.configure({ backgroundTransition: 'zoom' })</code></pre>
|
221
225
|
</section>
|
222
226
|
|
223
227
|
<section data-transition="slide" data-background="#b5533c" data-background-transition="zoom">
|
@@ -225,25 +229,32 @@
|
|
225
229
|
<p>
|
226
230
|
You can override background transitions per-slide.
|
227
231
|
</p>
|
228
|
-
<pre><code class="hljs" style="word-wrap: break-word;"><section data-background-transition="zoom"></code></pre>
|
232
|
+
<pre><code class="hljs html" style="word-wrap: break-word;"><section data-background-transition="zoom"></code></pre>
|
233
|
+
</section>
|
234
|
+
|
235
|
+
<section data-background-iframe="https://hakim.se" data-background-interactive>
|
236
|
+
<div style="position: absolute; width: 40%; right: 0; box-shadow: 0 1px 4px rgba(0,0,0,0.5), 0 5px 25px rgba(0,0,0,0.2); background-color: rgba(0, 0, 0, 0.9); color: #fff; padding: 20px; font-size: 20px; text-align: left;">
|
237
|
+
<h2>Iframe Backgrounds</h2>
|
238
|
+
<p>Since reveal.js runs on the web, you can easily embed other web content. Try interacting with the page in the background.</p>
|
239
|
+
</div>
|
229
240
|
</section>
|
230
241
|
|
231
242
|
<section>
|
232
243
|
<h2>Pretty Code</h2>
|
233
|
-
<pre><code class="hljs" data-trim
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
244
|
+
<pre><code class="hljs" data-trim data-line-numbers="4|9|4,8-11">
|
245
|
+
import React, { useState } from 'react';
|
246
|
+
|
247
|
+
function Example() {
|
248
|
+
const [count, setCount] = useState(0);
|
249
|
+
|
250
|
+
return (
|
251
|
+
<div>
|
252
|
+
<p>You clicked {count} times</p>
|
253
|
+
<button onClick={() => setCount(count + 1)}>
|
254
|
+
Click me
|
255
|
+
</button>
|
256
|
+
</div>
|
257
|
+
);
|
247
258
|
}
|
248
259
|
</code></pre>
|
249
260
|
<p>Code syntax highlighting courtesy of <a href="http://softwaremaniacs.org/soft/highlight/en/description/">highlight.js</a>.</p>
|
@@ -384,7 +395,6 @@ Reveal.addEventListener( 'customevent', function() {
|
|
384
395
|
|
385
396
|
</div>
|
386
397
|
|
387
|
-
<script src="lib/js/head.min.js"></script>
|
388
398
|
<script src="js/reveal.js"></script>
|
389
399
|
|
390
400
|
<script>
|
@@ -393,17 +403,16 @@ Reveal.addEventListener( 'customevent', function() {
|
|
393
403
|
Reveal.initialize({
|
394
404
|
controls: true,
|
395
405
|
progress: true,
|
396
|
-
history: true,
|
397
406
|
center: true,
|
407
|
+
hash: true,
|
398
408
|
|
399
409
|
transition: 'slide', // none/fade/slide/convex/concave/zoom
|
400
410
|
|
401
411
|
// More info https://github.com/hakimel/reveal.js#dependencies
|
402
412
|
dependencies: [
|
403
|
-
{ src: 'lib/js/classList.js', condition: function() { return !document.body.classList; } },
|
404
413
|
{ src: 'plugin/markdown/marked.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
|
405
414
|
{ src: 'plugin/markdown/markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
|
406
|
-
{ src: 'plugin/highlight/highlight.js'
|
415
|
+
{ src: 'plugin/highlight/highlight.js' },
|
407
416
|
{ src: 'plugin/search/search.js', async: true },
|
408
417
|
{ src: 'plugin/zoom-js/zoom.js', async: true },
|
409
418
|
{ src: 'plugin/notes/notes.js', async: true }
|
@@ -1,7 +1,11 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
const sass = require('node-sass');
|
2
|
+
|
3
|
+
module.exports = grunt => {
|
4
|
+
|
5
|
+
require('load-grunt-tasks')(grunt);
|
6
|
+
|
7
|
+
let port = grunt.option('port') || 8000;
|
8
|
+
let root = grunt.option('root') || '.';
|
5
9
|
|
6
10
|
if (!Array.isArray(root)) root = [root];
|
7
11
|
|
@@ -15,7 +19,7 @@ module.exports = function(grunt) {
|
|
15
19
|
' * http://revealjs.com\n' +
|
16
20
|
' * MIT licensed\n' +
|
17
21
|
' *\n' +
|
18
|
-
' * Copyright (C)
|
22
|
+
' * Copyright (C) 2020 Hakim El Hattab, http://hakim.se\n' +
|
19
23
|
' */'
|
20
24
|
},
|
21
25
|
|
@@ -35,6 +39,10 @@ module.exports = function(grunt) {
|
|
35
39
|
},
|
36
40
|
|
37
41
|
sass: {
|
42
|
+
options: {
|
43
|
+
implementation: sass,
|
44
|
+
sourceMap: false
|
45
|
+
},
|
38
46
|
core: {
|
39
47
|
src: 'css/reveal.scss',
|
40
48
|
dest: 'css/reveal.css'
|
@@ -85,10 +93,11 @@ module.exports = function(grunt) {
|
|
85
93
|
console: false,
|
86
94
|
unescape: false,
|
87
95
|
define: false,
|
88
|
-
exports: false
|
96
|
+
exports: false,
|
97
|
+
require: false
|
89
98
|
}
|
90
99
|
},
|
91
|
-
files: [ '
|
100
|
+
files: [ 'gruntfile.js', 'js/reveal.js' ]
|
92
101
|
},
|
93
102
|
|
94
103
|
connect: {
|
@@ -120,7 +129,7 @@ module.exports = function(grunt) {
|
|
120
129
|
|
121
130
|
watch: {
|
122
131
|
js: {
|
123
|
-
files: [ '
|
132
|
+
files: [ 'gruntfile.js', 'js/reveal.js' ],
|
124
133
|
tasks: 'js'
|
125
134
|
},
|
126
135
|
theme: {
|
@@ -136,6 +145,10 @@ module.exports = function(grunt) {
|
|
136
145
|
files: [ 'css/reveal.scss' ],
|
137
146
|
tasks: 'css-core'
|
138
147
|
},
|
148
|
+
test: {
|
149
|
+
files: [ 'test/*.html' ],
|
150
|
+
tasks: 'test'
|
151
|
+
},
|
139
152
|
html: {
|
140
153
|
files: root.map(path => path + '/*.html')
|
141
154
|
},
|
@@ -145,27 +158,10 @@ module.exports = function(grunt) {
|
|
145
158
|
options: {
|
146
159
|
livereload: true
|
147
160
|
}
|
148
|
-
},
|
149
|
-
|
150
|
-
retire: {
|
151
|
-
js: [ 'js/reveal.js', 'lib/js/*.js', 'plugin/**/*.js' ],
|
152
|
-
node: [ '.' ]
|
153
161
|
}
|
154
162
|
|
155
163
|
});
|
156
164
|
|
157
|
-
// Dependencies
|
158
|
-
grunt.loadNpmTasks( 'grunt-contrib-connect' );
|
159
|
-
grunt.loadNpmTasks( 'grunt-contrib-cssmin' );
|
160
|
-
grunt.loadNpmTasks( 'grunt-contrib-jshint' );
|
161
|
-
grunt.loadNpmTasks( 'grunt-contrib-qunit' );
|
162
|
-
grunt.loadNpmTasks( 'grunt-contrib-uglify' );
|
163
|
-
grunt.loadNpmTasks( 'grunt-contrib-watch' );
|
164
|
-
grunt.loadNpmTasks( 'grunt-autoprefixer' );
|
165
|
-
grunt.loadNpmTasks( 'grunt-retire' );
|
166
|
-
grunt.loadNpmTasks( 'grunt-sass' );
|
167
|
-
grunt.loadNpmTasks( 'grunt-zip' );
|
168
|
-
|
169
165
|
// Default task
|
170
166
|
grunt.registerTask( 'default', [ 'css', 'js' ] );
|
171
167
|
|
data/files/reveal.js/index.html
CHANGED
@@ -6,11 +6,12 @@
|
|
6
6
|
|
7
7
|
<title>reveal.js</title>
|
8
8
|
|
9
|
+
<link rel="stylesheet" href="css/reset.css">
|
9
10
|
<link rel="stylesheet" href="css/reveal.css">
|
10
11
|
<link rel="stylesheet" href="css/theme/black.css">
|
11
12
|
|
12
13
|
<!-- Theme used for syntax highlighting of code -->
|
13
|
-
<link rel="stylesheet" href="lib/css/
|
14
|
+
<link rel="stylesheet" href="lib/css/monokai.css">
|
14
15
|
|
15
16
|
<!-- Printing and PDF exports -->
|
16
17
|
<script>
|
@@ -29,7 +30,6 @@
|
|
29
30
|
</div>
|
30
31
|
</div>
|
31
32
|
|
32
|
-
<script src="lib/js/head.min.js"></script>
|
33
33
|
<script src="js/reveal.js"></script>
|
34
34
|
|
35
35
|
<script>
|
@@ -37,11 +37,12 @@
|
|
37
37
|
// - https://github.com/hakimel/reveal.js#configuration
|
38
38
|
// - https://github.com/hakimel/reveal.js#dependencies
|
39
39
|
Reveal.initialize({
|
40
|
+
hash: true,
|
40
41
|
dependencies: [
|
41
42
|
{ src: 'plugin/markdown/marked.js' },
|
42
43
|
{ src: 'plugin/markdown/markdown.js' },
|
43
|
-
{ src: 'plugin/
|
44
|
-
{ src: 'plugin/
|
44
|
+
{ src: 'plugin/highlight/highlight.js' },
|
45
|
+
{ src: 'plugin/notes/notes.js', async: true }
|
45
46
|
]
|
46
47
|
});
|
47
48
|
</script>
|
@@ -3,7 +3,7 @@
|
|
3
3
|
* http://revealjs.com
|
4
4
|
* MIT licensed
|
5
5
|
*
|
6
|
-
* Copyright (C)
|
6
|
+
* Copyright (C) 2020 Hakim El Hattab, http://hakim.se
|
7
7
|
*/
|
8
8
|
(function( root, factory ) {
|
9
9
|
if( typeof define === 'function' && define.amd ) {
|
@@ -26,14 +26,18 @@
|
|
26
26
|
var Reveal;
|
27
27
|
|
28
28
|
// The reveal.js version
|
29
|
-
var VERSION = '3.
|
29
|
+
var VERSION = '3.9.2';
|
30
30
|
|
31
31
|
var SLIDES_SELECTOR = '.slides section',
|
32
32
|
HORIZONTAL_SLIDES_SELECTOR = '.slides>section',
|
33
33
|
VERTICAL_SLIDES_SELECTOR = '.slides>section.present>section',
|
34
34
|
HOME_SLIDE_SELECTOR = '.slides>section:first-of-type',
|
35
|
+
|
35
36
|
UA = navigator.userAgent,
|
36
37
|
|
38
|
+
// Methods that may not be invoked via the postMessage API
|
39
|
+
POST_MESSAGE_METHOD_BLACKLIST = /registerPlugin|registerKeyboardShortcut|addKeyBinding|addEventListener/,
|
40
|
+
|
37
41
|
// Configuration defaults, can be overridden at initialization time
|
38
42
|
config = {
|
39
43
|
|
@@ -67,16 +71,36 @@
|
|
67
71
|
progress: true,
|
68
72
|
|
69
73
|
// Display the page number of the current slide
|
74
|
+
// - true: Show slide number
|
75
|
+
// - false: Hide slide number
|
76
|
+
//
|
77
|
+
// Can optionally be set as a string that specifies the number formatting:
|
78
|
+
// - "h.v": Horizontal . vertical slide number (default)
|
79
|
+
// - "h/v": Horizontal / vertical slide number
|
80
|
+
// - "c": Flattened slide number
|
81
|
+
// - "c/t": Flattened slide number / total slides
|
82
|
+
//
|
83
|
+
// Alternatively, you can provide a function that returns the slide
|
84
|
+
// number for the current slide. The function should take in a slide
|
85
|
+
// object and return an array with one string [slideNumber] or
|
86
|
+
// three strings [n1,delimiter,n2]. See #formatSlideNumber().
|
70
87
|
slideNumber: false,
|
71
88
|
|
89
|
+
// Can be used to limit the contexts in which the slide number appears
|
90
|
+
// - "all": Always show the slide number
|
91
|
+
// - "print": Only when printing to PDF
|
92
|
+
// - "speaker": Only in the speaker view
|
93
|
+
showSlideNumber: 'all',
|
94
|
+
|
72
95
|
// Use 1 based indexing for # links to match slide number (default is zero
|
73
96
|
// based)
|
74
97
|
hashOneBasedIndex: false,
|
75
98
|
|
76
|
-
//
|
77
|
-
|
99
|
+
// Add the current slide number to the URL hash so that reloading the
|
100
|
+
// page/copying the URL will return you to the same slide
|
101
|
+
hash: false,
|
78
102
|
|
79
|
-
// Push each slide change to the browser history
|
103
|
+
// Push each slide change to the browser history. Implies `hash: true`
|
80
104
|
history: false,
|
81
105
|
|
82
106
|
// Enable keyboard shortcuts for navigation
|
@@ -104,6 +128,32 @@
|
|
104
128
|
// Change the presentation direction to be RTL
|
105
129
|
rtl: false,
|
106
130
|
|
131
|
+
// Changes the behavior of our navigation directions.
|
132
|
+
//
|
133
|
+
// "default"
|
134
|
+
// Left/right arrow keys step between horizontal slides, up/down
|
135
|
+
// arrow keys step between vertical slides. Space key steps through
|
136
|
+
// all slides (both horizontal and vertical).
|
137
|
+
//
|
138
|
+
// "linear"
|
139
|
+
// Removes the up/down arrows. Left/right arrows step through all
|
140
|
+
// slides (both horizontal and vertical).
|
141
|
+
//
|
142
|
+
// "grid"
|
143
|
+
// When this is enabled, stepping left/right from a vertical stack
|
144
|
+
// to an adjacent vertical stack will land you at the same vertical
|
145
|
+
// index.
|
146
|
+
//
|
147
|
+
// Consider a deck with six slides ordered in two vertical stacks:
|
148
|
+
// 1.1 2.1
|
149
|
+
// 1.2 2.2
|
150
|
+
// 1.3 2.3
|
151
|
+
//
|
152
|
+
// If you're on slide 1.3 and navigate right, you will normally move
|
153
|
+
// from 1.3 -> 2.1. If "grid" is used, the same navigation takes you
|
154
|
+
// from 1.3 -> 2.3.
|
155
|
+
navigationMode: 'default',
|
156
|
+
|
107
157
|
// Randomizes the order of slides each time the presentation loads
|
108
158
|
shuffle: false,
|
109
159
|
|
@@ -134,6 +184,13 @@
|
|
134
184
|
// - false: No media will autoplay, regardless of individual setting
|
135
185
|
autoPlayMedia: null,
|
136
186
|
|
187
|
+
// Global override for preloading lazy-loaded iframes
|
188
|
+
// - null: Iframes with data-src AND data-preload will be loaded when within
|
189
|
+
// the viewDistance, iframes with only data-src will be loaded when visible
|
190
|
+
// - true: All iframes with data-src will be loaded when within the viewDistance
|
191
|
+
// - false: All iframes with data-src will be loaded only when visible
|
192
|
+
preloadIframes: null,
|
193
|
+
|
137
194
|
// Controls automatic progression to the next slide
|
138
195
|
// - 0: Auto-sliding only happens if the data-autoslide HTML attribute
|
139
196
|
// is present on the current slide or fragment
|
@@ -217,9 +274,20 @@
|
|
217
274
|
// Number of slides away from the current that are visible
|
218
275
|
viewDistance: 3,
|
219
276
|
|
277
|
+
// Number of slides away from the current that are visible on mobile
|
278
|
+
// devices. It is advisable to set this to a lower number than
|
279
|
+
// viewDistance in order to save resources.
|
280
|
+
mobileViewDistance: 2,
|
281
|
+
|
220
282
|
// The display mode that will be used to show slides
|
221
283
|
display: 'block',
|
222
284
|
|
285
|
+
// Hide cursor if inactive
|
286
|
+
hideInactiveCursor: true,
|
287
|
+
|
288
|
+
// Time before the cursor is hidden (in ms)
|
289
|
+
hideCursorTime: 5000,
|
290
|
+
|
223
291
|
// Script dependencies to load
|
224
292
|
dependencies: []
|
225
293
|
|
@@ -267,6 +335,12 @@
|
|
267
335
|
// Cached references to DOM elements
|
268
336
|
dom = {},
|
269
337
|
|
338
|
+
// A list of registered reveal.js plugins
|
339
|
+
plugins = {},
|
340
|
+
|
341
|
+
// List of asynchronously loaded reveal.js dependencies
|
342
|
+
asyncDependencies = [],
|
343
|
+
|
270
344
|
// Features supported by the browser, see #checkCapabilities()
|
271
345
|
features = {},
|
272
346
|
|
@@ -282,6 +356,12 @@
|
|
282
356
|
// Delays updates to the URL due to a Chrome thumbnailer bug
|
283
357
|
writeURLTimeout = 0,
|
284
358
|
|
359
|
+
// Is the mouse pointer currently hidden from view
|
360
|
+
cursorHidden = false,
|
361
|
+
|
362
|
+
// Timeout used to determine when the cursor is inactive
|
363
|
+
cursorInactiveTimeout = 0,
|
364
|
+
|
285
365
|
// Flags if the interaction event listeners are bound
|
286
366
|
eventsAreBound = false,
|
287
367
|
|
@@ -298,26 +378,14 @@
|
|
298
378
|
touch = {
|
299
379
|
startX: 0,
|
300
380
|
startY: 0,
|
301
|
-
startSpan: 0,
|
302
381
|
startCount: 0,
|
303
382
|
captured: false,
|
304
383
|
threshold: 40
|
305
384
|
},
|
306
385
|
|
307
|
-
//
|
308
|
-
|
309
|
-
|
310
|
-
'P': 'Previous slide',
|
311
|
-
'← , H': 'Navigate left',
|
312
|
-
'→ , L': 'Navigate right',
|
313
|
-
'↑ , K': 'Navigate up',
|
314
|
-
'↓ , J': 'Navigate down',
|
315
|
-
'Home': 'First slide',
|
316
|
-
'End': 'Last slide',
|
317
|
-
'B , .': 'Pause',
|
318
|
-
'F': 'Fullscreen',
|
319
|
-
'ESC, O': 'Slide overview'
|
320
|
-
},
|
386
|
+
// A key:value map of shortcut keyboard keys and descriptions of
|
387
|
+
// the actions they trigger, generated in #configure()
|
388
|
+
keyboardShortcuts = {},
|
321
389
|
|
322
390
|
// Holds custom key code mappings
|
323
391
|
registeredKeyBindings = {};
|
@@ -377,7 +445,7 @@
|
|
377
445
|
// Hide the address bar in mobile browsers
|
378
446
|
hideAddressBar();
|
379
447
|
|
380
|
-
// Loads
|
448
|
+
// Loads dependencies and continues to #start() once done
|
381
449
|
load();
|
382
450
|
|
383
451
|
}
|
@@ -388,7 +456,8 @@
|
|
388
456
|
*/
|
389
457
|
function checkCapabilities() {
|
390
458
|
|
391
|
-
isMobileDevice = /(iphone|ipod|ipad|android)/gi.test( UA )
|
459
|
+
isMobileDevice = /(iphone|ipod|ipad|android)/gi.test( UA ) ||
|
460
|
+
( navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1 ); // iPadOS
|
392
461
|
isChrome = /chrome/i.test( UA ) && !/edge/i.test( UA );
|
393
462
|
|
394
463
|
var testElement = document.createElement( 'div' );
|
@@ -432,58 +501,149 @@
|
|
432
501
|
function load() {
|
433
502
|
|
434
503
|
var scripts = [],
|
435
|
-
|
436
|
-
scriptsToPreload = 0;
|
504
|
+
scriptsToLoad = 0;
|
437
505
|
|
438
|
-
|
439
|
-
|
440
|
-
if(
|
441
|
-
|
442
|
-
|
506
|
+
config.dependencies.forEach( function( s ) {
|
507
|
+
// Load if there's no condition or the condition is truthy
|
508
|
+
if( !s.condition || s.condition() ) {
|
509
|
+
if( s.async ) {
|
510
|
+
asyncDependencies.push( s );
|
511
|
+
}
|
512
|
+
else {
|
513
|
+
scripts.push( s );
|
514
|
+
}
|
443
515
|
}
|
516
|
+
} );
|
517
|
+
|
518
|
+
if( scripts.length ) {
|
519
|
+
scriptsToLoad = scripts.length;
|
520
|
+
|
521
|
+
// Load synchronous scripts
|
522
|
+
scripts.forEach( function( s ) {
|
523
|
+
loadScript( s.src, function() {
|
524
|
+
|
525
|
+
if( typeof s.callback === 'function' ) s.callback();
|
526
|
+
|
527
|
+
if( --scriptsToLoad === 0 ) {
|
528
|
+
initPlugins();
|
529
|
+
}
|
530
|
+
|
531
|
+
} );
|
532
|
+
} );
|
533
|
+
}
|
534
|
+
else {
|
535
|
+
initPlugins();
|
536
|
+
}
|
537
|
+
|
538
|
+
}
|
539
|
+
|
540
|
+
/**
|
541
|
+
* Initializes our plugins and waits for them to be ready
|
542
|
+
* before proceeding.
|
543
|
+
*/
|
544
|
+
function initPlugins() {
|
545
|
+
|
546
|
+
var pluginsToInitialize = Object.keys( plugins ).length;
|
444
547
|
|
445
|
-
|
548
|
+
// If there are no plugins, skip this step
|
549
|
+
if( pluginsToInitialize === 0 ) {
|
550
|
+
loadAsyncDependencies();
|
446
551
|
}
|
552
|
+
// ... otherwise initialize plugins
|
553
|
+
else {
|
447
554
|
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
if( typeof s.callback === 'function' ) {
|
452
|
-
s.callback.apply( this );
|
555
|
+
var afterPlugInitialized = function() {
|
556
|
+
if( --pluginsToInitialize === 0 ) {
|
557
|
+
loadAsyncDependencies();
|
453
558
|
}
|
559
|
+
};
|
454
560
|
|
455
|
-
|
456
|
-
proceed();
|
457
|
-
}
|
458
|
-
});
|
459
|
-
}
|
561
|
+
for( var i in plugins ) {
|
460
562
|
|
461
|
-
|
462
|
-
var s = config.dependencies[i];
|
563
|
+
var plugin = plugins[i];
|
463
564
|
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
565
|
+
// If the plugin has an 'init' method, invoke it
|
566
|
+
if( typeof plugin.init === 'function' ) {
|
567
|
+
var callback = plugin.init();
|
568
|
+
|
569
|
+
// If the plugin returned a Promise, wait for it
|
570
|
+
if( callback && typeof callback.then === 'function' ) {
|
571
|
+
callback.then( afterPlugInitialized );
|
572
|
+
}
|
573
|
+
else {
|
574
|
+
afterPlugInitialized();
|
575
|
+
}
|
468
576
|
}
|
469
577
|
else {
|
470
|
-
|
578
|
+
afterPlugInitialized();
|
471
579
|
}
|
472
580
|
|
473
|
-
loadScript( s );
|
474
581
|
}
|
582
|
+
|
475
583
|
}
|
476
584
|
|
477
|
-
|
478
|
-
scriptsToPreload = scripts.length;
|
585
|
+
}
|
479
586
|
|
480
|
-
|
481
|
-
|
587
|
+
/**
|
588
|
+
* Loads all async reveal.js dependencies.
|
589
|
+
*/
|
590
|
+
function loadAsyncDependencies() {
|
591
|
+
|
592
|
+
if( asyncDependencies.length ) {
|
593
|
+
asyncDependencies.forEach( function( s ) {
|
594
|
+
loadScript( s.src, s.callback );
|
595
|
+
} );
|
482
596
|
}
|
483
|
-
|
484
|
-
|
597
|
+
|
598
|
+
start();
|
599
|
+
|
600
|
+
}
|
601
|
+
|
602
|
+
/**
|
603
|
+
* Loads a JavaScript file from the given URL and executes it.
|
604
|
+
*
|
605
|
+
* @param {string} url Address of the .js file to load
|
606
|
+
* @param {function} callback Method to invoke when the script
|
607
|
+
* has loaded and executed
|
608
|
+
*/
|
609
|
+
function loadScript( url, callback ) {
|
610
|
+
|
611
|
+
var script = document.createElement( 'script' );
|
612
|
+
script.type = 'text/javascript';
|
613
|
+
script.async = false;
|
614
|
+
script.defer = false;
|
615
|
+
script.src = url;
|
616
|
+
|
617
|
+
if( callback ) {
|
618
|
+
|
619
|
+
// Success callback
|
620
|
+
script.onload = script.onreadystatechange = function( event ) {
|
621
|
+
if( event.type === "load" || (/loaded|complete/.test( script.readyState ) ) ) {
|
622
|
+
|
623
|
+
// Kill event listeners
|
624
|
+
script.onload = script.onreadystatechange = script.onerror = null;
|
625
|
+
|
626
|
+
callback();
|
627
|
+
|
628
|
+
}
|
629
|
+
};
|
630
|
+
|
631
|
+
// Error callback
|
632
|
+
script.onerror = function( err ) {
|
633
|
+
|
634
|
+
// Kill event listeners
|
635
|
+
script.onload = script.onreadystatechange = script.onerror = null;
|
636
|
+
|
637
|
+
callback( new Error( 'Failed loading script: ' + script.src + '\n' + err) );
|
638
|
+
|
639
|
+
};
|
640
|
+
|
485
641
|
}
|
486
642
|
|
643
|
+
// Append the script at the end of <head>
|
644
|
+
var head = document.querySelector( 'head' );
|
645
|
+
head.insertBefore( script, head.lastChild );
|
646
|
+
|
487
647
|
}
|
488
648
|
|
489
649
|
/**
|
@@ -593,8 +753,7 @@
|
|
593
753
|
dom.speakerNotes.setAttribute( 'tabindex', '0' );
|
594
754
|
|
595
755
|
// Overlay graphic which is displayed during the paused mode
|
596
|
-
dom.pauseOverlay = createSingletonNode( dom.wrapper, 'div', 'pause-overlay', '<button class="resume-button">Resume presentation</button>' );
|
597
|
-
dom.resumeButton = dom.pauseOverlay.querySelector( '.resume-button' );
|
756
|
+
dom.pauseOverlay = createSingletonNode( dom.wrapper, 'div', 'pause-overlay', config.controls ? '<button class="resume-button">Resume presentation</button>' : null );
|
598
757
|
|
599
758
|
dom.wrapper.setAttribute( 'role', 'application' );
|
600
759
|
|
@@ -700,17 +859,10 @@
|
|
700
859
|
// Make sure stretch elements fit on slide
|
701
860
|
layoutSlideContents( slideWidth, slideHeight );
|
702
861
|
|
703
|
-
//
|
704
|
-
|
705
|
-
toArray( dom.wrapper.querySelectorAll(
|
706
|
-
|
707
|
-
|
708
|
-
if( hslide.classList.contains( 'stack' ) ) {
|
709
|
-
toArray( hslide.querySelectorAll( 'section' ) ).forEach( function( vslide, v ) {
|
710
|
-
vslide.setAttribute( 'data-index-h', h );
|
711
|
-
vslide.setAttribute( 'data-index-v', v );
|
712
|
-
} );
|
713
|
-
}
|
862
|
+
// Compute slide numbers now, before we start duplicating slides
|
863
|
+
var doingSlideNumbers = config.slideNumber && /all|print/i.test( config.showSlideNumber );
|
864
|
+
toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR ) ).forEach( function( slide ) {
|
865
|
+
slide.setAttribute( 'data-slide-number', getSlideNumber( slide ) );
|
714
866
|
} );
|
715
867
|
|
716
868
|
// Slide and slide background layout
|
@@ -781,14 +933,11 @@
|
|
781
933
|
}
|
782
934
|
|
783
935
|
// Inject slide numbers if `slideNumbers` are enabled
|
784
|
-
if(
|
785
|
-
var slideNumberH = parseInt( slide.getAttribute( 'data-index-h' ), 10 ) + 1,
|
786
|
-
slideNumberV = parseInt( slide.getAttribute( 'data-index-v' ), 10 ) + 1;
|
787
|
-
|
936
|
+
if( doingSlideNumbers ) {
|
788
937
|
var numberElement = document.createElement( 'div' );
|
789
938
|
numberElement.classList.add( 'slide-number' );
|
790
939
|
numberElement.classList.add( 'slide-number-pdf' );
|
791
|
-
numberElement.innerHTML =
|
940
|
+
numberElement.innerHTML = slide.getAttribute( 'data-slide-number' );
|
792
941
|
page.appendChild( numberElement );
|
793
942
|
}
|
794
943
|
|
@@ -1068,24 +1217,35 @@
|
|
1068
1217
|
if( data.backgroundColor ) element.style.backgroundColor = data.backgroundColor;
|
1069
1218
|
if( data.backgroundTransition ) element.setAttribute( 'data-background-transition', data.backgroundTransition );
|
1070
1219
|
|
1220
|
+
if( slide.hasAttribute( 'data-preload' ) ) element.setAttribute( 'data-preload', '' );
|
1221
|
+
|
1071
1222
|
// Background image options are set on the content wrapper
|
1072
1223
|
if( data.backgroundSize ) contentElement.style.backgroundSize = data.backgroundSize;
|
1073
1224
|
if( data.backgroundRepeat ) contentElement.style.backgroundRepeat = data.backgroundRepeat;
|
1074
1225
|
if( data.backgroundPosition ) contentElement.style.backgroundPosition = data.backgroundPosition;
|
1075
1226
|
if( data.backgroundOpacity ) contentElement.style.opacity = data.backgroundOpacity;
|
1076
1227
|
|
1077
|
-
// If this slide has a background color, add a class that
|
1228
|
+
// If this slide has a background color, we add a class that
|
1078
1229
|
// signals if it is light or dark. If the slide has no background
|
1079
|
-
// color, no class will be
|
1080
|
-
var
|
1081
|
-
|
1082
|
-
|
1230
|
+
// color, no class will be added
|
1231
|
+
var contrastColor = data.backgroundColor;
|
1232
|
+
|
1233
|
+
// If no bg color was found, check the computed background
|
1234
|
+
if( !contrastColor ) {
|
1235
|
+
var computedBackgroundStyle = window.getComputedStyle( element );
|
1236
|
+
if( computedBackgroundStyle && computedBackgroundStyle.backgroundColor ) {
|
1237
|
+
contrastColor = computedBackgroundStyle.backgroundColor;
|
1238
|
+
}
|
1239
|
+
}
|
1240
|
+
|
1241
|
+
if( contrastColor ) {
|
1242
|
+
var rgb = colorToRgb( contrastColor );
|
1083
1243
|
|
1084
1244
|
// Ignore fully transparent backgrounds. Some browsers return
|
1085
1245
|
// rgba(0,0,0,0) when reading the computed background color of
|
1086
1246
|
// an element with no background
|
1087
1247
|
if( rgb && rgb.a !== 0 ) {
|
1088
|
-
if( colorBrightness(
|
1248
|
+
if( colorBrightness( contrastColor ) < 128 ) {
|
1089
1249
|
slide.classList.add( 'has-dark-background' );
|
1090
1250
|
}
|
1091
1251
|
else {
|
@@ -1118,7 +1278,20 @@
|
|
1118
1278
|
|
1119
1279
|
// Check if the requested method can be found
|
1120
1280
|
if( data.method && typeof Reveal[data.method] === 'function' ) {
|
1121
|
-
|
1281
|
+
|
1282
|
+
if( POST_MESSAGE_METHOD_BLACKLIST.test( data.method ) === false ) {
|
1283
|
+
|
1284
|
+
var result = Reveal[data.method].apply( Reveal, data.args );
|
1285
|
+
|
1286
|
+
// Dispatch a postMessage event with the returned value from
|
1287
|
+
// our method invocation for getter functions
|
1288
|
+
dispatchPostMessage( 'callback', { method: data.method, result: result } );
|
1289
|
+
|
1290
|
+
}
|
1291
|
+
else {
|
1292
|
+
console.warn( 'reveal.js: "'+ data.method +'" is is blacklisted from the postMessage API' );
|
1293
|
+
}
|
1294
|
+
|
1122
1295
|
}
|
1123
1296
|
}
|
1124
1297
|
}, false );
|
@@ -1208,6 +1381,18 @@
|
|
1208
1381
|
disableRollingLinks();
|
1209
1382
|
}
|
1210
1383
|
|
1384
|
+
// Auto-hide the mouse pointer when its inactive
|
1385
|
+
if( config.hideInactiveCursor ) {
|
1386
|
+
document.addEventListener( 'mousemove', onDocumentCursorActive, false );
|
1387
|
+
document.addEventListener( 'mousedown', onDocumentCursorActive, false );
|
1388
|
+
}
|
1389
|
+
else {
|
1390
|
+
showCursor();
|
1391
|
+
|
1392
|
+
document.removeEventListener( 'mousemove', onDocumentCursorActive, false );
|
1393
|
+
document.removeEventListener( 'mousedown', onDocumentCursorActive, false );
|
1394
|
+
}
|
1395
|
+
|
1211
1396
|
// Iframe link previews
|
1212
1397
|
if( config.previewLinks ) {
|
1213
1398
|
enablePreviewLinks();
|
@@ -1255,6 +1440,34 @@
|
|
1255
1440
|
|
1256
1441
|
dom.slideNumber.style.display = slideNumberDisplay;
|
1257
1442
|
|
1443
|
+
// Add the navigation mode to the DOM so we can adjust styling
|
1444
|
+
if( config.navigationMode !== 'default' ) {
|
1445
|
+
dom.wrapper.setAttribute( 'data-navigation-mode', config.navigationMode );
|
1446
|
+
}
|
1447
|
+
else {
|
1448
|
+
dom.wrapper.removeAttribute( 'data-navigation-mode' );
|
1449
|
+
}
|
1450
|
+
|
1451
|
+
// Define our contextual list of keyboard shortcuts
|
1452
|
+
if( config.navigationMode === 'linear' ) {
|
1453
|
+
keyboardShortcuts['→ , ↓ , SPACE , N , L , J'] = 'Next slide';
|
1454
|
+
keyboardShortcuts['← , ↑ , P , H , K'] = 'Previous slide';
|
1455
|
+
}
|
1456
|
+
else {
|
1457
|
+
keyboardShortcuts['N , SPACE'] = 'Next slide';
|
1458
|
+
keyboardShortcuts['P'] = 'Previous slide';
|
1459
|
+
keyboardShortcuts['← , H'] = 'Navigate left';
|
1460
|
+
keyboardShortcuts['→ , L'] = 'Navigate right';
|
1461
|
+
keyboardShortcuts['↑ , K'] = 'Navigate up';
|
1462
|
+
keyboardShortcuts['↓ , J'] = 'Navigate down';
|
1463
|
+
}
|
1464
|
+
|
1465
|
+
keyboardShortcuts['Home , Shift ←'] = 'First slide';
|
1466
|
+
keyboardShortcuts['End , Shift →'] = 'Last slide';
|
1467
|
+
keyboardShortcuts['B , .'] = 'Pause';
|
1468
|
+
keyboardShortcuts['F'] = 'Fullscreen';
|
1469
|
+
keyboardShortcuts['ESC, O'] = 'Slide overview';
|
1470
|
+
|
1258
1471
|
sync();
|
1259
1472
|
|
1260
1473
|
}
|
@@ -1299,7 +1512,7 @@
|
|
1299
1512
|
dom.progress.addEventListener( 'click', onProgressClicked, false );
|
1300
1513
|
}
|
1301
1514
|
|
1302
|
-
dom.
|
1515
|
+
dom.pauseOverlay.addEventListener( 'click', resume, false );
|
1303
1516
|
|
1304
1517
|
if( config.focusBodyOnPageVisibilityChange ) {
|
1305
1518
|
var visibilityChange;
|
@@ -1364,7 +1577,7 @@
|
|
1364
1577
|
dom.wrapper.removeEventListener( 'touchmove', onTouchMove, false );
|
1365
1578
|
dom.wrapper.removeEventListener( 'touchend', onTouchEnd, false );
|
1366
1579
|
|
1367
|
-
dom.
|
1580
|
+
dom.pauseOverlay.removeEventListener( 'click', resume, false );
|
1368
1581
|
|
1369
1582
|
if ( config.progress && dom.progress ) {
|
1370
1583
|
dom.progress.removeEventListener( 'click', onProgressClicked, false );
|
@@ -1381,6 +1594,53 @@
|
|
1381
1594
|
|
1382
1595
|
}
|
1383
1596
|
|
1597
|
+
/**
|
1598
|
+
* Registers a new plugin with this reveal.js instance.
|
1599
|
+
*
|
1600
|
+
* reveal.js waits for all regisered plugins to initialize
|
1601
|
+
* before considering itself ready, as long as the plugin
|
1602
|
+
* is registered before calling `Reveal.initialize()`.
|
1603
|
+
*/
|
1604
|
+
function registerPlugin( id, plugin ) {
|
1605
|
+
|
1606
|
+
if( plugins[id] === undefined ) {
|
1607
|
+
plugins[id] = plugin;
|
1608
|
+
|
1609
|
+
// If a plugin is registered after reveal.js is loaded,
|
1610
|
+
// initialize it right away
|
1611
|
+
if( loaded && typeof plugin.init === 'function' ) {
|
1612
|
+
plugin.init();
|
1613
|
+
}
|
1614
|
+
}
|
1615
|
+
else {
|
1616
|
+
console.warn( 'reveal.js: "'+ id +'" plugin has already been registered' );
|
1617
|
+
}
|
1618
|
+
|
1619
|
+
}
|
1620
|
+
|
1621
|
+
/**
|
1622
|
+
* Checks if a specific plugin has been registered.
|
1623
|
+
*
|
1624
|
+
* @param {String} id Unique plugin identifier
|
1625
|
+
*/
|
1626
|
+
function hasPlugin( id ) {
|
1627
|
+
|
1628
|
+
return !!plugins[id];
|
1629
|
+
|
1630
|
+
}
|
1631
|
+
|
1632
|
+
/**
|
1633
|
+
* Returns the specific plugin instance, if a plugin
|
1634
|
+
* with the given ID has been registered.
|
1635
|
+
*
|
1636
|
+
* @param {String} id Unique plugin identifier
|
1637
|
+
*/
|
1638
|
+
function getPlugin( id ) {
|
1639
|
+
|
1640
|
+
return plugins[id];
|
1641
|
+
|
1642
|
+
}
|
1643
|
+
|
1384
1644
|
/**
|
1385
1645
|
* Add a custom key binding with optional description to
|
1386
1646
|
* be added to the help screen.
|
@@ -1669,11 +1929,19 @@
|
|
1669
1929
|
// Change the .stretch element height to 0 in order find the height of all
|
1670
1930
|
// the other elements
|
1671
1931
|
element.style.height = '0px';
|
1932
|
+
|
1933
|
+
// In Overview mode, the parent (.slide) height is set of 700px.
|
1934
|
+
// Restore it temporarily to its natural height.
|
1935
|
+
element.parentNode.style.height = 'auto';
|
1936
|
+
|
1672
1937
|
newHeight = height - element.parentNode.offsetHeight;
|
1673
1938
|
|
1674
1939
|
// Restore the old height, just in case
|
1675
1940
|
element.style.height = oldHeight + 'px';
|
1676
1941
|
|
1942
|
+
// Clear the parent (.slide) height. .removeProperty works in IE9+
|
1943
|
+
element.parentNode.style.removeProperty('height');
|
1944
|
+
|
1677
1945
|
return newHeight;
|
1678
1946
|
}
|
1679
1947
|
|
@@ -1690,15 +1958,6 @@
|
|
1690
1958
|
|
1691
1959
|
}
|
1692
1960
|
|
1693
|
-
/**
|
1694
|
-
* Check if this instance is being used to print a PDF with fragments.
|
1695
|
-
*/
|
1696
|
-
function isPrintingPDFFragments() {
|
1697
|
-
|
1698
|
-
return ( /print-pdf-fragments/gi ).test( window.location.search );
|
1699
|
-
|
1700
|
-
}
|
1701
|
-
|
1702
1961
|
/**
|
1703
1962
|
* Hides the address bar if we're on a mobile device.
|
1704
1963
|
*/
|
@@ -1737,8 +1996,25 @@
|
|
1737
1996
|
|
1738
1997
|
// If we're in an iframe, post each reveal.js event to the
|
1739
1998
|
// parent window. Used by the notes plugin
|
1999
|
+
dispatchPostMessage( type );
|
2000
|
+
|
2001
|
+
}
|
2002
|
+
|
2003
|
+
/**
|
2004
|
+
* Dispatched a postMessage of the given type from our window.
|
2005
|
+
*/
|
2006
|
+
function dispatchPostMessage( type, data ) {
|
2007
|
+
|
1740
2008
|
if( config.postMessageEvents && window.parent !== window.self ) {
|
1741
|
-
|
2009
|
+
var message = {
|
2010
|
+
namespace: 'reveal',
|
2011
|
+
eventName: type,
|
2012
|
+
state: getState()
|
2013
|
+
};
|
2014
|
+
|
2015
|
+
extend( message, data );
|
2016
|
+
|
2017
|
+
window.parent.postMessage( JSON.stringify( message ), '*' );
|
1742
2018
|
}
|
1743
2019
|
|
1744
2020
|
}
|
@@ -1962,8 +2238,20 @@
|
|
1962
2238
|
|
1963
2239
|
if( !config.disableLayout ) {
|
1964
2240
|
|
2241
|
+
// On some mobile devices '100vh' is taller than the visible
|
2242
|
+
// viewport which leads to part of the presentation being
|
2243
|
+
// cut off. To work around this we define our own '--vh' custom
|
2244
|
+
// property where 100x adds up to the correct height.
|
2245
|
+
//
|
2246
|
+
// https://css-tricks.com/the-trick-to-viewport-units-on-mobile/
|
2247
|
+
if( isMobileDevice ) {
|
2248
|
+
document.documentElement.style.setProperty( '--vh', ( window.innerHeight * 0.01 ) + 'px' );
|
2249
|
+
}
|
2250
|
+
|
1965
2251
|
var size = getComputedSlideSize();
|
1966
2252
|
|
2253
|
+
var oldScale = scale;
|
2254
|
+
|
1967
2255
|
// Layout the contents of the slides
|
1968
2256
|
layoutSlideContents( config.width, config.height );
|
1969
2257
|
|
@@ -1987,10 +2275,12 @@
|
|
1987
2275
|
transformSlides( { layout: '' } );
|
1988
2276
|
}
|
1989
2277
|
else {
|
1990
|
-
//
|
1991
|
-
//
|
1992
|
-
// in text layout
|
1993
|
-
|
2278
|
+
// Zoom Scaling
|
2279
|
+
// Content remains crisp no matter how much we scale. Side
|
2280
|
+
// effects are minor differences in text layout and iframe
|
2281
|
+
// viewports changing size. A 200x200 iframe viewport in a
|
2282
|
+
// 2x zoomed presentation ends up having a 400x400 viewport.
|
2283
|
+
if( scale > 1 && features.zoom && window.devicePixelRatio < 2 ) {
|
1994
2284
|
dom.slides.style.zoom = scale;
|
1995
2285
|
dom.slides.style.left = '';
|
1996
2286
|
dom.slides.style.top = '';
|
@@ -1998,7 +2288,10 @@
|
|
1998
2288
|
dom.slides.style.right = '';
|
1999
2289
|
transformSlides( { layout: '' } );
|
2000
2290
|
}
|
2001
|
-
//
|
2291
|
+
// Transform Scaling
|
2292
|
+
// Content layout remains the exact same when scaled up.
|
2293
|
+
// Side effect is content becoming blurred, especially with
|
2294
|
+
// high scale values on ldpi screens.
|
2002
2295
|
else {
|
2003
2296
|
dom.slides.style.zoom = '';
|
2004
2297
|
dom.slides.style.left = '50%';
|
@@ -2036,6 +2329,13 @@
|
|
2036
2329
|
|
2037
2330
|
}
|
2038
2331
|
|
2332
|
+
if( oldScale !== scale ) {
|
2333
|
+
dispatchEvent( 'resize', {
|
2334
|
+
'oldScale': oldScale,
|
2335
|
+
'scale': scale,
|
2336
|
+
'size': size
|
2337
|
+
} );
|
2338
|
+
}
|
2039
2339
|
}
|
2040
2340
|
|
2041
2341
|
updateProgress();
|
@@ -2360,34 +2660,37 @@
|
|
2360
2660
|
}
|
2361
2661
|
|
2362
2662
|
/**
|
2363
|
-
* Return a hash URL that will resolve to the
|
2663
|
+
* Return a hash URL that will resolve to the given slide location.
|
2664
|
+
*
|
2665
|
+
* @param {HTMLElement} [slide=currentSlide] The slide to link to
|
2364
2666
|
*/
|
2365
|
-
function locationHash() {
|
2667
|
+
function locationHash( slide ) {
|
2366
2668
|
|
2367
2669
|
var url = '/';
|
2368
2670
|
|
2369
2671
|
// Attempt to create a named link based on the slide's ID
|
2370
|
-
var
|
2672
|
+
var s = slide || currentSlide;
|
2673
|
+
var id = s ? s.getAttribute( 'id' ) : null;
|
2371
2674
|
if( id ) {
|
2372
2675
|
id = encodeURIComponent( id );
|
2373
2676
|
}
|
2374
2677
|
|
2375
|
-
var
|
2376
|
-
if( config.fragmentInURL ) {
|
2377
|
-
|
2678
|
+
var index = getIndices( slide );
|
2679
|
+
if( !config.fragmentInURL ) {
|
2680
|
+
index.f = undefined;
|
2378
2681
|
}
|
2379
2682
|
|
2380
2683
|
// If the current slide has an ID, use that as a named link,
|
2381
2684
|
// but we don't support named links with a fragment index
|
2382
|
-
if( typeof id === 'string' && id.length &&
|
2685
|
+
if( typeof id === 'string' && id.length && index.f === undefined ) {
|
2383
2686
|
url = '/' + id;
|
2384
2687
|
}
|
2385
2688
|
// Otherwise use the /h/v index
|
2386
2689
|
else {
|
2387
2690
|
var hashIndexBase = config.hashOneBasedIndex ? 1 : 0;
|
2388
|
-
if(
|
2389
|
-
if(
|
2390
|
-
if(
|
2691
|
+
if( index.h > 0 || index.v > 0 || index.f !== undefined ) url += index.h + hashIndexBase;
|
2692
|
+
if( index.v > 0 || index.f !== undefined ) url += '/' + (index.v + hashIndexBase );
|
2693
|
+
if( index.f !== undefined ) url += '/' + index.f;
|
2391
2694
|
}
|
2392
2695
|
|
2393
2696
|
return url;
|
@@ -2434,6 +2737,32 @@
|
|
2434
2737
|
|
2435
2738
|
}
|
2436
2739
|
|
2740
|
+
/**
|
2741
|
+
* Shows the mouse pointer after it has been hidden with
|
2742
|
+
* #hideCursor.
|
2743
|
+
*/
|
2744
|
+
function showCursor() {
|
2745
|
+
|
2746
|
+
if( cursorHidden ) {
|
2747
|
+
cursorHidden = false;
|
2748
|
+
dom.wrapper.style.cursor = '';
|
2749
|
+
}
|
2750
|
+
|
2751
|
+
}
|
2752
|
+
|
2753
|
+
/**
|
2754
|
+
* Hides the mouse pointer when it's on top of the .reveal
|
2755
|
+
* container.
|
2756
|
+
*/
|
2757
|
+
function hideCursor() {
|
2758
|
+
|
2759
|
+
if( cursorHidden === false ) {
|
2760
|
+
cursorHidden = true;
|
2761
|
+
dom.wrapper.style.cursor = 'none';
|
2762
|
+
}
|
2763
|
+
|
2764
|
+
}
|
2765
|
+
|
2437
2766
|
/**
|
2438
2767
|
* Enters the paused mode which fades everything on screen to
|
2439
2768
|
* black.
|
@@ -2576,28 +2905,6 @@
|
|
2576
2905
|
|
2577
2906
|
layout();
|
2578
2907
|
|
2579
|
-
// Apply the new state
|
2580
|
-
stateLoop: for( var i = 0, len = state.length; i < len; i++ ) {
|
2581
|
-
// Check if this state existed on the previous slide. If it
|
2582
|
-
// did, we will avoid adding it repeatedly
|
2583
|
-
for( var j = 0; j < stateBefore.length; j++ ) {
|
2584
|
-
if( stateBefore[j] === state[i] ) {
|
2585
|
-
stateBefore.splice( j, 1 );
|
2586
|
-
continue stateLoop;
|
2587
|
-
}
|
2588
|
-
}
|
2589
|
-
|
2590
|
-
document.documentElement.classList.add( state[i] );
|
2591
|
-
|
2592
|
-
// Dispatch custom event matching the state's name
|
2593
|
-
dispatchEvent( state[i] );
|
2594
|
-
}
|
2595
|
-
|
2596
|
-
// Clean up the remains of the previous state
|
2597
|
-
while( stateBefore.length ) {
|
2598
|
-
document.documentElement.classList.remove( stateBefore.pop() );
|
2599
|
-
}
|
2600
|
-
|
2601
2908
|
// Update the overview if it's currently active
|
2602
2909
|
if( isOverview() ) {
|
2603
2910
|
updateOverview();
|
@@ -2646,6 +2953,28 @@
|
|
2646
2953
|
}
|
2647
2954
|
}
|
2648
2955
|
|
2956
|
+
// Apply the new state
|
2957
|
+
stateLoop: for( var i = 0, len = state.length; i < len; i++ ) {
|
2958
|
+
// Check if this state existed on the previous slide. If it
|
2959
|
+
// did, we will avoid adding it repeatedly
|
2960
|
+
for( var j = 0; j < stateBefore.length; j++ ) {
|
2961
|
+
if( stateBefore[j] === state[i] ) {
|
2962
|
+
stateBefore.splice( j, 1 );
|
2963
|
+
continue stateLoop;
|
2964
|
+
}
|
2965
|
+
}
|
2966
|
+
|
2967
|
+
document.documentElement.classList.add( state[i] );
|
2968
|
+
|
2969
|
+
// Dispatch custom event matching the state's name
|
2970
|
+
dispatchEvent( state[i] );
|
2971
|
+
}
|
2972
|
+
|
2973
|
+
// Clean up the remains of the previous state
|
2974
|
+
while( stateBefore.length ) {
|
2975
|
+
document.documentElement.classList.remove( stateBefore.pop() );
|
2976
|
+
}
|
2977
|
+
|
2649
2978
|
if( slideChanged ) {
|
2650
2979
|
dispatchEvent( 'slidechanged', {
|
2651
2980
|
'indexh': indexh,
|
@@ -2671,6 +3000,7 @@
|
|
2671
3000
|
updateParallax();
|
2672
3001
|
updateSlideNumber();
|
2673
3002
|
updateNotes();
|
3003
|
+
updateFragments();
|
2674
3004
|
|
2675
3005
|
// Update the URL hash
|
2676
3006
|
writeURL();
|
@@ -2743,14 +3073,17 @@
|
|
2743
3073
|
*/
|
2744
3074
|
function syncSlide( slide ) {
|
2745
3075
|
|
3076
|
+
// Default to the current slide
|
3077
|
+
slide = slide || currentSlide;
|
3078
|
+
|
2746
3079
|
syncBackground( slide );
|
2747
3080
|
syncFragments( slide );
|
2748
3081
|
|
3082
|
+
loadSlide( slide );
|
3083
|
+
|
2749
3084
|
updateBackground();
|
2750
3085
|
updateNotes();
|
2751
3086
|
|
2752
|
-
loadSlide( slide );
|
2753
|
-
|
2754
3087
|
}
|
2755
3088
|
|
2756
3089
|
/**
|
@@ -2759,10 +3092,14 @@
|
|
2759
3092
|
* after reveal.js has already initialized.
|
2760
3093
|
*
|
2761
3094
|
* @param {HTMLElement} slide
|
3095
|
+
* @return {Array} a list of the HTML fragments that were synced
|
2762
3096
|
*/
|
2763
3097
|
function syncFragments( slide ) {
|
2764
3098
|
|
2765
|
-
|
3099
|
+
// Default to the current slide
|
3100
|
+
slide = slide || currentSlide;
|
3101
|
+
|
3102
|
+
return sortFragments( slide.querySelectorAll( '.fragment' ) );
|
2766
3103
|
|
2767
3104
|
}
|
2768
3105
|
|
@@ -2895,14 +3232,11 @@
|
|
2895
3232
|
element.classList.add( reverse ? 'future' : 'past' );
|
2896
3233
|
|
2897
3234
|
if( config.fragments ) {
|
2898
|
-
|
2899
|
-
|
2900
|
-
|
2901
|
-
|
2902
|
-
|
2903
|
-
pastFragment.classList.add( 'visible' );
|
2904
|
-
pastFragment.classList.remove( 'current-fragment' );
|
2905
|
-
}
|
3235
|
+
// Show all fragments in prior slides
|
3236
|
+
toArray( element.querySelectorAll( '.fragment' ) ).forEach( function( fragment ) {
|
3237
|
+
fragment.classList.add( 'visible' );
|
3238
|
+
fragment.classList.remove( 'current-fragment' );
|
3239
|
+
} );
|
2906
3240
|
}
|
2907
3241
|
}
|
2908
3242
|
else if( i > index ) {
|
@@ -2910,14 +3244,11 @@
|
|
2910
3244
|
element.classList.add( reverse ? 'past' : 'future' );
|
2911
3245
|
|
2912
3246
|
if( config.fragments ) {
|
2913
|
-
|
2914
|
-
|
2915
|
-
|
2916
|
-
|
2917
|
-
|
2918
|
-
futureFragment.classList.remove( 'visible' );
|
2919
|
-
futureFragment.classList.remove( 'current-fragment' );
|
2920
|
-
}
|
3247
|
+
// Hide all fragments in future slides
|
3248
|
+
toArray( element.querySelectorAll( '.fragment.visible' ) ).forEach( function( fragment ) {
|
3249
|
+
fragment.classList.remove( 'visible' );
|
3250
|
+
fragment.classList.remove( 'current-fragment' );
|
3251
|
+
} );
|
2921
3252
|
}
|
2922
3253
|
}
|
2923
3254
|
}
|
@@ -2964,9 +3295,10 @@
|
|
2964
3295
|
// be visible
|
2965
3296
|
var viewDistance = isOverview() ? 10 : config.viewDistance;
|
2966
3297
|
|
2967
|
-
//
|
3298
|
+
// Shorten the view distance on devices that typically have
|
3299
|
+
// less resources
|
2968
3300
|
if( isMobileDevice ) {
|
2969
|
-
viewDistance = isOverview() ? 6 :
|
3301
|
+
viewDistance = isOverview() ? 6 : config.mobileViewDistance;
|
2970
3302
|
}
|
2971
3303
|
|
2972
3304
|
// All slides need to be visible when exporting to PDF
|
@@ -3018,7 +3350,7 @@
|
|
3018
3350
|
}
|
3019
3351
|
|
3020
3352
|
// Flag if there are ANY vertical slides, anywhere in the deck
|
3021
|
-
if(
|
3353
|
+
if( hasVerticalSlides() ) {
|
3022
3354
|
dom.wrapper.classList.add( 'has-vertical-slides' );
|
3023
3355
|
}
|
3024
3356
|
else {
|
@@ -3026,7 +3358,7 @@
|
|
3026
3358
|
}
|
3027
3359
|
|
3028
3360
|
// Flag if there are ANY horizontal slides, anywhere in the deck
|
3029
|
-
if(
|
3361
|
+
if( hasHorizontalSlides() ) {
|
3030
3362
|
dom.wrapper.classList.add( 'has-horizontal-slides' );
|
3031
3363
|
}
|
3032
3364
|
else {
|
@@ -3096,22 +3428,32 @@
|
|
3096
3428
|
|
3097
3429
|
|
3098
3430
|
/**
|
3099
|
-
* Updates the slide number
|
3100
|
-
*
|
3101
|
-
* The following slide number formats are available:
|
3102
|
-
* "h.v": horizontal . vertical slide number (default)
|
3103
|
-
* "h/v": horizontal / vertical slide number
|
3104
|
-
* "c": flattened slide number
|
3105
|
-
* "c/t": flattened slide number / total slides
|
3431
|
+
* Updates the slide number to match the current slide.
|
3106
3432
|
*/
|
3107
3433
|
function updateSlideNumber() {
|
3108
3434
|
|
3109
3435
|
// Update slide number if enabled
|
3110
3436
|
if( config.slideNumber && dom.slideNumber ) {
|
3437
|
+
dom.slideNumber.innerHTML = getSlideNumber();
|
3438
|
+
}
|
3439
|
+
|
3440
|
+
}
|
3441
|
+
|
3442
|
+
/**
|
3443
|
+
* Returns the HTML string corresponding to the current slide number,
|
3444
|
+
* including formatting.
|
3445
|
+
*/
|
3446
|
+
function getSlideNumber( slide ) {
|
3111
3447
|
|
3112
|
-
|
3113
|
-
|
3448
|
+
var value;
|
3449
|
+
var format = 'h.v';
|
3450
|
+
if( slide === undefined ) {
|
3451
|
+
slide = currentSlide;
|
3452
|
+
}
|
3114
3453
|
|
3454
|
+
if ( typeof config.slideNumber === 'function' ) {
|
3455
|
+
value = config.slideNumber( slide );
|
3456
|
+
} else {
|
3115
3457
|
// Check if a custom number format is available
|
3116
3458
|
if( typeof config.slideNumber === 'string' ) {
|
3117
3459
|
format = config.slideNumber;
|
@@ -3123,25 +3465,25 @@
|
|
3123
3465
|
format = 'c';
|
3124
3466
|
}
|
3125
3467
|
|
3468
|
+
value = [];
|
3126
3469
|
switch( format ) {
|
3127
3470
|
case 'c':
|
3128
|
-
value.push( getSlidePastCount() + 1 );
|
3471
|
+
value.push( getSlidePastCount( slide ) + 1 );
|
3129
3472
|
break;
|
3130
3473
|
case 'c/t':
|
3131
|
-
value.push( getSlidePastCount() + 1, '/', getTotalSlides() );
|
3132
|
-
break;
|
3133
|
-
case 'h/v':
|
3134
|
-
value.push( indexh + 1 );
|
3135
|
-
if( isVerticalSlide() ) value.push( '/', indexv + 1 );
|
3474
|
+
value.push( getSlidePastCount( slide ) + 1, '/', getTotalSlides() );
|
3136
3475
|
break;
|
3137
3476
|
default:
|
3138
|
-
|
3139
|
-
|
3477
|
+
var indices = getIndices( slide );
|
3478
|
+
value.push( indices.h + 1 );
|
3479
|
+
var sep = format === 'h/v' ? '/' : '.';
|
3480
|
+
if( isVerticalSlide( slide ) ) value.push( sep, indices.v + 1 );
|
3140
3481
|
}
|
3141
|
-
|
3142
|
-
dom.slideNumber.innerHTML = formatSlideNumber( value[0], value[1], value[2] );
|
3143
3482
|
}
|
3144
3483
|
|
3484
|
+
var url = '#' + locationHash( slide );
|
3485
|
+
return formatSlideNumber( value[0], value[1], value[2], url );
|
3486
|
+
|
3145
3487
|
}
|
3146
3488
|
|
3147
3489
|
/**
|
@@ -3151,11 +3493,14 @@
|
|
3151
3493
|
* @param {number} a Current slide
|
3152
3494
|
* @param {string} delimiter Character to separate slide numbers
|
3153
3495
|
* @param {(number|*)} b Total slides
|
3496
|
+
* @param {HTMLElement} [url='#'+locationHash()] The url to link to
|
3154
3497
|
* @return {string} HTML string fragment
|
3155
3498
|
*/
|
3156
|
-
function formatSlideNumber( a, delimiter, b ) {
|
3499
|
+
function formatSlideNumber( a, delimiter, b, url ) {
|
3157
3500
|
|
3158
|
-
|
3501
|
+
if( url === undefined ) {
|
3502
|
+
url = '#' + locationHash();
|
3503
|
+
}
|
3159
3504
|
if( typeof b === 'number' && !isNaN( b ) ) {
|
3160
3505
|
return '<a href="' + url + '">' +
|
3161
3506
|
'<span class="slide-number-a">'+ a +'</span>' +
|
@@ -3308,7 +3653,7 @@
|
|
3308
3653
|
// Stop content inside of previous backgrounds
|
3309
3654
|
if( previousBackground ) {
|
3310
3655
|
|
3311
|
-
stopEmbeddedContent( previousBackground );
|
3656
|
+
stopEmbeddedContent( previousBackground, { unloadIframes: !shouldPreload( previousBackground ) } );
|
3312
3657
|
|
3313
3658
|
}
|
3314
3659
|
|
@@ -3419,6 +3764,26 @@
|
|
3419
3764
|
|
3420
3765
|
}
|
3421
3766
|
|
3767
|
+
/**
|
3768
|
+
* Should the given element be preloaded?
|
3769
|
+
* Decides based on local element attributes and global config.
|
3770
|
+
*
|
3771
|
+
* @param {HTMLElement} element
|
3772
|
+
*/
|
3773
|
+
function shouldPreload( element ) {
|
3774
|
+
|
3775
|
+
// Prefer an explicit global preload setting
|
3776
|
+
var preload = config.preloadIframes;
|
3777
|
+
|
3778
|
+
// If no global setting is available, fall back on the element's
|
3779
|
+
// own preload setting
|
3780
|
+
if( typeof preload !== 'boolean' ) {
|
3781
|
+
preload = element.hasAttribute( 'data-preload' );
|
3782
|
+
}
|
3783
|
+
|
3784
|
+
return preload;
|
3785
|
+
}
|
3786
|
+
|
3422
3787
|
/**
|
3423
3788
|
* Called when the given slide is within the configured view
|
3424
3789
|
* distance. Shows the slide element and loads any content
|
@@ -3434,10 +3799,12 @@
|
|
3434
3799
|
slide.style.display = config.display;
|
3435
3800
|
|
3436
3801
|
// Media elements with data-src attributes
|
3437
|
-
toArray( slide.querySelectorAll( 'img[data-src], video[data-src], audio[data-src]' ) ).forEach( function( element ) {
|
3438
|
-
element.
|
3439
|
-
|
3440
|
-
|
3802
|
+
toArray( slide.querySelectorAll( 'img[data-src], video[data-src], audio[data-src], iframe[data-src]' ) ).forEach( function( element ) {
|
3803
|
+
if( element.tagName !== 'IFRAME' || shouldPreload( element ) ) {
|
3804
|
+
element.setAttribute( 'src', element.getAttribute( 'data-src' ) );
|
3805
|
+
element.setAttribute( 'data-lazy-loaded', '' );
|
3806
|
+
element.removeAttribute( 'data-src' );
|
3807
|
+
}
|
3441
3808
|
} );
|
3442
3809
|
|
3443
3810
|
// Media elements with <source> children
|
@@ -3465,6 +3832,7 @@
|
|
3465
3832
|
background.style.display = 'block';
|
3466
3833
|
|
3467
3834
|
var backgroundContent = slide.slideBackgroundContentElement;
|
3835
|
+
var backgroundIframe = slide.getAttribute( 'data-background-iframe' );
|
3468
3836
|
|
3469
3837
|
// If the background contains media, load it
|
3470
3838
|
if( background.hasAttribute( 'data-loaded' ) === false ) {
|
@@ -3473,8 +3841,7 @@
|
|
3473
3841
|
var backgroundImage = slide.getAttribute( 'data-background-image' ),
|
3474
3842
|
backgroundVideo = slide.getAttribute( 'data-background-video' ),
|
3475
3843
|
backgroundVideoLoop = slide.hasAttribute( 'data-background-video-loop' ),
|
3476
|
-
backgroundVideoMuted = slide.hasAttribute( 'data-background-video-muted' )
|
3477
|
-
backgroundIframe = slide.getAttribute( 'data-background-iframe' );
|
3844
|
+
backgroundVideoMuted = slide.hasAttribute( 'data-background-video-muted' );
|
3478
3845
|
|
3479
3846
|
// Images
|
3480
3847
|
if( backgroundImage ) {
|
@@ -3514,15 +3881,9 @@
|
|
3514
3881
|
iframe.setAttribute( 'allowfullscreen', '' );
|
3515
3882
|
iframe.setAttribute( 'mozallowfullscreen', '' );
|
3516
3883
|
iframe.setAttribute( 'webkitallowfullscreen', '' );
|
3884
|
+
iframe.setAttribute( 'allow', 'autoplay' );
|
3517
3885
|
|
3518
|
-
|
3519
|
-
// avoid having it play in the background
|
3520
|
-
if( /autoplay=(1|true|yes)/gi.test( backgroundIframe ) ) {
|
3521
|
-
iframe.setAttribute( 'data-src', backgroundIframe );
|
3522
|
-
}
|
3523
|
-
else {
|
3524
|
-
iframe.setAttribute( 'src', backgroundIframe );
|
3525
|
-
}
|
3886
|
+
iframe.setAttribute( 'data-src', backgroundIframe );
|
3526
3887
|
|
3527
3888
|
iframe.style.width = '100%';
|
3528
3889
|
iframe.style.height = '100%';
|
@@ -3533,6 +3894,19 @@
|
|
3533
3894
|
}
|
3534
3895
|
}
|
3535
3896
|
|
3897
|
+
// Start loading preloadable iframes
|
3898
|
+
var backgroundIframeElement = backgroundContent.querySelector( 'iframe[data-src]' );
|
3899
|
+
if( backgroundIframeElement ) {
|
3900
|
+
|
3901
|
+
// Check if this iframe is eligible to be preloaded
|
3902
|
+
if( shouldPreload( background ) && !/autoplay=(1|true|yes)/gi.test( backgroundIframe ) ) {
|
3903
|
+
if( backgroundIframeElement.getAttribute( 'src' ) !== backgroundIframe ) {
|
3904
|
+
backgroundIframeElement.setAttribute( 'src', backgroundIframe );
|
3905
|
+
}
|
3906
|
+
}
|
3907
|
+
|
3908
|
+
}
|
3909
|
+
|
3536
3910
|
}
|
3537
3911
|
|
3538
3912
|
}
|
@@ -3552,10 +3926,15 @@
|
|
3552
3926
|
var background = getSlideBackground( slide );
|
3553
3927
|
if( background ) {
|
3554
3928
|
background.style.display = 'none';
|
3929
|
+
|
3930
|
+
// Unload any background iframes
|
3931
|
+
toArray( background.querySelectorAll( 'iframe[src]' ) ).forEach( function( element ) {
|
3932
|
+
element.removeAttribute( 'src' );
|
3933
|
+
} );
|
3555
3934
|
}
|
3556
3935
|
|
3557
3936
|
// Reset lazy-loaded media elements with src attributes
|
3558
|
-
toArray( slide.querySelectorAll( 'video[data-lazy-loaded][src], audio[data-lazy-loaded][src]' ) ).forEach( function( element ) {
|
3937
|
+
toArray( slide.querySelectorAll( 'video[data-lazy-loaded][src], audio[data-lazy-loaded][src], iframe[data-lazy-loaded][src]' ) ).forEach( function( element ) {
|
3559
3938
|
element.setAttribute( 'data-src', element.getAttribute( 'src' ) );
|
3560
3939
|
element.removeAttribute( 'src' );
|
3561
3940
|
} );
|
@@ -3655,13 +4034,6 @@
|
|
3655
4034
|
_appendParamToIframeSource( 'src', 'player.vimeo.com/', 'api=1' );
|
3656
4035
|
_appendParamToIframeSource( 'data-src', 'player.vimeo.com/', 'api=1' );
|
3657
4036
|
|
3658
|
-
// Always show media controls on mobile devices
|
3659
|
-
if( isMobileDevice ) {
|
3660
|
-
toArray( dom.slides.querySelectorAll( 'video, audio' ) ).forEach( function( el ) {
|
3661
|
-
el.controls = true;
|
3662
|
-
} );
|
3663
|
-
}
|
3664
|
-
|
3665
4037
|
}
|
3666
4038
|
|
3667
4039
|
/**
|
@@ -3705,7 +4077,20 @@
|
|
3705
4077
|
// Mobile devices never fire a loaded event so instead
|
3706
4078
|
// of waiting, we initiate playback
|
3707
4079
|
else if( isMobileDevice ) {
|
3708
|
-
el.play();
|
4080
|
+
var promise = el.play();
|
4081
|
+
|
4082
|
+
// If autoplay does not work, ensure that the controls are visible so
|
4083
|
+
// that the viewer can start the media on their own
|
4084
|
+
if( promise && typeof promise.catch === 'function' && el.controls === false ) {
|
4085
|
+
promise.catch( function() {
|
4086
|
+
el.controls = true;
|
4087
|
+
|
4088
|
+
// Once the video does start playing, hide the controls again
|
4089
|
+
el.addEventListener( 'play', function() {
|
4090
|
+
el.controls = false;
|
4091
|
+
} );
|
4092
|
+
} );
|
4093
|
+
}
|
3709
4094
|
}
|
3710
4095
|
// If the media isn't loaded, wait before playing
|
3711
4096
|
else {
|
@@ -3866,9 +4251,15 @@
|
|
3866
4251
|
* Returns the number of past slides. This can be used as a global
|
3867
4252
|
* flattened index for slides.
|
3868
4253
|
*
|
4254
|
+
* @param {HTMLElement} [slide=currentSlide] The slide we're counting before
|
4255
|
+
*
|
3869
4256
|
* @return {number} Past slide count
|
3870
4257
|
*/
|
3871
|
-
function getSlidePastCount() {
|
4258
|
+
function getSlidePastCount( slide ) {
|
4259
|
+
|
4260
|
+
if( slide === undefined ) {
|
4261
|
+
slide = currentSlide;
|
4262
|
+
}
|
3872
4263
|
|
3873
4264
|
var horizontalSlides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) );
|
3874
4265
|
|
@@ -3884,7 +4275,7 @@
|
|
3884
4275
|
for( var j = 0; j < verticalSlides.length; j++ ) {
|
3885
4276
|
|
3886
4277
|
// Stop as soon as we arrive at the present
|
3887
|
-
if( verticalSlides[j]
|
4278
|
+
if( verticalSlides[j] === slide ) {
|
3888
4279
|
break mainLoop;
|
3889
4280
|
}
|
3890
4281
|
|
@@ -3893,7 +4284,7 @@
|
|
3893
4284
|
}
|
3894
4285
|
|
3895
4286
|
// Stop as soon as we arrive at the present
|
3896
|
-
if( horizontalSlide
|
4287
|
+
if( horizontalSlide === slide ) {
|
3897
4288
|
break;
|
3898
4289
|
}
|
3899
4290
|
|
@@ -3939,7 +4330,7 @@
|
|
3939
4330
|
|
3940
4331
|
}
|
3941
4332
|
|
3942
|
-
return pastCount / ( totalCount - 1 );
|
4333
|
+
return Math.min( pastCount / ( totalCount - 1 ), 1 );
|
3943
4334
|
|
3944
4335
|
}
|
3945
4336
|
|
@@ -3966,9 +4357,9 @@
|
|
3966
4357
|
var bits = hash.slice( 2 ).split( '/' ),
|
3967
4358
|
name = hash.replace( /#|\//gi, '' );
|
3968
4359
|
|
3969
|
-
// If the first bit is
|
3970
|
-
// assume that this is a named link
|
3971
|
-
if(
|
4360
|
+
// If the first bit is not fully numeric and there is a name we
|
4361
|
+
// can assume that this is a named link
|
4362
|
+
if( !/^[0-9]*$/.test( bits[0] ) && name.length ) {
|
3972
4363
|
var element;
|
3973
4364
|
|
3974
4365
|
// Ensure the named link is a valid HTML ID attribute
|
@@ -3980,10 +4371,13 @@
|
|
3980
4371
|
// Ensure that we're not already on a slide with the same name
|
3981
4372
|
var isSameNameAsCurrentSlide = currentSlide ? currentSlide.getAttribute( 'id' ) === name : false;
|
3982
4373
|
|
3983
|
-
if( element
|
3984
|
-
//
|
3985
|
-
|
3986
|
-
|
4374
|
+
if( element ) {
|
4375
|
+
// If the slide exists and is not the current slide...
|
4376
|
+
if ( !isSameNameAsCurrentSlide ) {
|
4377
|
+
// ...find the position of the named slide and navigate to it
|
4378
|
+
var indices = Reveal.getIndices(element);
|
4379
|
+
slide(indices.h, indices.v);
|
4380
|
+
}
|
3987
4381
|
}
|
3988
4382
|
// If the slide doesn't exist, navigate to the current slide
|
3989
4383
|
else {
|
@@ -4021,18 +4415,30 @@
|
|
4021
4415
|
*/
|
4022
4416
|
function writeURL( delay ) {
|
4023
4417
|
|
4024
|
-
|
4025
|
-
|
4026
|
-
// Make sure there's never more than one timeout running
|
4027
|
-
clearTimeout( writeURLTimeout );
|
4418
|
+
// Make sure there's never more than one timeout running
|
4419
|
+
clearTimeout( writeURLTimeout );
|
4028
4420
|
|
4029
|
-
|
4030
|
-
|
4031
|
-
|
4032
|
-
|
4033
|
-
|
4421
|
+
// If a delay is specified, timeout this call
|
4422
|
+
if( typeof delay === 'number' ) {
|
4423
|
+
writeURLTimeout = setTimeout( writeURL, delay );
|
4424
|
+
}
|
4425
|
+
else if( currentSlide ) {
|
4426
|
+
// If we're configured to push to history OR the history
|
4427
|
+
// API is not avaialble.
|
4428
|
+
if( config.history || !window.history ) {
|
4034
4429
|
window.location.hash = locationHash();
|
4035
4430
|
}
|
4431
|
+
// If we're configured to reflect the current slide in the
|
4432
|
+
// URL without pushing to history.
|
4433
|
+
else if( config.hash ) {
|
4434
|
+
window.history.replaceState( null, null, '#' + locationHash() );
|
4435
|
+
}
|
4436
|
+
// If history and hash are both disabled, a hash may still
|
4437
|
+
// be added to the URL by clicking on a href with a hash
|
4438
|
+
// target. Counter this by always removing the hash.
|
4439
|
+
else {
|
4440
|
+
window.history.replaceState( null, null, window.location.pathname + window.location.search );
|
4441
|
+
}
|
4036
4442
|
}
|
4037
4443
|
|
4038
4444
|
}
|
@@ -4095,7 +4501,63 @@
|
|
4095
4501
|
*/
|
4096
4502
|
function getSlides() {
|
4097
4503
|
|
4098
|
-
return toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR + ':not(.stack)' ));
|
4504
|
+
return toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR + ':not(.stack)' ) );
|
4505
|
+
|
4506
|
+
}
|
4507
|
+
|
4508
|
+
/**
|
4509
|
+
* Returns a list of all horizontal slides in the deck. Each
|
4510
|
+
* vertical stack is included as one horizontal slide in the
|
4511
|
+
* resulting array.
|
4512
|
+
*/
|
4513
|
+
function getHorizontalSlides() {
|
4514
|
+
|
4515
|
+
return toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) );
|
4516
|
+
|
4517
|
+
}
|
4518
|
+
|
4519
|
+
/**
|
4520
|
+
* Returns all vertical slides that exist within this deck.
|
4521
|
+
*/
|
4522
|
+
function getVerticalSlides() {
|
4523
|
+
|
4524
|
+
return toArray( dom.wrapper.querySelectorAll( '.slides>section>section' ) );
|
4525
|
+
|
4526
|
+
}
|
4527
|
+
|
4528
|
+
/**
|
4529
|
+
* Returns true if there are at least two horizontal slides.
|
4530
|
+
*/
|
4531
|
+
function hasHorizontalSlides() {
|
4532
|
+
|
4533
|
+
return getHorizontalSlides().length > 1;
|
4534
|
+
}
|
4535
|
+
|
4536
|
+
/**
|
4537
|
+
* Returns true if there are at least two vertical slides.
|
4538
|
+
*/
|
4539
|
+
function hasVerticalSlides() {
|
4540
|
+
|
4541
|
+
return getVerticalSlides().length > 1;
|
4542
|
+
|
4543
|
+
}
|
4544
|
+
|
4545
|
+
/**
|
4546
|
+
* Returns an array of objects where each object represents the
|
4547
|
+
* attributes on its respective slide.
|
4548
|
+
*/
|
4549
|
+
function getSlidesAttributes() {
|
4550
|
+
|
4551
|
+
return getSlides().map( function( slide ) {
|
4552
|
+
|
4553
|
+
var attributes = {};
|
4554
|
+
for( var i = 0; i < slide.attributes.length; i++ ) {
|
4555
|
+
var attribute = slide.attributes[ i ];
|
4556
|
+
attributes[ attribute.name ] = attribute.value;
|
4557
|
+
}
|
4558
|
+
return attributes;
|
4559
|
+
|
4560
|
+
} );
|
4099
4561
|
|
4100
4562
|
}
|
4101
4563
|
|
@@ -4291,6 +4753,84 @@
|
|
4291
4753
|
|
4292
4754
|
}
|
4293
4755
|
|
4756
|
+
/**
|
4757
|
+
* Refreshes the fragments on the current slide so that they
|
4758
|
+
* have the appropriate classes (.visible + .current-fragment).
|
4759
|
+
*
|
4760
|
+
* @param {number} [index] The index of the current fragment
|
4761
|
+
* @param {array} [fragments] Array containing all fragments
|
4762
|
+
* in the current slide
|
4763
|
+
*
|
4764
|
+
* @return {{shown: array, hidden: array}}
|
4765
|
+
*/
|
4766
|
+
function updateFragments( index, fragments ) {
|
4767
|
+
|
4768
|
+
var changedFragments = {
|
4769
|
+
shown: [],
|
4770
|
+
hidden: []
|
4771
|
+
};
|
4772
|
+
|
4773
|
+
if( currentSlide && config.fragments ) {
|
4774
|
+
|
4775
|
+
fragments = fragments || sortFragments( currentSlide.querySelectorAll( '.fragment' ) );
|
4776
|
+
|
4777
|
+
if( fragments.length ) {
|
4778
|
+
|
4779
|
+
var maxIndex = 0;
|
4780
|
+
|
4781
|
+
if( typeof index !== 'number' ) {
|
4782
|
+
var currentFragment = sortFragments( currentSlide.querySelectorAll( '.fragment.visible' ) ).pop();
|
4783
|
+
if( currentFragment ) {
|
4784
|
+
index = parseInt( currentFragment.getAttribute( 'data-fragment-index' ) || 0, 10 );
|
4785
|
+
}
|
4786
|
+
}
|
4787
|
+
|
4788
|
+
toArray( fragments ).forEach( function( el, i ) {
|
4789
|
+
|
4790
|
+
if( el.hasAttribute( 'data-fragment-index' ) ) {
|
4791
|
+
i = parseInt( el.getAttribute( 'data-fragment-index' ), 10 );
|
4792
|
+
}
|
4793
|
+
|
4794
|
+
maxIndex = Math.max( maxIndex, i );
|
4795
|
+
|
4796
|
+
// Visible fragments
|
4797
|
+
if( i <= index ) {
|
4798
|
+
if( !el.classList.contains( 'visible' ) ) changedFragments.shown.push( el );
|
4799
|
+
el.classList.add( 'visible' );
|
4800
|
+
el.classList.remove( 'current-fragment' );
|
4801
|
+
|
4802
|
+
// Announce the fragments one by one to the Screen Reader
|
4803
|
+
dom.statusDiv.textContent = getStatusText( el );
|
4804
|
+
|
4805
|
+
if( i === index ) {
|
4806
|
+
el.classList.add( 'current-fragment' );
|
4807
|
+
startEmbeddedContent( el );
|
4808
|
+
}
|
4809
|
+
}
|
4810
|
+
// Hidden fragments
|
4811
|
+
else {
|
4812
|
+
if( el.classList.contains( 'visible' ) ) changedFragments.hidden.push( el );
|
4813
|
+
el.classList.remove( 'visible' );
|
4814
|
+
el.classList.remove( 'current-fragment' );
|
4815
|
+
}
|
4816
|
+
|
4817
|
+
} );
|
4818
|
+
|
4819
|
+
// Write the current fragment index to the slide <section>.
|
4820
|
+
// This can be used by end users to apply styles based on
|
4821
|
+
// the current fragment index.
|
4822
|
+
index = typeof index === 'number' ? index : -1;
|
4823
|
+
index = Math.max( Math.min( index, maxIndex ), -1 );
|
4824
|
+
currentSlide.setAttribute( 'data-fragment', index );
|
4825
|
+
|
4826
|
+
}
|
4827
|
+
|
4828
|
+
}
|
4829
|
+
|
4830
|
+
return changedFragments;
|
4831
|
+
|
4832
|
+
}
|
4833
|
+
|
4294
4834
|
/**
|
4295
4835
|
* Navigate to the specified slide fragment.
|
4296
4836
|
*
|
@@ -4326,53 +4866,24 @@
|
|
4326
4866
|
index += offset;
|
4327
4867
|
}
|
4328
4868
|
|
4329
|
-
var
|
4330
|
-
fragmentsHidden = [];
|
4331
|
-
|
4332
|
-
toArray( fragments ).forEach( function( element, i ) {
|
4333
|
-
|
4334
|
-
if( element.hasAttribute( 'data-fragment-index' ) ) {
|
4335
|
-
i = parseInt( element.getAttribute( 'data-fragment-index' ), 10 );
|
4336
|
-
}
|
4337
|
-
|
4338
|
-
// Visible fragments
|
4339
|
-
if( i <= index ) {
|
4340
|
-
if( !element.classList.contains( 'visible' ) ) fragmentsShown.push( element );
|
4341
|
-
element.classList.add( 'visible' );
|
4342
|
-
element.classList.remove( 'current-fragment' );
|
4343
|
-
|
4344
|
-
// Announce the fragments one by one to the Screen Reader
|
4345
|
-
dom.statusDiv.textContent = getStatusText( element );
|
4869
|
+
var changedFragments = updateFragments( index, fragments );
|
4346
4870
|
|
4347
|
-
|
4348
|
-
|
4349
|
-
startEmbeddedContent( element );
|
4350
|
-
}
|
4351
|
-
}
|
4352
|
-
// Hidden fragments
|
4353
|
-
else {
|
4354
|
-
if( element.classList.contains( 'visible' ) ) fragmentsHidden.push( element );
|
4355
|
-
element.classList.remove( 'visible' );
|
4356
|
-
element.classList.remove( 'current-fragment' );
|
4357
|
-
}
|
4358
|
-
|
4359
|
-
} );
|
4360
|
-
|
4361
|
-
if( fragmentsHidden.length ) {
|
4362
|
-
dispatchEvent( 'fragmenthidden', { fragment: fragmentsHidden[0], fragments: fragmentsHidden } );
|
4871
|
+
if( changedFragments.hidden.length ) {
|
4872
|
+
dispatchEvent( 'fragmenthidden', { fragment: changedFragments.hidden[0], fragments: changedFragments.hidden } );
|
4363
4873
|
}
|
4364
4874
|
|
4365
|
-
if(
|
4366
|
-
dispatchEvent( 'fragmentshown', { fragment:
|
4875
|
+
if( changedFragments.shown.length ) {
|
4876
|
+
dispatchEvent( 'fragmentshown', { fragment: changedFragments.shown[0], fragments: changedFragments.shown } );
|
4367
4877
|
}
|
4368
4878
|
|
4369
4879
|
updateControls();
|
4370
4880
|
updateProgress();
|
4881
|
+
|
4371
4882
|
if( config.fragmentInURL ) {
|
4372
4883
|
writeURL();
|
4373
4884
|
}
|
4374
4885
|
|
4375
|
-
return !!(
|
4886
|
+
return !!( changedFragments.shown.length || changedFragments.hidden.length );
|
4376
4887
|
|
4377
4888
|
}
|
4378
4889
|
|
@@ -4519,12 +5030,12 @@
|
|
4519
5030
|
// Reverse for RTL
|
4520
5031
|
if( config.rtl ) {
|
4521
5032
|
if( ( isOverview() || nextFragment() === false ) && availableRoutes().left ) {
|
4522
|
-
slide( indexh + 1 );
|
5033
|
+
slide( indexh + 1, config.navigationMode === 'grid' ? indexv : undefined );
|
4523
5034
|
}
|
4524
5035
|
}
|
4525
5036
|
// Normal navigation
|
4526
5037
|
else if( ( isOverview() || previousFragment() === false ) && availableRoutes().left ) {
|
4527
|
-
slide( indexh - 1 );
|
5038
|
+
slide( indexh - 1, config.navigationMode === 'grid' ? indexv : undefined );
|
4528
5039
|
}
|
4529
5040
|
|
4530
5041
|
}
|
@@ -4536,12 +5047,12 @@
|
|
4536
5047
|
// Reverse for RTL
|
4537
5048
|
if( config.rtl ) {
|
4538
5049
|
if( ( isOverview() || previousFragment() === false ) && availableRoutes().right ) {
|
4539
|
-
slide( indexh - 1 );
|
5050
|
+
slide( indexh - 1, config.navigationMode === 'grid' ? indexv : undefined );
|
4540
5051
|
}
|
4541
5052
|
}
|
4542
5053
|
// Normal navigation
|
4543
5054
|
else if( ( isOverview() || nextFragment() === false ) && availableRoutes().right ) {
|
4544
|
-
slide( indexh + 1 );
|
5055
|
+
slide( indexh + 1, config.navigationMode === 'grid' ? indexv : undefined );
|
4545
5056
|
}
|
4546
5057
|
|
4547
5058
|
}
|
@@ -4667,6 +5178,22 @@
|
|
4667
5178
|
|
4668
5179
|
}
|
4669
5180
|
|
5181
|
+
/**
|
5182
|
+
* Called whenever there is mouse input at the document level
|
5183
|
+
* to determine if the cursor is active or not.
|
5184
|
+
*
|
5185
|
+
* @param {object} event
|
5186
|
+
*/
|
5187
|
+
function onDocumentCursorActive( event ) {
|
5188
|
+
|
5189
|
+
showCursor();
|
5190
|
+
|
5191
|
+
clearTimeout( cursorInactiveTimeout );
|
5192
|
+
|
5193
|
+
cursorInactiveTimeout = setTimeout( hideCursor, config.hideCursorTime );
|
5194
|
+
|
5195
|
+
}
|
5196
|
+
|
4670
5197
|
/**
|
4671
5198
|
* Handler for the document level 'keypress' event.
|
4672
5199
|
*
|
@@ -4694,20 +5221,31 @@
|
|
4694
5221
|
return true;
|
4695
5222
|
}
|
4696
5223
|
|
5224
|
+
// Shorthand
|
5225
|
+
var keyCode = event.keyCode;
|
5226
|
+
|
4697
5227
|
// Remember if auto-sliding was paused so we can toggle it
|
4698
5228
|
var autoSlideWasPaused = autoSlidePaused;
|
4699
5229
|
|
4700
5230
|
onUserInput( event );
|
4701
5231
|
|
4702
|
-
//
|
4703
|
-
// the keyboard
|
5232
|
+
// Is there a focused element that could be using the keyboard?
|
4704
5233
|
var activeElementIsCE = document.activeElement && document.activeElement.contentEditable !== 'inherit';
|
4705
5234
|
var activeElementIsInput = document.activeElement && document.activeElement.tagName && /input|textarea/i.test( document.activeElement.tagName );
|
4706
5235
|
var activeElementIsNotes = document.activeElement && document.activeElement.className && /speaker-notes/i.test( document.activeElement.className);
|
4707
5236
|
|
5237
|
+
// Whitelist specific modified + keycode combinations
|
5238
|
+
var prevSlideShortcut = event.shiftKey && event.keyCode === 32;
|
5239
|
+
var firstSlideShortcut = event.shiftKey && keyCode === 37;
|
5240
|
+
var lastSlideShortcut = event.shiftKey && keyCode === 39;
|
5241
|
+
|
5242
|
+
// Prevent all other events when a modifier is pressed
|
5243
|
+
var unusedModifier = !prevSlideShortcut && !firstSlideShortcut && !lastSlideShortcut &&
|
5244
|
+
( event.shiftKey || event.altKey || event.ctrlKey || event.metaKey );
|
5245
|
+
|
4708
5246
|
// Disregard the event if there's a focused element or a
|
4709
5247
|
// keyboard modifier key is present
|
4710
|
-
if( activeElementIsCE || activeElementIsInput || activeElementIsNotes ||
|
5248
|
+
if( activeElementIsCE || activeElementIsInput || activeElementIsNotes || unusedModifier ) return;
|
4711
5249
|
|
4712
5250
|
// While paused only allow resume keyboard events; 'b', 'v', '.'
|
4713
5251
|
var resumeKeyCodes = [66,86,190,191];
|
@@ -4722,10 +5260,14 @@
|
|
4722
5260
|
}
|
4723
5261
|
}
|
4724
5262
|
|
4725
|
-
if( isPaused() && resumeKeyCodes.indexOf(
|
5263
|
+
if( isPaused() && resumeKeyCodes.indexOf( keyCode ) === -1 ) {
|
4726
5264
|
return false;
|
4727
5265
|
}
|
4728
5266
|
|
5267
|
+
// Use linear navigation if we're configured to OR if
|
5268
|
+
// the presentation is one-dimensional
|
5269
|
+
var useLinearMode = config.navigationMode === 'linear' || !hasHorizontalSlides() || !hasVerticalSlides();
|
5270
|
+
|
4729
5271
|
var triggered = false;
|
4730
5272
|
|
4731
5273
|
// 1. User defined key bindings
|
@@ -4734,7 +5276,7 @@
|
|
4734
5276
|
for( key in config.keyboard ) {
|
4735
5277
|
|
4736
5278
|
// Check if this binding matches the pressed key
|
4737
|
-
if( parseInt( key, 10 ) ===
|
5279
|
+
if( parseInt( key, 10 ) === keyCode ) {
|
4738
5280
|
|
4739
5281
|
var value = config.keyboard[ key ];
|
4740
5282
|
|
@@ -4761,7 +5303,7 @@
|
|
4761
5303
|
for( key in registeredKeyBindings ) {
|
4762
5304
|
|
4763
5305
|
// Check if this binding matches the pressed key
|
4764
|
-
if( parseInt( key, 10 ) ===
|
5306
|
+
if( parseInt( key, 10 ) === keyCode ) {
|
4765
5307
|
|
4766
5308
|
var action = registeredKeyBindings[ key ].callback;
|
4767
5309
|
|
@@ -4785,35 +5327,92 @@
|
|
4785
5327
|
// Assume true and try to prove false
|
4786
5328
|
triggered = true;
|
4787
5329
|
|
4788
|
-
|
4789
|
-
|
4790
|
-
|
4791
|
-
|
4792
|
-
|
4793
|
-
|
4794
|
-
|
4795
|
-
|
4796
|
-
|
4797
|
-
|
4798
|
-
|
4799
|
-
|
4800
|
-
|
4801
|
-
|
4802
|
-
|
4803
|
-
|
4804
|
-
|
4805
|
-
|
4806
|
-
|
4807
|
-
|
4808
|
-
|
4809
|
-
|
4810
|
-
|
4811
|
-
|
4812
|
-
|
4813
|
-
|
4814
|
-
|
4815
|
-
|
4816
|
-
|
5330
|
+
// P, PAGE UP
|
5331
|
+
if( keyCode === 80 || keyCode === 33 ) {
|
5332
|
+
navigatePrev();
|
5333
|
+
}
|
5334
|
+
// N, PAGE DOWN
|
5335
|
+
else if( keyCode === 78 || keyCode === 34 ) {
|
5336
|
+
navigateNext();
|
5337
|
+
}
|
5338
|
+
// H, LEFT
|
5339
|
+
else if( keyCode === 72 || keyCode === 37 ) {
|
5340
|
+
if( firstSlideShortcut ) {
|
5341
|
+
slide( 0 );
|
5342
|
+
}
|
5343
|
+
else if( !isOverview() && useLinearMode ) {
|
5344
|
+
navigatePrev();
|
5345
|
+
}
|
5346
|
+
else {
|
5347
|
+
navigateLeft();
|
5348
|
+
}
|
5349
|
+
}
|
5350
|
+
// L, RIGHT
|
5351
|
+
else if( keyCode === 76 || keyCode === 39 ) {
|
5352
|
+
if( lastSlideShortcut ) {
|
5353
|
+
slide( Number.MAX_VALUE );
|
5354
|
+
}
|
5355
|
+
else if( !isOverview() && useLinearMode ) {
|
5356
|
+
navigateNext();
|
5357
|
+
}
|
5358
|
+
else {
|
5359
|
+
navigateRight();
|
5360
|
+
}
|
5361
|
+
}
|
5362
|
+
// K, UP
|
5363
|
+
else if( keyCode === 75 || keyCode === 38 ) {
|
5364
|
+
if( !isOverview() && useLinearMode ) {
|
5365
|
+
navigatePrev();
|
5366
|
+
}
|
5367
|
+
else {
|
5368
|
+
navigateUp();
|
5369
|
+
}
|
5370
|
+
}
|
5371
|
+
// J, DOWN
|
5372
|
+
else if( keyCode === 74 || keyCode === 40 ) {
|
5373
|
+
if( !isOverview() && useLinearMode ) {
|
5374
|
+
navigateNext();
|
5375
|
+
}
|
5376
|
+
else {
|
5377
|
+
navigateDown();
|
5378
|
+
}
|
5379
|
+
}
|
5380
|
+
// HOME
|
5381
|
+
else if( keyCode === 36 ) {
|
5382
|
+
slide( 0 );
|
5383
|
+
}
|
5384
|
+
// END
|
5385
|
+
else if( keyCode === 35 ) {
|
5386
|
+
slide( Number.MAX_VALUE );
|
5387
|
+
}
|
5388
|
+
// SPACE
|
5389
|
+
else if( keyCode === 32 ) {
|
5390
|
+
if( isOverview() ) {
|
5391
|
+
deactivateOverview();
|
5392
|
+
}
|
5393
|
+
if( event.shiftKey ) {
|
5394
|
+
navigatePrev();
|
5395
|
+
}
|
5396
|
+
else {
|
5397
|
+
navigateNext();
|
5398
|
+
}
|
5399
|
+
}
|
5400
|
+
// TWO-SPOT, SEMICOLON, B, V, PERIOD, LOGITECH PRESENTER TOOLS "BLACK SCREEN" BUTTON
|
5401
|
+
else if( keyCode === 58 || keyCode === 59 || keyCode === 66 || keyCode === 86 || keyCode === 190 || keyCode === 191 ) {
|
5402
|
+
togglePause();
|
5403
|
+
}
|
5404
|
+
// F
|
5405
|
+
else if( keyCode === 70 ) {
|
5406
|
+
enterFullscreen();
|
5407
|
+
}
|
5408
|
+
// A
|
5409
|
+
else if( keyCode === 65 ) {
|
5410
|
+
if ( config.autoSlideStoppable ) {
|
5411
|
+
toggleAutoSlide( autoSlideWasPaused );
|
5412
|
+
}
|
5413
|
+
}
|
5414
|
+
else {
|
5415
|
+
triggered = false;
|
4817
5416
|
}
|
4818
5417
|
|
4819
5418
|
}
|
@@ -4824,7 +5423,7 @@
|
|
4824
5423
|
event.preventDefault && event.preventDefault();
|
4825
5424
|
}
|
4826
5425
|
// ESC or O key
|
4827
|
-
else if ( (
|
5426
|
+
else if ( ( keyCode === 27 || keyCode === 79 ) && features.transforms3d ) {
|
4828
5427
|
if( dom.overlay ) {
|
4829
5428
|
closeOverlay();
|
4830
5429
|
}
|
@@ -4855,18 +5454,6 @@
|
|
4855
5454
|
touch.startY = event.touches[0].clientY;
|
4856
5455
|
touch.startCount = event.touches.length;
|
4857
5456
|
|
4858
|
-
// If there's two touches we need to memorize the distance
|
4859
|
-
// between those two points to detect pinching
|
4860
|
-
if( event.touches.length === 2 && config.overview ) {
|
4861
|
-
touch.startSpan = distanceBetween( {
|
4862
|
-
x: event.touches[1].clientX,
|
4863
|
-
y: event.touches[1].clientY
|
4864
|
-
}, {
|
4865
|
-
x: touch.startX,
|
4866
|
-
y: touch.startY
|
4867
|
-
} );
|
4868
|
-
}
|
4869
|
-
|
4870
5457
|
}
|
4871
5458
|
|
4872
5459
|
/**
|
@@ -4885,56 +5472,57 @@
|
|
4885
5472
|
var currentX = event.touches[0].clientX;
|
4886
5473
|
var currentY = event.touches[0].clientY;
|
4887
5474
|
|
4888
|
-
// If the touch started with two points and still has
|
4889
|
-
// two active touches; test for the pinch gesture
|
4890
|
-
if( event.touches.length === 2 && touch.startCount === 2 && config.overview ) {
|
4891
|
-
|
4892
|
-
// The current distance in pixels between the two touch points
|
4893
|
-
var currentSpan = distanceBetween( {
|
4894
|
-
x: event.touches[1].clientX,
|
4895
|
-
y: event.touches[1].clientY
|
4896
|
-
}, {
|
4897
|
-
x: touch.startX,
|
4898
|
-
y: touch.startY
|
4899
|
-
} );
|
4900
|
-
|
4901
|
-
// If the span is larger than the desire amount we've got
|
4902
|
-
// ourselves a pinch
|
4903
|
-
if( Math.abs( touch.startSpan - currentSpan ) > touch.threshold ) {
|
4904
|
-
touch.captured = true;
|
4905
|
-
|
4906
|
-
if( currentSpan < touch.startSpan ) {
|
4907
|
-
activateOverview();
|
4908
|
-
}
|
4909
|
-
else {
|
4910
|
-
deactivateOverview();
|
4911
|
-
}
|
4912
|
-
}
|
4913
|
-
|
4914
|
-
event.preventDefault();
|
4915
|
-
|
4916
|
-
}
|
4917
5475
|
// There was only one touch point, look for a swipe
|
4918
|
-
|
5476
|
+
if( event.touches.length === 1 && touch.startCount !== 2 ) {
|
4919
5477
|
|
4920
5478
|
var deltaX = currentX - touch.startX,
|
4921
5479
|
deltaY = currentY - touch.startY;
|
4922
5480
|
|
4923
5481
|
if( deltaX > touch.threshold && Math.abs( deltaX ) > Math.abs( deltaY ) ) {
|
4924
5482
|
touch.captured = true;
|
4925
|
-
|
5483
|
+
if( config.navigationMode === 'linear' ) {
|
5484
|
+
if( config.rtl ) {
|
5485
|
+
navigateNext();
|
5486
|
+
}
|
5487
|
+
else {
|
5488
|
+
navigatePrev();
|
5489
|
+
}
|
5490
|
+
}
|
5491
|
+
else {
|
5492
|
+
navigateLeft();
|
5493
|
+
}
|
4926
5494
|
}
|
4927
5495
|
else if( deltaX < -touch.threshold && Math.abs( deltaX ) > Math.abs( deltaY ) ) {
|
4928
5496
|
touch.captured = true;
|
4929
|
-
|
5497
|
+
if( config.navigationMode === 'linear' ) {
|
5498
|
+
if( config.rtl ) {
|
5499
|
+
navigatePrev();
|
5500
|
+
}
|
5501
|
+
else {
|
5502
|
+
navigateNext();
|
5503
|
+
}
|
5504
|
+
}
|
5505
|
+
else {
|
5506
|
+
navigateRight();
|
5507
|
+
}
|
4930
5508
|
}
|
4931
5509
|
else if( deltaY > touch.threshold ) {
|
4932
5510
|
touch.captured = true;
|
4933
|
-
|
5511
|
+
if( config.navigationMode === 'linear' ) {
|
5512
|
+
navigatePrev();
|
5513
|
+
}
|
5514
|
+
else {
|
5515
|
+
navigateUp();
|
5516
|
+
}
|
4934
5517
|
}
|
4935
5518
|
else if( deltaY < -touch.threshold ) {
|
4936
5519
|
touch.captured = true;
|
4937
|
-
|
5520
|
+
if( config.navigationMode === 'linear' ) {
|
5521
|
+
navigateNext();
|
5522
|
+
}
|
5523
|
+
else {
|
5524
|
+
navigateDown();
|
5525
|
+
}
|
4938
5526
|
}
|
4939
5527
|
|
4940
5528
|
// If we're embedded, only block touch events if they have
|
@@ -5065,8 +5653,8 @@
|
|
5065
5653
|
/**
|
5066
5654
|
* Event handler for navigation control buttons.
|
5067
5655
|
*/
|
5068
|
-
function onNavigateLeftClicked( event ) { event.preventDefault(); onUserInput(); navigateLeft(); }
|
5069
|
-
function onNavigateRightClicked( event ) { event.preventDefault(); onUserInput(); navigateRight(); }
|
5656
|
+
function onNavigateLeftClicked( event ) { event.preventDefault(); onUserInput(); config.navigationMode === 'linear' ? navigatePrev() : navigateLeft(); }
|
5657
|
+
function onNavigateRightClicked( event ) { event.preventDefault(); onUserInput(); config.navigationMode === 'linear' ? navigateNext() : navigateRight(); }
|
5070
5658
|
function onNavigateUpClicked( event ) { event.preventDefault(); onUserInput(); navigateUp(); }
|
5071
5659
|
function onNavigateDownClicked( event ) { event.preventDefault(); onUserInput(); navigateDown(); }
|
5072
5660
|
function onNavigatePrevClicked( event ) { event.preventDefault(); onUserInput(); navigatePrev(); }
|
@@ -5455,6 +6043,10 @@
|
|
5455
6043
|
// Returns an Array of all slides
|
5456
6044
|
getSlides: getSlides,
|
5457
6045
|
|
6046
|
+
// Returns an Array of objects representing the attributes on
|
6047
|
+
// the slides
|
6048
|
+
getSlidesAttributes: getSlidesAttributes,
|
6049
|
+
|
5458
6050
|
// Returns the total number of slides
|
5459
6051
|
getTotalSlides: getTotalSlides,
|
5460
6052
|
|
@@ -5467,6 +6059,15 @@
|
|
5467
6059
|
// Returns the speaker notes string for a slide, or null
|
5468
6060
|
getSlideNotes: getSlideNotes,
|
5469
6061
|
|
6062
|
+
// Returns an array with all horizontal/vertical slides in the deck
|
6063
|
+
getHorizontalSlides: getHorizontalSlides,
|
6064
|
+
getVerticalSlides: getVerticalSlides,
|
6065
|
+
|
6066
|
+
// Checks if the presentation contains two or more
|
6067
|
+
// horizontal/vertical slides
|
6068
|
+
hasHorizontalSlides: hasHorizontalSlides,
|
6069
|
+
hasVerticalSlides: hasVerticalSlides,
|
6070
|
+
|
5470
6071
|
// Returns the previous slide element, may be null
|
5471
6072
|
getPreviousSlide: function() {
|
5472
6073
|
return previousSlide;
|
@@ -5505,6 +6106,16 @@
|
|
5505
6106
|
return query;
|
5506
6107
|
},
|
5507
6108
|
|
6109
|
+
// Returns the top-level DOM element
|
6110
|
+
getRevealElement: function() {
|
6111
|
+
return dom.wrapper || document.querySelector( '.reveal' );
|
6112
|
+
},
|
6113
|
+
|
6114
|
+
// Returns a hash with all registered plugins
|
6115
|
+
getPlugins: function() {
|
6116
|
+
return plugins;
|
6117
|
+
},
|
6118
|
+
|
5508
6119
|
// Returns true if we're currently on the first slide
|
5509
6120
|
isFirstSlide: function() {
|
5510
6121
|
return ( indexh === 0 && indexv === 0 );
|
@@ -5546,22 +6157,25 @@
|
|
5546
6157
|
// Forward event binding to the reveal DOM element
|
5547
6158
|
addEventListener: function( type, listener, useCapture ) {
|
5548
6159
|
if( 'addEventListener' in window ) {
|
5549
|
-
|
6160
|
+
Reveal.getRevealElement().addEventListener( type, listener, useCapture );
|
5550
6161
|
}
|
5551
6162
|
},
|
5552
6163
|
removeEventListener: function( type, listener, useCapture ) {
|
5553
6164
|
if( 'addEventListener' in window ) {
|
5554
|
-
|
6165
|
+
Reveal.getRevealElement().removeEventListener( type, listener, useCapture );
|
5555
6166
|
}
|
5556
6167
|
},
|
5557
6168
|
|
5558
|
-
// Adds a custom key binding
|
6169
|
+
// Adds/removes a custom key binding
|
5559
6170
|
addKeyBinding: addKeyBinding,
|
5560
|
-
|
5561
|
-
// Removes a custom key binding
|
5562
6171
|
removeKeyBinding: removeKeyBinding,
|
5563
6172
|
|
5564
|
-
//
|
6173
|
+
// API for registering and retrieving plugins
|
6174
|
+
registerPlugin: registerPlugin,
|
6175
|
+
hasPlugin: hasPlugin,
|
6176
|
+
getPlugin: getPlugin,
|
6177
|
+
|
6178
|
+
// Programmatically triggers a keyboard event
|
5565
6179
|
triggerKey: function( keyCode ) {
|
5566
6180
|
onDocumentKeyDown( { keyCode: keyCode } );
|
5567
6181
|
},
|