reveal-ck 3.9.2 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
},
|