slide_hero 0.0.10 → 0.0.11
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 +4 -4
- data/.ruby-version +1 -1
- data/Gemfile +1 -1
- data/README.md +2 -2
- data/lib/slide_hero/blockquote.rb +10 -0
- data/lib/slide_hero/pluggable.rb +4 -4
- data/lib/slide_hero/plugins.rb +0 -1
- data/lib/slide_hero/presentation.rb +3 -3
- data/lib/slide_hero/slide.rb +2 -2
- data/lib/slide_hero/version.rb +1 -1
- data/lib/slide_hero/views/blockquote.html.erb +3 -0
- data/lib/slide_hero.rb +1 -0
- data/test/slide_hero/blockquote_spec.rb +13 -0
- data/test/slide_hero/plugins_spec.rb +0 -1
- data/test/slide_hero/presentation_spec.rb +2 -1
- data/test/slide_hero/slide_spec.rb +14 -3
- data/test/slide_hero_spec.rb +1 -0
- data/vendor/reveal.js/.gitignore +6 -1
- data/vendor/reveal.js/.travis.yml +1 -1
- data/vendor/reveal.js/CONTRIBUTING.md +4 -0
- data/vendor/reveal.js/Gruntfile.js +26 -24
- data/vendor/reveal.js/LICENSE +1 -1
- data/vendor/reveal.js/README.md +215 -124
- data/vendor/reveal.js/bower.json +27 -0
- data/vendor/reveal.js/css/print/paper.css +1 -1
- data/vendor/reveal.js/css/print/pdf.css +30 -27
- data/vendor/reveal.js/css/reveal.css +381 -191
- data/vendor/reveal.js/css/reveal.scss +259 -164
- data/vendor/reveal.js/css/theme/README.md +2 -6
- data/vendor/reveal.js/css/theme/beige.css +75 -49
- data/vendor/reveal.js/css/theme/black.css +64 -38
- data/vendor/reveal.js/css/theme/blood.css +75 -56
- data/vendor/reveal.js/css/theme/league.css +69 -43
- data/vendor/reveal.js/css/theme/moon.css +69 -43
- data/vendor/reveal.js/css/theme/night.css +64 -38
- data/vendor/reveal.js/css/theme/serif.css +66 -40
- data/vendor/reveal.js/css/theme/simple.css +63 -37
- data/vendor/reveal.js/css/theme/sky.css +69 -43
- data/vendor/reveal.js/css/theme/solarized.css +69 -43
- data/vendor/reveal.js/css/theme/source/black.scss +1 -1
- data/vendor/reveal.js/css/theme/source/blood.scss +3 -15
- data/vendor/reveal.js/css/theme/source/white.scss +1 -1
- data/vendor/reveal.js/css/theme/template/theme.scss +30 -23
- data/vendor/reveal.js/css/theme/white.css +69 -43
- data/vendor/reveal.js/demo.html +410 -0
- data/vendor/reveal.js/index.html +13 -371
- data/vendor/reveal.js/js/reveal.js +643 -175
- data/vendor/reveal.js/lib/css/zenburn.css +41 -78
- data/vendor/reveal.js/lib/js/head.min.js +9 -8
- data/vendor/reveal.js/package.json +20 -24
- data/vendor/reveal.js/plugin/highlight/highlight.js +4 -3
- data/vendor/reveal.js/plugin/markdown/example.html +1 -1
- data/vendor/reveal.js/plugin/markdown/markdown.js +19 -7
- data/vendor/reveal.js/plugin/markdown/marked.js +2 -33
- data/vendor/reveal.js/plugin/math/math.js +5 -2
- data/vendor/reveal.js/plugin/multiplex/client.js +1 -1
- data/vendor/reveal.js/plugin/multiplex/index.js +24 -16
- data/vendor/reveal.js/plugin/multiplex/master.js +22 -42
- data/vendor/reveal.js/plugin/multiplex/package.json +19 -0
- data/vendor/reveal.js/plugin/notes/notes.html +11 -3
- data/vendor/reveal.js/plugin/notes/notes.js +19 -5
- data/vendor/reveal.js/plugin/notes-server/client.js +6 -1
- data/vendor/reveal.js/plugin/notes-server/index.js +17 -14
- data/vendor/reveal.js/plugin/notes-server/notes.html +17 -6
- data/vendor/reveal.js/plugin/print-pdf/print-pdf.js +1 -1
- data/vendor/reveal.js/plugin/zoom-js/zoom.js +1 -1
- data/vendor/reveal.js/test/examples/slide-backgrounds.html +1 -1
- data/vendor/reveal.js/test/examples/slide-transitions.html +101 -0
- data/vendor/reveal.js/test/test-markdown-element-attributes.html +3 -3
- data/vendor/reveal.js/test/test-markdown-element-attributes.js +1 -1
- data/vendor/reveal.js/test/test.html +5 -1
- data/vendor/reveal.js/test/test.js +26 -1
- metadata +11 -5
- data/vendor/reveal.js/plugin/leap/leap.js +0 -159
- data/vendor/reveal.js/plugin/remotes/remotes.js +0 -39
@@ -3,7 +3,7 @@
|
|
3
3
|
* http://lab.hakim.se/reveal-js
|
4
4
|
* MIT licensed
|
5
5
|
*
|
6
|
-
* Copyright (C)
|
6
|
+
* Copyright (C) 2016 Hakim El Hattab, http://hakim.se
|
7
7
|
*/
|
8
8
|
(function( root, factory ) {
|
9
9
|
if( typeof define === 'function' && define.amd ) {
|
@@ -25,12 +25,16 @@
|
|
25
25
|
|
26
26
|
var Reveal;
|
27
27
|
|
28
|
+
// The reveal.js version
|
29
|
+
var VERSION = '3.3.0';
|
30
|
+
|
28
31
|
var SLIDES_SELECTOR = '.slides section',
|
29
32
|
HORIZONTAL_SLIDES_SELECTOR = '.slides>section',
|
30
33
|
VERTICAL_SLIDES_SELECTOR = '.slides>section.present>section',
|
31
34
|
HOME_SLIDE_SELECTOR = '.slides>section:first-of-type',
|
35
|
+
UA = navigator.userAgent,
|
32
36
|
|
33
|
-
//
|
37
|
+
// Configuration defaults, can be overridden at initialization time
|
34
38
|
config = {
|
35
39
|
|
36
40
|
// The "normal" size of the presentation, aspect ratio will be preserved
|
@@ -78,6 +82,9 @@
|
|
78
82
|
// Change the presentation direction to be RTL
|
79
83
|
rtl: false,
|
80
84
|
|
85
|
+
// Randomizes the order of slides each time the presentation loads
|
86
|
+
shuffle: false,
|
87
|
+
|
81
88
|
// Turns fragments on and off globally
|
82
89
|
fragments: true,
|
83
90
|
|
@@ -92,6 +99,9 @@
|
|
92
99
|
// Flags if it should be possible to pause the presentation (blackout)
|
93
100
|
pause: true,
|
94
101
|
|
102
|
+
// Flags if speaker notes should be visible to all viewers
|
103
|
+
showNotes: false,
|
104
|
+
|
95
105
|
// Number of milliseconds between automatically proceeding to the
|
96
106
|
// next slide, disabled when set to 0, this value can be overwritten
|
97
107
|
// by using a data-autoslide attribute on your slides
|
@@ -100,6 +110,9 @@
|
|
100
110
|
// Stop auto-sliding after user input
|
101
111
|
autoSlideStoppable: true,
|
102
112
|
|
113
|
+
// Use this method for navigation when auto-sliding (defaults to navigateNext)
|
114
|
+
autoSlideMethod: null,
|
115
|
+
|
103
116
|
// Enable slide navigation via mouse wheel
|
104
117
|
mouseWheel: false,
|
105
118
|
|
@@ -136,6 +149,10 @@
|
|
136
149
|
// Parallax background size
|
137
150
|
parallaxBackgroundSize: '', // CSS syntax, e.g. "3000px 2000px"
|
138
151
|
|
152
|
+
// Amount of pixels to move the parallax background per slide step
|
153
|
+
parallaxBackgroundHorizontal: null,
|
154
|
+
parallaxBackgroundVertical: null,
|
155
|
+
|
139
156
|
// Number of slides away from the current that are visible
|
140
157
|
viewDistance: 3,
|
141
158
|
|
@@ -147,6 +164,13 @@
|
|
147
164
|
// Flags if reveal.js is loaded (has dispatched the 'ready' event)
|
148
165
|
loaded = false,
|
149
166
|
|
167
|
+
// Flags if the overview mode is currently active
|
168
|
+
overview = false,
|
169
|
+
|
170
|
+
// Holds the dimensions of our overview slides, including margins
|
171
|
+
overviewSlideWidth = null,
|
172
|
+
overviewSlideHeight = null,
|
173
|
+
|
150
174
|
// The horizontal and vertical index of the currently active slide
|
151
175
|
indexh,
|
152
176
|
indexv,
|
@@ -165,6 +189,10 @@
|
|
165
189
|
// The current scale of the presentation (see width/height config)
|
166
190
|
scale = 1,
|
167
191
|
|
192
|
+
// CSS transform that is currently applied to the slides container,
|
193
|
+
// split into two groups
|
194
|
+
slidesTransform = { layout: '', overview: '' },
|
195
|
+
|
168
196
|
// Cached references to DOM elements
|
169
197
|
dom = {},
|
170
198
|
|
@@ -174,6 +202,9 @@
|
|
174
202
|
// Client is a mobile device, see #checkCapabilities()
|
175
203
|
isMobileDevice,
|
176
204
|
|
205
|
+
// Client is a desktop Chrome, see #checkCapabilities()
|
206
|
+
isChrome,
|
207
|
+
|
177
208
|
// Throttles mouse wheel navigation
|
178
209
|
lastMouseWheelStep = 0,
|
179
210
|
|
@@ -227,14 +258,18 @@
|
|
227
258
|
if( !features.transforms2d && !features.transforms3d ) {
|
228
259
|
document.body.setAttribute( 'class', 'no-transforms' );
|
229
260
|
|
230
|
-
// Since JS won't be running any further, we
|
231
|
-
//
|
232
|
-
var images = document.getElementsByTagName( 'img' )
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
261
|
+
// Since JS won't be running any further, we load all lazy
|
262
|
+
// loading elements upfront
|
263
|
+
var images = toArray( document.getElementsByTagName( 'img' ) ),
|
264
|
+
iframes = toArray( document.getElementsByTagName( 'iframe' ) );
|
265
|
+
|
266
|
+
var lazyLoadable = images.concat( iframes );
|
267
|
+
|
268
|
+
for( var i = 0, len = lazyLoadable.length; i < len; i++ ) {
|
269
|
+
var element = lazyLoadable[i];
|
270
|
+
if( element.getAttribute( 'data-src' ) ) {
|
271
|
+
element.setAttribute( 'src', element.getAttribute( 'data-src' ) );
|
272
|
+
element.removeAttribute( 'data-src' );
|
238
273
|
}
|
239
274
|
}
|
240
275
|
|
@@ -274,26 +309,37 @@
|
|
274
309
|
*/
|
275
310
|
function checkCapabilities() {
|
276
311
|
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
'perspective' in document.body.style;
|
312
|
+
isMobileDevice = /(iphone|ipod|ipad|android)/gi.test( UA );
|
313
|
+
isChrome = /chrome/i.test( UA ) && !/edge/i.test( UA );
|
314
|
+
|
315
|
+
var testElement = document.createElement( 'div' );
|
282
316
|
|
283
|
-
features.
|
284
|
-
'
|
285
|
-
'
|
286
|
-
'
|
287
|
-
'
|
317
|
+
features.transforms3d = 'WebkitPerspective' in testElement.style ||
|
318
|
+
'MozPerspective' in testElement.style ||
|
319
|
+
'msPerspective' in testElement.style ||
|
320
|
+
'OPerspective' in testElement.style ||
|
321
|
+
'perspective' in testElement.style;
|
322
|
+
|
323
|
+
features.transforms2d = 'WebkitTransform' in testElement.style ||
|
324
|
+
'MozTransform' in testElement.style ||
|
325
|
+
'msTransform' in testElement.style ||
|
326
|
+
'OTransform' in testElement.style ||
|
327
|
+
'transform' in testElement.style;
|
288
328
|
|
289
329
|
features.requestAnimationFrameMethod = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame;
|
290
330
|
features.requestAnimationFrame = typeof features.requestAnimationFrameMethod === 'function';
|
291
331
|
|
292
332
|
features.canvas = !!document.createElement( 'canvas' ).getContext;
|
293
333
|
|
294
|
-
|
334
|
+
// Transitions in the overview are disabled in desktop and
|
335
|
+
// Safari due to lag
|
336
|
+
features.overviewTransitions = !/Version\/[\d\.]+.*Safari/.test( UA );
|
295
337
|
|
296
|
-
|
338
|
+
// Flags if we should use zoom instead of transform to scale
|
339
|
+
// up slides. Zoom produces crisper results but has a lot of
|
340
|
+
// xbrowser quirks so we only use it in whitelsited browsers.
|
341
|
+
features.zoom = 'zoom' in testElement.style && !isMobileDevice &&
|
342
|
+
( isChrome || /Version\/[\d\.]+.*Safari/.test( UA ) );
|
297
343
|
|
298
344
|
}
|
299
345
|
|
@@ -373,6 +419,9 @@
|
|
373
419
|
// Listen to messages posted to this window
|
374
420
|
setupPostMessage();
|
375
421
|
|
422
|
+
// Prevent the slides from being scrolled out of view
|
423
|
+
setupScrollPrevention();
|
424
|
+
|
376
425
|
// Resets all vertical slides so that only the first is visible
|
377
426
|
resetVerticalSlides();
|
378
427
|
|
@@ -435,14 +484,18 @@
|
|
435
484
|
|
436
485
|
// Arrow controls
|
437
486
|
createSingletonNode( dom.wrapper, 'aside', 'controls',
|
438
|
-
'<
|
439
|
-
'<
|
440
|
-
'<
|
441
|
-
'<
|
487
|
+
'<button class="navigate-left" aria-label="previous slide"></button>' +
|
488
|
+
'<button class="navigate-right" aria-label="next slide"></button>' +
|
489
|
+
'<button class="navigate-up" aria-label="above slide"></button>' +
|
490
|
+
'<button class="navigate-down" aria-label="below slide"></button>' );
|
442
491
|
|
443
492
|
// Slide number
|
444
493
|
dom.slideNumber = createSingletonNode( dom.wrapper, 'div', 'slide-number', '' );
|
445
494
|
|
495
|
+
// Element containing notes that are visible to the audience
|
496
|
+
dom.speakerNotes = createSingletonNode( dom.wrapper, 'div', 'speaker-notes', null );
|
497
|
+
dom.speakerNotes.setAttribute( 'data-prevent-swipe', '' );
|
498
|
+
|
446
499
|
// Overlay graphic which is displayed during the paused mode
|
447
500
|
createSingletonNode( dom.wrapper, 'div', 'pause-overlay', null );
|
448
501
|
|
@@ -513,6 +566,19 @@
|
|
513
566
|
document.body.style.width = pageWidth + 'px';
|
514
567
|
document.body.style.height = pageHeight + 'px';
|
515
568
|
|
569
|
+
// Add each slide's index as attributes on itself, we need these
|
570
|
+
// indices to generate slide numbers below
|
571
|
+
toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ).forEach( function( hslide, h ) {
|
572
|
+
hslide.setAttribute( 'data-index-h', h );
|
573
|
+
|
574
|
+
if( hslide.classList.contains( 'stack' ) ) {
|
575
|
+
toArray( hslide.querySelectorAll( 'section' ) ).forEach( function( vslide, v ) {
|
576
|
+
vslide.setAttribute( 'data-index-h', h );
|
577
|
+
vslide.setAttribute( 'data-index-v', v );
|
578
|
+
} );
|
579
|
+
}
|
580
|
+
} );
|
581
|
+
|
516
582
|
// Slide and slide background layout
|
517
583
|
toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR ) ).forEach( function( slide ) {
|
518
584
|
|
@@ -545,6 +611,34 @@
|
|
545
611
|
background.style.top = -top + 'px';
|
546
612
|
background.style.left = -left + 'px';
|
547
613
|
}
|
614
|
+
|
615
|
+
// Inject notes if `showNotes` is enabled
|
616
|
+
if( config.showNotes ) {
|
617
|
+
var notes = getSlideNotes( slide );
|
618
|
+
if( notes ) {
|
619
|
+
var notesSpacing = 8;
|
620
|
+
var notesElement = document.createElement( 'div' );
|
621
|
+
notesElement.classList.add( 'speaker-notes' );
|
622
|
+
notesElement.classList.add( 'speaker-notes-pdf' );
|
623
|
+
notesElement.innerHTML = notes;
|
624
|
+
notesElement.style.left = ( notesSpacing - left ) + 'px';
|
625
|
+
notesElement.style.bottom = ( notesSpacing - top ) + 'px';
|
626
|
+
notesElement.style.width = ( pageWidth - notesSpacing*2 ) + 'px';
|
627
|
+
slide.appendChild( notesElement );
|
628
|
+
}
|
629
|
+
}
|
630
|
+
|
631
|
+
// Inject slide numbers if `slideNumbers` are enabled
|
632
|
+
if( config.slideNumber ) {
|
633
|
+
var slideNumberH = parseInt( slide.getAttribute( 'data-index-h' ), 10 ) + 1,
|
634
|
+
slideNumberV = parseInt( slide.getAttribute( 'data-index-v' ), 10 ) + 1;
|
635
|
+
|
636
|
+
var numberElement = document.createElement( 'div' );
|
637
|
+
numberElement.classList.add( 'slide-number' );
|
638
|
+
numberElement.classList.add( 'slide-number-pdf' );
|
639
|
+
numberElement.innerHTML = formatSlideNumber( slideNumberH, '.', slideNumberV );
|
640
|
+
background.appendChild( numberElement );
|
641
|
+
}
|
548
642
|
}
|
549
643
|
|
550
644
|
} );
|
@@ -556,6 +650,26 @@
|
|
556
650
|
|
557
651
|
}
|
558
652
|
|
653
|
+
/**
|
654
|
+
* This is an unfortunate necessity. Some actions – such as
|
655
|
+
* an input field being focused in an iframe or using the
|
656
|
+
* keyboard to expand text selection beyond the bounds of
|
657
|
+
* a slide – can trigger our content to be pushed out of view.
|
658
|
+
* This scrolling can not be prevented by hiding overflow in
|
659
|
+
* CSS (we already do) so we have to resort to repeatedly
|
660
|
+
* checking if the slides have been offset :(
|
661
|
+
*/
|
662
|
+
function setupScrollPrevention() {
|
663
|
+
|
664
|
+
setInterval( function() {
|
665
|
+
if( dom.wrapper.scrollTop !== 0 || dom.wrapper.scrollLeft !== 0 ) {
|
666
|
+
dom.wrapper.scrollTop = 0;
|
667
|
+
dom.wrapper.scrollLeft = 0;
|
668
|
+
}
|
669
|
+
}, 1000 );
|
670
|
+
|
671
|
+
}
|
672
|
+
|
559
673
|
/**
|
560
674
|
* Creates an HTML element and returns a reference to it.
|
561
675
|
* If the element already exists the existing instance will
|
@@ -757,7 +871,7 @@
|
|
757
871
|
var data = event.data;
|
758
872
|
|
759
873
|
// Make sure we're dealing with JSON
|
760
|
-
if( data.charAt( 0 ) === '{' && data.charAt( data.length - 1 ) === '}' ) {
|
874
|
+
if( typeof data === 'string' && data.charAt( 0 ) === '{' && data.charAt( data.length - 1 ) === '}' ) {
|
761
875
|
data = JSON.parse( data );
|
762
876
|
|
763
877
|
// Check if the requested method can be found
|
@@ -794,6 +908,11 @@
|
|
794
908
|
|
795
909
|
dom.controls.style.display = config.controls ? 'block' : 'none';
|
796
910
|
dom.progress.style.display = config.progress ? 'block' : 'none';
|
911
|
+
dom.slideNumber.style.display = config.slideNumber && !isPrintingPDF() ? 'block' : 'none';
|
912
|
+
|
913
|
+
if( config.shuffle ) {
|
914
|
+
shuffle();
|
915
|
+
}
|
797
916
|
|
798
917
|
if( config.rtl ) {
|
799
918
|
dom.wrapper.classList.add( 'rtl' );
|
@@ -814,6 +933,13 @@
|
|
814
933
|
resume();
|
815
934
|
}
|
816
935
|
|
936
|
+
if( config.showNotes ) {
|
937
|
+
dom.speakerNotes.classList.add( 'visible' );
|
938
|
+
}
|
939
|
+
else {
|
940
|
+
dom.speakerNotes.classList.remove( 'visible' );
|
941
|
+
}
|
942
|
+
|
817
943
|
if( config.mouseWheel ) {
|
818
944
|
document.addEventListener( 'DOMMouseScroll', onDocumentMouseScroll, false ); // FF
|
819
945
|
document.addEventListener( 'mousewheel', onDocumentMouseScroll, false );
|
@@ -931,7 +1057,7 @@
|
|
931
1057
|
|
932
1058
|
// Only support touch for Android, fixes double navigations in
|
933
1059
|
// stock browser
|
934
|
-
if(
|
1060
|
+
if( UA.match( /android/gi ) ) {
|
935
1061
|
pointerEvents = [ 'touchstart' ];
|
936
1062
|
}
|
937
1063
|
|
@@ -1051,11 +1177,31 @@
|
|
1051
1177
|
element.style.WebkitTransform = transform;
|
1052
1178
|
element.style.MozTransform = transform;
|
1053
1179
|
element.style.msTransform = transform;
|
1054
|
-
element.style.OTransform = transform;
|
1055
1180
|
element.style.transform = transform;
|
1056
1181
|
|
1057
1182
|
}
|
1058
1183
|
|
1184
|
+
/**
|
1185
|
+
* Applies CSS transforms to the slides container. The container
|
1186
|
+
* is transformed from two separate sources: layout and the overview
|
1187
|
+
* mode.
|
1188
|
+
*/
|
1189
|
+
function transformSlides( transforms ) {
|
1190
|
+
|
1191
|
+
// Pick up new transforms from arguments
|
1192
|
+
if( typeof transforms.layout === 'string' ) slidesTransform.layout = transforms.layout;
|
1193
|
+
if( typeof transforms.overview === 'string' ) slidesTransform.overview = transforms.overview;
|
1194
|
+
|
1195
|
+
// Apply the transforms to the slides container
|
1196
|
+
if( slidesTransform.layout ) {
|
1197
|
+
transformElement( dom.slides, slidesTransform.layout + ' ' + slidesTransform.overview );
|
1198
|
+
}
|
1199
|
+
else {
|
1200
|
+
transformElement( dom.slides, slidesTransform.overview );
|
1201
|
+
}
|
1202
|
+
|
1203
|
+
}
|
1204
|
+
|
1059
1205
|
/**
|
1060
1206
|
* Injects the given CSS styles into the DOM.
|
1061
1207
|
*/
|
@@ -1074,7 +1220,7 @@
|
|
1074
1220
|
}
|
1075
1221
|
|
1076
1222
|
/**
|
1077
|
-
*
|
1223
|
+
* Converts various color input formats to an {r:0,g:0,b:0} object.
|
1078
1224
|
*
|
1079
1225
|
* @param {String} color The string representation of a color,
|
1080
1226
|
* the following formats are supported:
|
@@ -1465,20 +1611,28 @@
|
|
1465
1611
|
dom.slides.style.top = '';
|
1466
1612
|
dom.slides.style.bottom = '';
|
1467
1613
|
dom.slides.style.right = '';
|
1468
|
-
|
1614
|
+
transformSlides( { layout: '' } );
|
1469
1615
|
}
|
1470
1616
|
else {
|
1471
|
-
// Prefer
|
1472
|
-
|
1617
|
+
// Prefer zoom for scaling up so that content remains crisp.
|
1618
|
+
// Don't use zoom to scale down since that can lead to shifts
|
1619
|
+
// in text layout/line breaks.
|
1620
|
+
if( scale > 1 && features.zoom ) {
|
1473
1621
|
dom.slides.style.zoom = scale;
|
1622
|
+
dom.slides.style.left = '';
|
1623
|
+
dom.slides.style.top = '';
|
1624
|
+
dom.slides.style.bottom = '';
|
1625
|
+
dom.slides.style.right = '';
|
1626
|
+
transformSlides( { layout: '' } );
|
1474
1627
|
}
|
1475
1628
|
// Apply scale transform as a fallback
|
1476
1629
|
else {
|
1630
|
+
dom.slides.style.zoom = '';
|
1477
1631
|
dom.slides.style.left = '50%';
|
1478
1632
|
dom.slides.style.top = '50%';
|
1479
1633
|
dom.slides.style.bottom = 'auto';
|
1480
1634
|
dom.slides.style.right = 'auto';
|
1481
|
-
|
1635
|
+
transformSlides( { layout: 'translate(-50%, -50%) scale('+ scale +')' } );
|
1482
1636
|
}
|
1483
1637
|
}
|
1484
1638
|
|
@@ -1566,7 +1720,7 @@
|
|
1566
1720
|
};
|
1567
1721
|
|
1568
1722
|
// Reduce available space by margin
|
1569
|
-
size.presentationWidth -= ( size.
|
1723
|
+
size.presentationWidth -= ( size.presentationWidth * config.margin );
|
1570
1724
|
size.presentationHeight -= ( size.presentationHeight * config.margin );
|
1571
1725
|
|
1572
1726
|
// Slide width may be a percentage of available width
|
@@ -1620,81 +1774,114 @@
|
|
1620
1774
|
}
|
1621
1775
|
|
1622
1776
|
/**
|
1623
|
-
* Displays the overview of slides (quick nav) by
|
1624
|
-
*
|
1625
|
-
*
|
1626
|
-
* Experimental feature, might be dropped if perf
|
1627
|
-
* can't be improved.
|
1777
|
+
* Displays the overview of slides (quick nav) by scaling
|
1778
|
+
* down and arranging all slide elements.
|
1628
1779
|
*/
|
1629
1780
|
function activateOverview() {
|
1630
1781
|
|
1631
1782
|
// Only proceed if enabled in config
|
1632
|
-
if( config.overview ) {
|
1633
|
-
|
1634
|
-
// Don't auto-slide while in overview mode
|
1635
|
-
cancelAutoSlide();
|
1783
|
+
if( config.overview && !isOverview() ) {
|
1636
1784
|
|
1637
|
-
|
1638
|
-
|
1639
|
-
// Vary the depth of the overview based on screen size
|
1640
|
-
var depth = window.innerWidth < 400 ? 1000 : 2500;
|
1785
|
+
overview = true;
|
1641
1786
|
|
1642
1787
|
dom.wrapper.classList.add( 'overview' );
|
1643
1788
|
dom.wrapper.classList.remove( 'overview-deactivating' );
|
1644
1789
|
|
1645
|
-
|
1646
|
-
|
1647
|
-
|
1648
|
-
|
1649
|
-
|
1790
|
+
if( features.overviewTransitions ) {
|
1791
|
+
setTimeout( function() {
|
1792
|
+
dom.wrapper.classList.add( 'overview-animated' );
|
1793
|
+
}, 1 );
|
1794
|
+
}
|
1650
1795
|
|
1651
|
-
|
1796
|
+
// Don't auto-slide while in overview mode
|
1797
|
+
cancelAutoSlide();
|
1652
1798
|
|
1653
|
-
|
1654
|
-
|
1799
|
+
// Move the backgrounds element into the slide container to
|
1800
|
+
// that the same scaling is applied
|
1801
|
+
dom.slides.appendChild( dom.background );
|
1655
1802
|
|
1656
|
-
|
1803
|
+
// Clicking on an overview slide navigates to it
|
1804
|
+
toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR ) ).forEach( function( slide ) {
|
1805
|
+
if( !slide.classList.contains( 'stack' ) ) {
|
1806
|
+
slide.addEventListener( 'click', onOverviewSlideClicked, true );
|
1807
|
+
}
|
1808
|
+
} );
|
1657
1809
|
|
1658
|
-
|
1810
|
+
// Calculate slide sizes
|
1811
|
+
var margin = 70;
|
1812
|
+
var slideSize = getComputedSlideSize();
|
1813
|
+
overviewSlideWidth = slideSize.width + margin;
|
1814
|
+
overviewSlideHeight = slideSize.height + margin;
|
1659
1815
|
|
1660
|
-
|
1661
|
-
|
1816
|
+
// Reverse in RTL mode
|
1817
|
+
if( config.rtl ) {
|
1818
|
+
overviewSlideWidth = -overviewSlideWidth;
|
1819
|
+
}
|
1662
1820
|
|
1663
|
-
|
1821
|
+
updateSlidesVisibility();
|
1822
|
+
layoutOverview();
|
1823
|
+
updateOverview();
|
1664
1824
|
|
1665
|
-
|
1666
|
-
vslide.setAttribute( 'data-index-v', j );
|
1825
|
+
layout();
|
1667
1826
|
|
1668
|
-
|
1669
|
-
|
1827
|
+
// Notify observers of the overview showing
|
1828
|
+
dispatchEvent( 'overviewshown', {
|
1829
|
+
'indexh': indexh,
|
1830
|
+
'indexv': indexv,
|
1831
|
+
'currentSlide': currentSlide
|
1832
|
+
} );
|
1670
1833
|
|
1671
|
-
|
1672
|
-
vslide.addEventListener( 'click', onOverviewSlideClicked, true );
|
1673
|
-
}
|
1834
|
+
}
|
1674
1835
|
|
1675
|
-
|
1676
|
-
else {
|
1836
|
+
}
|
1677
1837
|
|
1678
|
-
|
1679
|
-
|
1838
|
+
/**
|
1839
|
+
* Uses CSS transforms to position all slides in a grid for
|
1840
|
+
* display inside of the overview mode.
|
1841
|
+
*/
|
1842
|
+
function layoutOverview() {
|
1680
1843
|
|
1681
|
-
|
1682
|
-
|
1844
|
+
// Layout slides
|
1845
|
+
toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ).forEach( function( hslide, h ) {
|
1846
|
+
hslide.setAttribute( 'data-index-h', h );
|
1847
|
+
transformElement( hslide, 'translate3d(' + ( h * overviewSlideWidth ) + 'px, 0, 0)' );
|
1683
1848
|
|
1684
|
-
|
1849
|
+
if( hslide.classList.contains( 'stack' ) ) {
|
1685
1850
|
|
1686
|
-
|
1851
|
+
toArray( hslide.querySelectorAll( 'section' ) ).forEach( function( vslide, v ) {
|
1852
|
+
vslide.setAttribute( 'data-index-h', h );
|
1853
|
+
vslide.setAttribute( 'data-index-v', v );
|
1687
1854
|
|
1688
|
-
|
1689
|
-
// Notify observers of the overview showing
|
1690
|
-
dispatchEvent( 'overviewshown', {
|
1691
|
-
'indexh': indexh,
|
1692
|
-
'indexv': indexv,
|
1693
|
-
'currentSlide': currentSlide
|
1855
|
+
transformElement( vslide, 'translate3d(0, ' + ( v * overviewSlideHeight ) + 'px, 0)' );
|
1694
1856
|
} );
|
1857
|
+
|
1695
1858
|
}
|
1859
|
+
} );
|
1696
1860
|
|
1697
|
-
|
1861
|
+
// Layout slide backgrounds
|
1862
|
+
toArray( dom.background.childNodes ).forEach( function( hbackground, h ) {
|
1863
|
+
transformElement( hbackground, 'translate3d(' + ( h * overviewSlideWidth ) + 'px, 0, 0)' );
|
1864
|
+
|
1865
|
+
toArray( hbackground.querySelectorAll( '.slide-background' ) ).forEach( function( vbackground, v ) {
|
1866
|
+
transformElement( vbackground, 'translate3d(0, ' + ( v * overviewSlideHeight ) + 'px, 0)' );
|
1867
|
+
} );
|
1868
|
+
} );
|
1869
|
+
|
1870
|
+
}
|
1871
|
+
|
1872
|
+
/**
|
1873
|
+
* Moves the overview viewport to the current slides.
|
1874
|
+
* Called each time the current slide changes.
|
1875
|
+
*/
|
1876
|
+
function updateOverview() {
|
1877
|
+
|
1878
|
+
transformSlides( {
|
1879
|
+
overview: [
|
1880
|
+
'translateX('+ ( -indexh * overviewSlideWidth ) +'px)',
|
1881
|
+
'translateY('+ ( -indexv * overviewSlideHeight ) +'px)',
|
1882
|
+
'translateZ('+ ( window.innerWidth < 400 ? -1000 : -2500 ) +'px)'
|
1883
|
+
].join( ' ' )
|
1884
|
+
} );
|
1698
1885
|
|
1699
1886
|
}
|
1700
1887
|
|
@@ -1707,7 +1894,10 @@
|
|
1707
1894
|
// Only proceed if enabled in config
|
1708
1895
|
if( config.overview ) {
|
1709
1896
|
|
1897
|
+
overview = false;
|
1898
|
+
|
1710
1899
|
dom.wrapper.classList.remove( 'overview' );
|
1900
|
+
dom.wrapper.classList.remove( 'overview-animated' );
|
1711
1901
|
|
1712
1902
|
// Temporarily add a class so that transitions can do different things
|
1713
1903
|
// depending on whether they are exiting/entering overview, or just
|
@@ -1718,16 +1908,27 @@
|
|
1718
1908
|
dom.wrapper.classList.remove( 'overview-deactivating' );
|
1719
1909
|
}, 1 );
|
1720
1910
|
|
1721
|
-
//
|
1911
|
+
// Move the background element back out
|
1912
|
+
dom.wrapper.appendChild( dom.background );
|
1913
|
+
|
1914
|
+
// Clean up changes made to slides
|
1722
1915
|
toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR ) ).forEach( function( slide ) {
|
1723
|
-
// Resets all transforms to use the external styles
|
1724
1916
|
transformElement( slide, '' );
|
1725
1917
|
|
1726
1918
|
slide.removeEventListener( 'click', onOverviewSlideClicked, true );
|
1727
1919
|
} );
|
1728
1920
|
|
1921
|
+
// Clean up changes made to backgrounds
|
1922
|
+
toArray( dom.background.querySelectorAll( '.slide-background' ) ).forEach( function( background ) {
|
1923
|
+
transformElement( background, '' );
|
1924
|
+
} );
|
1925
|
+
|
1926
|
+
transformSlides( { overview: '' } );
|
1927
|
+
|
1729
1928
|
slide( indexh, indexv );
|
1730
1929
|
|
1930
|
+
layout();
|
1931
|
+
|
1731
1932
|
cueAutoSlide();
|
1732
1933
|
|
1733
1934
|
// Notify observers of the overview hiding
|
@@ -1766,7 +1967,7 @@
|
|
1766
1967
|
*/
|
1767
1968
|
function isOverview() {
|
1768
1969
|
|
1769
|
-
return
|
1970
|
+
return overview;
|
1770
1971
|
|
1771
1972
|
}
|
1772
1973
|
|
@@ -1916,7 +2117,7 @@
|
|
1916
2117
|
|
1917
2118
|
// If no vertical index is specified and the upcoming slide is a
|
1918
2119
|
// stack, resume at its previous vertical index
|
1919
|
-
if( v === undefined ) {
|
2120
|
+
if( v === undefined && !isOverview() ) {
|
1920
2121
|
v = getPreviousVerticalIndex( horizontalSlides[ h ] );
|
1921
2122
|
}
|
1922
2123
|
|
@@ -1966,9 +2167,9 @@
|
|
1966
2167
|
document.documentElement.classList.remove( stateBefore.pop() );
|
1967
2168
|
}
|
1968
2169
|
|
1969
|
-
//
|
2170
|
+
// Update the overview if it's currently active
|
1970
2171
|
if( isOverview() ) {
|
1971
|
-
|
2172
|
+
updateOverview();
|
1972
2173
|
}
|
1973
2174
|
|
1974
2175
|
// Find the current horizontal slide and any possible vertical slides
|
@@ -2037,6 +2238,7 @@
|
|
2037
2238
|
updateBackground();
|
2038
2239
|
updateParallax();
|
2039
2240
|
updateSlideNumber();
|
2241
|
+
updateNotes();
|
2040
2242
|
|
2041
2243
|
// Update the URL hash
|
2042
2244
|
writeURL();
|
@@ -2078,8 +2280,14 @@
|
|
2078
2280
|
updateBackground( true );
|
2079
2281
|
updateSlideNumber();
|
2080
2282
|
updateSlidesVisibility();
|
2283
|
+
updateNotes();
|
2081
2284
|
|
2082
2285
|
formatEmbeddedContent();
|
2286
|
+
startEmbeddedContent( currentSlide );
|
2287
|
+
|
2288
|
+
if( isOverview() ) {
|
2289
|
+
layoutOverview();
|
2290
|
+
}
|
2083
2291
|
|
2084
2292
|
}
|
2085
2293
|
|
@@ -2130,6 +2338,23 @@
|
|
2130
2338
|
|
2131
2339
|
}
|
2132
2340
|
|
2341
|
+
/**
|
2342
|
+
* Randomly shuffles all slides in the deck.
|
2343
|
+
*/
|
2344
|
+
function shuffle() {
|
2345
|
+
|
2346
|
+
var slides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) );
|
2347
|
+
|
2348
|
+
slides.forEach( function( slide ) {
|
2349
|
+
|
2350
|
+
// Insert this slide next to another random slide. This may
|
2351
|
+
// cause the slide to insert before itself but that's fine.
|
2352
|
+
dom.slides.insertBefore( slide, slides[ Math.floor( Math.random() * slides.length ) ] );
|
2353
|
+
|
2354
|
+
} );
|
2355
|
+
|
2356
|
+
}
|
2357
|
+
|
2133
2358
|
/**
|
2134
2359
|
* Updates one dimension of slides by showing the slide
|
2135
2360
|
* with the specified index.
|
@@ -2269,7 +2494,7 @@
|
|
2269
2494
|
viewDistance = isOverview() ? 6 : 2;
|
2270
2495
|
}
|
2271
2496
|
|
2272
|
-
//
|
2497
|
+
// All slides need to be visible when exporting to PDF
|
2273
2498
|
if( isPrintingPDF() ) {
|
2274
2499
|
viewDistance = Number.MAX_VALUE;
|
2275
2500
|
}
|
@@ -2280,8 +2505,14 @@
|
|
2280
2505
|
var verticalSlides = toArray( horizontalSlide.querySelectorAll( 'section' ) ),
|
2281
2506
|
verticalSlidesLength = verticalSlides.length;
|
2282
2507
|
|
2283
|
-
//
|
2284
|
-
distanceX = Math.abs( (
|
2508
|
+
// Determine how far away this slide is from the present
|
2509
|
+
distanceX = Math.abs( ( indexh || 0 ) - x ) || 0;
|
2510
|
+
|
2511
|
+
// If the presentation is looped, distance should measure
|
2512
|
+
// 1 between the first and last slides
|
2513
|
+
if( config.loop ) {
|
2514
|
+
distanceX = Math.abs( ( ( indexh || 0 ) - x ) % ( horizontalSlidesLength - viewDistance ) ) || 0;
|
2515
|
+
}
|
2285
2516
|
|
2286
2517
|
// Show the horizontal slide if it's within the view distance
|
2287
2518
|
if( distanceX < viewDistance ) {
|
@@ -2315,6 +2546,22 @@
|
|
2315
2546
|
|
2316
2547
|
}
|
2317
2548
|
|
2549
|
+
/**
|
2550
|
+
* Pick up notes from the current slide and display tham
|
2551
|
+
* to the viewer.
|
2552
|
+
*
|
2553
|
+
* @see `showNotes` config value
|
2554
|
+
*/
|
2555
|
+
function updateNotes() {
|
2556
|
+
|
2557
|
+
if( config.showNotes && dom.speakerNotes && currentSlide && !isPrintingPDF() ) {
|
2558
|
+
|
2559
|
+
dom.speakerNotes.innerHTML = getSlideNotes() || '';
|
2560
|
+
|
2561
|
+
}
|
2562
|
+
|
2563
|
+
}
|
2564
|
+
|
2318
2565
|
/**
|
2319
2566
|
* Updates the progress bar to reflect the current slide.
|
2320
2567
|
*/
|
@@ -2331,19 +2578,60 @@
|
|
2331
2578
|
|
2332
2579
|
/**
|
2333
2580
|
* Updates the slide number div to reflect the current slide.
|
2581
|
+
*
|
2582
|
+
* The following slide number formats are available:
|
2583
|
+
* "h.v": horizontal . vertical slide number (default)
|
2584
|
+
* "h/v": horizontal / vertical slide number
|
2585
|
+
* "c": flattened slide number
|
2586
|
+
* "c/t": flattened slide number / total slides
|
2334
2587
|
*/
|
2335
2588
|
function updateSlideNumber() {
|
2336
2589
|
|
2337
2590
|
// Update slide number if enabled
|
2338
|
-
if( config.slideNumber && dom.slideNumber) {
|
2591
|
+
if( config.slideNumber && dom.slideNumber ) {
|
2592
|
+
|
2593
|
+
var value = [];
|
2594
|
+
var format = 'h.v';
|
2339
2595
|
|
2340
|
-
//
|
2341
|
-
|
2342
|
-
|
2343
|
-
indexString += ' - ' + indexv;
|
2596
|
+
// Check if a custom number format is available
|
2597
|
+
if( typeof config.slideNumber === 'string' ) {
|
2598
|
+
format = config.slideNumber;
|
2344
2599
|
}
|
2345
2600
|
|
2346
|
-
|
2601
|
+
switch( format ) {
|
2602
|
+
case 'c':
|
2603
|
+
value.push( getSlidePastCount() + 1 );
|
2604
|
+
break;
|
2605
|
+
case 'c/t':
|
2606
|
+
value.push( getSlidePastCount() + 1, '/', getTotalSlides() );
|
2607
|
+
break;
|
2608
|
+
case 'h/v':
|
2609
|
+
value.push( indexh + 1 );
|
2610
|
+
if( isVerticalSlide() ) value.push( '/', indexv + 1 );
|
2611
|
+
break;
|
2612
|
+
default:
|
2613
|
+
value.push( indexh + 1 );
|
2614
|
+
if( isVerticalSlide() ) value.push( '.', indexv + 1 );
|
2615
|
+
}
|
2616
|
+
|
2617
|
+
dom.slideNumber.innerHTML = formatSlideNumber( value[0], value[1], value[2] );
|
2618
|
+
}
|
2619
|
+
|
2620
|
+
}
|
2621
|
+
|
2622
|
+
/**
|
2623
|
+
* Applies HTML formatting to a slide number before it's
|
2624
|
+
* written to the DOM.
|
2625
|
+
*/
|
2626
|
+
function formatSlideNumber( a, delimiter, b ) {
|
2627
|
+
|
2628
|
+
if( typeof b === 'number' && !isNaN( b ) ) {
|
2629
|
+
return '<span class="slide-number-a">'+ a +'</span>' +
|
2630
|
+
'<span class="slide-number-delimiter">'+ delimiter +'</span>' +
|
2631
|
+
'<span class="slide-number-b">'+ b +'</span>';
|
2632
|
+
}
|
2633
|
+
else {
|
2634
|
+
return '<span class="slide-number-a">'+ a +'</span>';
|
2347
2635
|
}
|
2348
2636
|
|
2349
2637
|
}
|
@@ -2472,8 +2760,29 @@
|
|
2472
2760
|
// Start video playback
|
2473
2761
|
var currentVideo = currentBackground.querySelector( 'video' );
|
2474
2762
|
if( currentVideo ) {
|
2475
|
-
|
2476
|
-
|
2763
|
+
|
2764
|
+
var startVideo = function() {
|
2765
|
+
currentVideo.currentTime = 0;
|
2766
|
+
currentVideo.play();
|
2767
|
+
currentVideo.removeEventListener( 'loadeddata', startVideo );
|
2768
|
+
};
|
2769
|
+
|
2770
|
+
if( currentVideo.readyState > 1 ) {
|
2771
|
+
startVideo();
|
2772
|
+
}
|
2773
|
+
else {
|
2774
|
+
currentVideo.addEventListener( 'loadeddata', startVideo );
|
2775
|
+
}
|
2776
|
+
|
2777
|
+
}
|
2778
|
+
|
2779
|
+
var backgroundImageURL = currentBackground.style.backgroundImage || '';
|
2780
|
+
|
2781
|
+
// Restart GIFs (doesn't work in Firefox)
|
2782
|
+
if( /\.gif/i.test( backgroundImageURL ) ) {
|
2783
|
+
currentBackground.style.backgroundImage = '';
|
2784
|
+
window.getComputedStyle( currentBackground ).opacity;
|
2785
|
+
currentBackground.style.backgroundImage = backgroundImageURL;
|
2477
2786
|
}
|
2478
2787
|
|
2479
2788
|
// Don't transition between identical backgrounds. This
|
@@ -2530,15 +2839,35 @@
|
|
2530
2839
|
backgroundHeight = parseInt( backgroundSize[1], 10 );
|
2531
2840
|
}
|
2532
2841
|
|
2533
|
-
var slideWidth = dom.background.offsetWidth
|
2534
|
-
|
2535
|
-
|
2842
|
+
var slideWidth = dom.background.offsetWidth,
|
2843
|
+
horizontalSlideCount = horizontalSlides.length,
|
2844
|
+
horizontalOffsetMultiplier,
|
2845
|
+
horizontalOffset;
|
2846
|
+
|
2847
|
+
if( typeof config.parallaxBackgroundHorizontal === 'number' ) {
|
2848
|
+
horizontalOffsetMultiplier = config.parallaxBackgroundHorizontal;
|
2849
|
+
}
|
2850
|
+
else {
|
2851
|
+
horizontalOffsetMultiplier = horizontalSlideCount > 1 ? ( backgroundWidth - slideWidth ) / ( horizontalSlideCount-1 ) : 0;
|
2852
|
+
}
|
2853
|
+
|
2854
|
+
horizontalOffset = horizontalOffsetMultiplier * indexh * -1;
|
2855
|
+
|
2856
|
+
var slideHeight = dom.background.offsetHeight,
|
2857
|
+
verticalSlideCount = verticalSlides.length,
|
2858
|
+
verticalOffsetMultiplier,
|
2859
|
+
verticalOffset;
|
2860
|
+
|
2861
|
+
if( typeof config.parallaxBackgroundVertical === 'number' ) {
|
2862
|
+
verticalOffsetMultiplier = config.parallaxBackgroundVertical;
|
2863
|
+
}
|
2864
|
+
else {
|
2865
|
+
verticalOffsetMultiplier = ( backgroundHeight - slideHeight ) / ( verticalSlideCount-1 );
|
2866
|
+
}
|
2536
2867
|
|
2537
|
-
|
2538
|
-
var verticalSlideCount = verticalSlides.length;
|
2539
|
-
var verticalOffset = verticalSlideCount > 1 ? -( backgroundHeight - slideHeight ) / ( verticalSlideCount-1 ) * indexv : 0;
|
2868
|
+
verticalOffset = verticalSlideCount > 0 ? verticalOffsetMultiplier * indexv * 1 : 0;
|
2540
2869
|
|
2541
|
-
dom.background.style.backgroundPosition = horizontalOffset + 'px ' + verticalOffset + 'px';
|
2870
|
+
dom.background.style.backgroundPosition = horizontalOffset + 'px ' + -verticalOffset + 'px';
|
2542
2871
|
|
2543
2872
|
}
|
2544
2873
|
|
@@ -2555,7 +2884,7 @@
|
|
2555
2884
|
slide.style.display = 'block';
|
2556
2885
|
|
2557
2886
|
// Media elements with data-src attributes
|
2558
|
-
toArray( slide.querySelectorAll( 'img[data-src], video[data-src], audio[data-src]
|
2887
|
+
toArray( slide.querySelectorAll( 'img[data-src], video[data-src], audio[data-src]' ) ).forEach( function( element ) {
|
2559
2888
|
element.setAttribute( 'src', element.getAttribute( 'data-src' ) );
|
2560
2889
|
element.removeAttribute( 'data-src' );
|
2561
2890
|
} );
|
@@ -2590,6 +2919,8 @@
|
|
2590
2919
|
|
2591
2920
|
var backgroundImage = slide.getAttribute( 'data-background-image' ),
|
2592
2921
|
backgroundVideo = slide.getAttribute( 'data-background-video' ),
|
2922
|
+
backgroundVideoLoop = slide.hasAttribute( 'data-background-video-loop' ),
|
2923
|
+
backgroundVideoMuted = slide.hasAttribute( 'data-background-video-muted' ),
|
2593
2924
|
backgroundIframe = slide.getAttribute( 'data-background-iframe' );
|
2594
2925
|
|
2595
2926
|
// Images
|
@@ -2600,6 +2931,14 @@
|
|
2600
2931
|
else if ( backgroundVideo && !isSpeakerNotes() ) {
|
2601
2932
|
var video = document.createElement( 'video' );
|
2602
2933
|
|
2934
|
+
if( backgroundVideoLoop ) {
|
2935
|
+
video.setAttribute( 'loop', '' );
|
2936
|
+
}
|
2937
|
+
|
2938
|
+
if( backgroundVideoMuted ) {
|
2939
|
+
video.muted = true;
|
2940
|
+
}
|
2941
|
+
|
2603
2942
|
// Support comma separated lists of video sources
|
2604
2943
|
backgroundVideo.split( ',' ).forEach( function( source ) {
|
2605
2944
|
video.innerHTML += '<source src="'+ source +'">';
|
@@ -2608,7 +2947,7 @@
|
|
2608
2947
|
background.appendChild( video );
|
2609
2948
|
}
|
2610
2949
|
// Iframes
|
2611
|
-
else if
|
2950
|
+
else if( backgroundIframe ) {
|
2612
2951
|
var iframe = document.createElement( 'iframe' );
|
2613
2952
|
iframe.setAttribute( 'src', backgroundIframe );
|
2614
2953
|
iframe.style.width = '100%';
|
@@ -2697,21 +3036,22 @@
|
|
2697
3036
|
*/
|
2698
3037
|
function formatEmbeddedContent() {
|
2699
3038
|
|
3039
|
+
var _appendParamToIframeSource = function( sourceAttribute, sourceURL, param ) {
|
3040
|
+
toArray( dom.slides.querySelectorAll( 'iframe['+ sourceAttribute +'*="'+ sourceURL +'"]' ) ).forEach( function( el ) {
|
3041
|
+
var src = el.getAttribute( sourceAttribute );
|
3042
|
+
if( src && src.indexOf( param ) === -1 ) {
|
3043
|
+
el.setAttribute( sourceAttribute, src + ( !/\?/.test( src ) ? '?' : '&' ) + param );
|
3044
|
+
}
|
3045
|
+
});
|
3046
|
+
};
|
3047
|
+
|
2700
3048
|
// YouTube frames must include "?enablejsapi=1"
|
2701
|
-
|
2702
|
-
|
2703
|
-
if( !/enablejsapi\=1/gi.test( src ) ) {
|
2704
|
-
el.setAttribute( 'src', src + ( !/\?/.test( src ) ? '?' : '&' ) + 'enablejsapi=1' );
|
2705
|
-
}
|
2706
|
-
});
|
3049
|
+
_appendParamToIframeSource( 'src', 'youtube.com/embed/', 'enablejsapi=1' );
|
3050
|
+
_appendParamToIframeSource( 'data-src', 'youtube.com/embed/', 'enablejsapi=1' );
|
2707
3051
|
|
2708
3052
|
// Vimeo frames must include "?api=1"
|
2709
|
-
|
2710
|
-
|
2711
|
-
if( !/api\=1/gi.test( src ) ) {
|
2712
|
-
el.setAttribute( 'src', src + ( !/\?/.test( src ) ? '?' : '&' ) + 'api=1' );
|
2713
|
-
}
|
2714
|
-
});
|
3053
|
+
_appendParamToIframeSource( 'src', 'player.vimeo.com/', 'api=1' );
|
3054
|
+
_appendParamToIframeSource( 'data-src', 'player.vimeo.com/', 'api=1' );
|
2715
3055
|
|
2716
3056
|
}
|
2717
3057
|
|
@@ -2722,31 +3062,56 @@
|
|
2722
3062
|
function startEmbeddedContent( slide ) {
|
2723
3063
|
|
2724
3064
|
if( slide && !isSpeakerNotes() ) {
|
3065
|
+
// Restart GIFs
|
3066
|
+
toArray( slide.querySelectorAll( 'img[src$=".gif"]' ) ).forEach( function( el ) {
|
3067
|
+
// Setting the same unchanged source like this was confirmed
|
3068
|
+
// to work in Chrome, FF & Safari
|
3069
|
+
el.setAttribute( 'src', el.getAttribute( 'src' ) );
|
3070
|
+
} );
|
3071
|
+
|
2725
3072
|
// HTML5 media elements
|
2726
3073
|
toArray( slide.querySelectorAll( 'video, audio' ) ).forEach( function( el ) {
|
2727
|
-
if( el.hasAttribute( 'data-autoplay' ) ) {
|
3074
|
+
if( el.hasAttribute( 'data-autoplay' ) && typeof el.play === 'function' ) {
|
2728
3075
|
el.play();
|
2729
3076
|
}
|
2730
3077
|
} );
|
2731
3078
|
|
2732
|
-
//
|
2733
|
-
toArray( slide.querySelectorAll( 'iframe' ) ).forEach( function( el ) {
|
2734
|
-
|
2735
|
-
});
|
3079
|
+
// Normal iframes
|
3080
|
+
toArray( slide.querySelectorAll( 'iframe[src]' ) ).forEach( function( el ) {
|
3081
|
+
startEmbeddedIframe( { target: el } );
|
3082
|
+
} );
|
2736
3083
|
|
2737
|
-
//
|
2738
|
-
toArray( slide.querySelectorAll( 'iframe[src
|
2739
|
-
if( el.
|
2740
|
-
el.
|
3084
|
+
// Lazy loading iframes
|
3085
|
+
toArray( slide.querySelectorAll( 'iframe[data-src]' ) ).forEach( function( el ) {
|
3086
|
+
if( el.getAttribute( 'src' ) !== el.getAttribute( 'data-src' ) ) {
|
3087
|
+
el.removeEventListener( 'load', startEmbeddedIframe ); // remove first to avoid dupes
|
3088
|
+
el.addEventListener( 'load', startEmbeddedIframe );
|
3089
|
+
el.setAttribute( 'src', el.getAttribute( 'data-src' ) );
|
2741
3090
|
}
|
2742
|
-
});
|
3091
|
+
} );
|
3092
|
+
}
|
2743
3093
|
|
2744
|
-
|
2745
|
-
|
2746
|
-
|
2747
|
-
|
2748
|
-
|
2749
|
-
|
3094
|
+
}
|
3095
|
+
|
3096
|
+
/**
|
3097
|
+
* "Starts" the content of an embedded iframe using the
|
3098
|
+
* postmessage API.
|
3099
|
+
*/
|
3100
|
+
function startEmbeddedIframe( event ) {
|
3101
|
+
|
3102
|
+
var iframe = event.target;
|
3103
|
+
|
3104
|
+
// YouTube postMessage API
|
3105
|
+
if( /youtube\.com\/embed\//.test( iframe.getAttribute( 'src' ) ) && iframe.hasAttribute( 'data-autoplay' ) ) {
|
3106
|
+
iframe.contentWindow.postMessage( '{"event":"command","func":"playVideo","args":""}', '*' );
|
3107
|
+
}
|
3108
|
+
// Vimeo postMessage API
|
3109
|
+
else if( /player\.vimeo\.com\//.test( iframe.getAttribute( 'src' ) ) && iframe.hasAttribute( 'data-autoplay' ) ) {
|
3110
|
+
iframe.contentWindow.postMessage( '{"method":"play"}', '*' );
|
3111
|
+
}
|
3112
|
+
// Generic postMessage API
|
3113
|
+
else {
|
3114
|
+
iframe.contentWindow.postMessage( 'slide:start', '*' );
|
2750
3115
|
}
|
2751
3116
|
|
2752
3117
|
}
|
@@ -2760,43 +3125,51 @@
|
|
2760
3125
|
if( slide && slide.parentNode ) {
|
2761
3126
|
// HTML5 media elements
|
2762
3127
|
toArray( slide.querySelectorAll( 'video, audio' ) ).forEach( function( el ) {
|
2763
|
-
if( !el.hasAttribute( 'data-ignore' ) ) {
|
3128
|
+
if( !el.hasAttribute( 'data-ignore' ) && typeof el.pause === 'function' ) {
|
2764
3129
|
el.pause();
|
2765
3130
|
}
|
2766
3131
|
} );
|
2767
3132
|
|
2768
|
-
//
|
3133
|
+
// Generic postMessage API for non-lazy loaded iframes
|
2769
3134
|
toArray( slide.querySelectorAll( 'iframe' ) ).forEach( function( el ) {
|
2770
3135
|
el.contentWindow.postMessage( 'slide:stop', '*' );
|
3136
|
+
el.removeEventListener( 'load', startEmbeddedIframe );
|
2771
3137
|
});
|
2772
3138
|
|
2773
|
-
// YouTube
|
3139
|
+
// YouTube postMessage API
|
2774
3140
|
toArray( slide.querySelectorAll( 'iframe[src*="youtube.com/embed/"]' ) ).forEach( function( el ) {
|
2775
3141
|
if( !el.hasAttribute( 'data-ignore' ) && typeof el.contentWindow.postMessage === 'function' ) {
|
2776
3142
|
el.contentWindow.postMessage( '{"event":"command","func":"pauseVideo","args":""}', '*' );
|
2777
3143
|
}
|
2778
3144
|
});
|
2779
3145
|
|
2780
|
-
// Vimeo
|
3146
|
+
// Vimeo postMessage API
|
2781
3147
|
toArray( slide.querySelectorAll( 'iframe[src*="player.vimeo.com/"]' ) ).forEach( function( el ) {
|
2782
3148
|
if( !el.hasAttribute( 'data-ignore' ) && typeof el.contentWindow.postMessage === 'function' ) {
|
2783
3149
|
el.contentWindow.postMessage( '{"method":"pause"}', '*' );
|
2784
3150
|
}
|
2785
3151
|
});
|
3152
|
+
|
3153
|
+
// Lazy loading iframes
|
3154
|
+
toArray( slide.querySelectorAll( 'iframe[data-src]' ) ).forEach( function( el ) {
|
3155
|
+
// Only removing the src doesn't actually unload the frame
|
3156
|
+
// in all browsers (Firefox) so we set it to blank first
|
3157
|
+
el.setAttribute( 'src', 'about:blank' );
|
3158
|
+
el.removeAttribute( 'src' );
|
3159
|
+
} );
|
2786
3160
|
}
|
2787
3161
|
|
2788
3162
|
}
|
2789
3163
|
|
2790
3164
|
/**
|
2791
|
-
* Returns
|
2792
|
-
*
|
3165
|
+
* Returns the number of past slides. This can be used as a global
|
3166
|
+
* flattened index for slides.
|
2793
3167
|
*/
|
2794
|
-
function
|
3168
|
+
function getSlidePastCount() {
|
2795
3169
|
|
2796
3170
|
var horizontalSlides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) );
|
2797
3171
|
|
2798
|
-
// The number of past
|
2799
|
-
var totalCount = getTotalSlides();
|
3172
|
+
// The number of past slides
|
2800
3173
|
var pastCount = 0;
|
2801
3174
|
|
2802
3175
|
// Step through all slides and count the past ones
|
@@ -2828,6 +3201,20 @@
|
|
2828
3201
|
|
2829
3202
|
}
|
2830
3203
|
|
3204
|
+
return pastCount;
|
3205
|
+
|
3206
|
+
}
|
3207
|
+
|
3208
|
+
/**
|
3209
|
+
* Returns a value ranging from 0-1 that represents
|
3210
|
+
* how far into the presentation we have navigated.
|
3211
|
+
*/
|
3212
|
+
function getProgress() {
|
3213
|
+
|
3214
|
+
// The number of past and total slides
|
3215
|
+
var totalCount = getTotalSlides();
|
3216
|
+
var pastCount = getSlidePastCount();
|
3217
|
+
|
2831
3218
|
if( currentSlide ) {
|
2832
3219
|
|
2833
3220
|
var allFragments = currentSlide.querySelectorAll( '.fragment' );
|
@@ -2880,7 +3267,7 @@
|
|
2880
3267
|
// Ensure the named link is a valid HTML ID attribute
|
2881
3268
|
if( /^[a-zA-Z][\w:.-]*$/.test( name ) ) {
|
2882
3269
|
// Find the slide with the specified ID
|
2883
|
-
element = document.
|
3270
|
+
element = document.getElementById( name );
|
2884
3271
|
}
|
2885
3272
|
|
2886
3273
|
if( element ) {
|
@@ -2929,7 +3316,6 @@
|
|
2929
3316
|
// Attempt to create a named link based on the slide's ID
|
2930
3317
|
var id = currentSlide.getAttribute( 'id' );
|
2931
3318
|
if( id ) {
|
2932
|
-
id = id.toLowerCase();
|
2933
3319
|
id = id.replace( /[^a-zA-Z0-9\-\_\:\.]/g, '' );
|
2934
3320
|
}
|
2935
3321
|
|
@@ -3061,6 +3447,32 @@
|
|
3061
3447
|
|
3062
3448
|
}
|
3063
3449
|
|
3450
|
+
/**
|
3451
|
+
* Retrieves the speaker notes from a slide. Notes can be
|
3452
|
+
* defined in two ways:
|
3453
|
+
* 1. As a data-notes attribute on the slide <section>
|
3454
|
+
* 2. As an <aside class="notes"> inside of the slide
|
3455
|
+
*/
|
3456
|
+
function getSlideNotes( slide ) {
|
3457
|
+
|
3458
|
+
// Default to the current slide
|
3459
|
+
slide = slide || currentSlide;
|
3460
|
+
|
3461
|
+
// Notes can be specified via the data-notes attribute...
|
3462
|
+
if( slide.hasAttribute( 'data-notes' ) ) {
|
3463
|
+
return slide.getAttribute( 'data-notes' );
|
3464
|
+
}
|
3465
|
+
|
3466
|
+
// ... or using an <aside class="notes"> element
|
3467
|
+
var notesElement = slide.querySelector( 'aside.notes' );
|
3468
|
+
if( notesElement ) {
|
3469
|
+
return notesElement.innerHTML;
|
3470
|
+
}
|
3471
|
+
|
3472
|
+
return null;
|
3473
|
+
|
3474
|
+
}
|
3475
|
+
|
3064
3476
|
/**
|
3065
3477
|
* Retrieves the current state of the presentation as
|
3066
3478
|
* an object. This state can then be restored at any
|
@@ -3312,14 +3724,17 @@
|
|
3312
3724
|
|
3313
3725
|
// If there are media elements with data-autoplay,
|
3314
3726
|
// automatically set the autoSlide duration to the
|
3315
|
-
// length of that media
|
3316
|
-
|
3317
|
-
|
3318
|
-
|
3319
|
-
|
3727
|
+
// length of that media. Not applicable if the slide
|
3728
|
+
// is divided up into fragments.
|
3729
|
+
if( currentSlide.querySelectorAll( '.fragment' ).length === 0 ) {
|
3730
|
+
toArray( currentSlide.querySelectorAll( 'video, audio' ) ).forEach( function( el ) {
|
3731
|
+
if( el.hasAttribute( 'data-autoplay' ) ) {
|
3732
|
+
if( autoSlide && el.duration * 1000 > autoSlide ) {
|
3733
|
+
autoSlide = ( el.duration * 1000 ) + 1000;
|
3734
|
+
}
|
3320
3735
|
}
|
3321
|
-
}
|
3322
|
-
}
|
3736
|
+
} );
|
3737
|
+
}
|
3323
3738
|
|
3324
3739
|
// Cue the next auto-slide if:
|
3325
3740
|
// - There is an autoSlide value
|
@@ -3328,7 +3743,10 @@
|
|
3328
3743
|
// - The overview isn't active
|
3329
3744
|
// - The presentation isn't over
|
3330
3745
|
if( autoSlide && !autoSlidePaused && !isPaused() && !isOverview() && ( !Reveal.isLastSlide() || availableFragments().next || config.loop === true ) ) {
|
3331
|
-
autoSlideTimeout = setTimeout(
|
3746
|
+
autoSlideTimeout = setTimeout( function() {
|
3747
|
+
typeof config.autoSlideMethod === 'function' ? config.autoSlideMethod() : navigateNext();
|
3748
|
+
cueAutoSlide();
|
3749
|
+
}, autoSlide );
|
3332
3750
|
autoSlideStartTime = Date.now();
|
3333
3751
|
}
|
3334
3752
|
|
@@ -3474,9 +3892,20 @@
|
|
3474
3892
|
}
|
3475
3893
|
}
|
3476
3894
|
|
3477
|
-
|
3478
|
-
|
3479
|
-
|
3895
|
+
}
|
3896
|
+
|
3897
|
+
/**
|
3898
|
+
* Checks if the target element prevents the triggering of
|
3899
|
+
* swipe navigation.
|
3900
|
+
*/
|
3901
|
+
function isSwipePrevented( target ) {
|
3902
|
+
|
3903
|
+
while( target && typeof target.hasAttribute === 'function' ) {
|
3904
|
+
if( target.hasAttribute( 'data-prevent-swipe' ) ) return true;
|
3905
|
+
target = target.parentNode;
|
3906
|
+
}
|
3907
|
+
|
3908
|
+
return false;
|
3480
3909
|
|
3481
3910
|
}
|
3482
3911
|
|
@@ -3539,8 +3968,20 @@
|
|
3539
3968
|
// keyboard modifier key is present
|
3540
3969
|
if( activeElementIsCE || activeElementIsInput || (event.shiftKey && event.keyCode !== 32) || event.altKey || event.ctrlKey || event.metaKey ) return;
|
3541
3970
|
|
3542
|
-
// While paused only allow
|
3543
|
-
|
3971
|
+
// While paused only allow resume keyboard events; 'b', '.''
|
3972
|
+
var resumeKeyCodes = [66,190,191];
|
3973
|
+
var key;
|
3974
|
+
|
3975
|
+
// Custom key bindings for togglePause should be able to resume
|
3976
|
+
if( typeof config.keyboard === 'object' ) {
|
3977
|
+
for( key in config.keyboard ) {
|
3978
|
+
if( config.keyboard[key] === 'togglePause' ) {
|
3979
|
+
resumeKeyCodes.push( parseInt( key, 10 ) );
|
3980
|
+
}
|
3981
|
+
}
|
3982
|
+
}
|
3983
|
+
|
3984
|
+
if( isPaused() && resumeKeyCodes.indexOf( event.keyCode ) === -1 ) {
|
3544
3985
|
return false;
|
3545
3986
|
}
|
3546
3987
|
|
@@ -3549,7 +3990,7 @@
|
|
3549
3990
|
// 1. User defined key bindings
|
3550
3991
|
if( typeof config.keyboard === 'object' ) {
|
3551
3992
|
|
3552
|
-
for(
|
3993
|
+
for( key in config.keyboard ) {
|
3553
3994
|
|
3554
3995
|
// Check if this binding matches the pressed key
|
3555
3996
|
if( parseInt( key, 10 ) === event.keyCode ) {
|
@@ -3641,6 +4082,8 @@
|
|
3641
4082
|
*/
|
3642
4083
|
function onTouchStart( event ) {
|
3643
4084
|
|
4085
|
+
if( isSwipePrevented( event.target ) ) return true;
|
4086
|
+
|
3644
4087
|
touch.startX = event.touches[0].clientX;
|
3645
4088
|
touch.startY = event.touches[0].clientY;
|
3646
4089
|
touch.startCount = event.touches.length;
|
@@ -3664,6 +4107,8 @@
|
|
3664
4107
|
*/
|
3665
4108
|
function onTouchMove( event ) {
|
3666
4109
|
|
4110
|
+
if( isSwipePrevented( event.target ) ) return true;
|
4111
|
+
|
3667
4112
|
// Each touch should only trigger one action
|
3668
4113
|
if( !touch.captured ) {
|
3669
4114
|
onUserInput( event );
|
@@ -3740,7 +4185,7 @@
|
|
3740
4185
|
}
|
3741
4186
|
// There's a bug with swiping on some Android devices unless
|
3742
4187
|
// the default action is always prevented
|
3743
|
-
else if(
|
4188
|
+
else if( UA.match( /android/gi ) ) {
|
3744
4189
|
event.preventDefault();
|
3745
4190
|
}
|
3746
4191
|
|
@@ -3828,6 +4273,10 @@
|
|
3828
4273
|
var slidesTotal = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ).length;
|
3829
4274
|
var slideIndex = Math.floor( ( event.clientX / dom.wrapper.offsetWidth ) * slidesTotal );
|
3830
4275
|
|
4276
|
+
if( config.rtl ) {
|
4277
|
+
slideIndex = slidesTotal - slideIndex;
|
4278
|
+
}
|
4279
|
+
|
3831
4280
|
slide( slideIndex );
|
3832
4281
|
|
3833
4282
|
}
|
@@ -3872,7 +4321,10 @@
|
|
3872
4321
|
// If, after clicking a link or similar and we're coming back,
|
3873
4322
|
// focus the document.body to ensure we can use keyboard shortcuts
|
3874
4323
|
if( isHidden === false && document.activeElement !== document.body ) {
|
3875
|
-
|
4324
|
+
// Not all elements support .blur() - SVGs among them.
|
4325
|
+
if( typeof document.activeElement.blur === 'function' ) {
|
4326
|
+
document.activeElement.blur();
|
4327
|
+
}
|
3876
4328
|
document.body.focus();
|
3877
4329
|
}
|
3878
4330
|
|
@@ -3966,8 +4418,9 @@
|
|
3966
4418
|
function Playback( container, progressCheck ) {
|
3967
4419
|
|
3968
4420
|
// Cosmetics
|
3969
|
-
this.diameter =
|
3970
|
-
this.
|
4421
|
+
this.diameter = 100;
|
4422
|
+
this.diameter2 = this.diameter/2;
|
4423
|
+
this.thickness = 6;
|
3971
4424
|
|
3972
4425
|
// Flags if we are currently playing
|
3973
4426
|
this.playing = false;
|
@@ -3985,6 +4438,8 @@
|
|
3985
4438
|
this.canvas.className = 'playback';
|
3986
4439
|
this.canvas.width = this.diameter;
|
3987
4440
|
this.canvas.height = this.diameter;
|
4441
|
+
this.canvas.style.width = this.diameter2 + 'px';
|
4442
|
+
this.canvas.style.height = this.diameter2 + 'px';
|
3988
4443
|
this.context = this.canvas.getContext( '2d' );
|
3989
4444
|
|
3990
4445
|
this.container.appendChild( this.canvas );
|
@@ -4035,10 +4490,10 @@
|
|
4035
4490
|
Playback.prototype.render = function() {
|
4036
4491
|
|
4037
4492
|
var progress = this.playing ? this.progress : 0,
|
4038
|
-
radius = ( this.
|
4039
|
-
x = this.
|
4040
|
-
y = this.
|
4041
|
-
iconSize =
|
4493
|
+
radius = ( this.diameter2 ) - this.thickness,
|
4494
|
+
x = this.diameter2,
|
4495
|
+
y = this.diameter2,
|
4496
|
+
iconSize = 28;
|
4042
4497
|
|
4043
4498
|
// Ease towards 1
|
4044
4499
|
this.progressOffset += ( 1 - this.progressOffset ) * 0.1;
|
@@ -4051,7 +4506,7 @@
|
|
4051
4506
|
|
4052
4507
|
// Solid background color
|
4053
4508
|
this.context.beginPath();
|
4054
|
-
this.context.arc( x, y, radius +
|
4509
|
+
this.context.arc( x, y, radius + 4, 0, Math.PI * 2, false );
|
4055
4510
|
this.context.fillStyle = 'rgba( 0, 0, 0, 0.4 )';
|
4056
4511
|
this.context.fill();
|
4057
4512
|
|
@@ -4076,14 +4531,14 @@
|
|
4076
4531
|
// Draw play/pause icons
|
4077
4532
|
if( this.playing ) {
|
4078
4533
|
this.context.fillStyle = '#fff';
|
4079
|
-
this.context.fillRect( 0, 0, iconSize / 2 -
|
4080
|
-
this.context.fillRect( iconSize / 2 +
|
4534
|
+
this.context.fillRect( 0, 0, iconSize / 2 - 4, iconSize );
|
4535
|
+
this.context.fillRect( iconSize / 2 + 4, 0, iconSize / 2 - 4, iconSize );
|
4081
4536
|
}
|
4082
4537
|
else {
|
4083
4538
|
this.context.beginPath();
|
4084
|
-
this.context.translate(
|
4539
|
+
this.context.translate( 4, 0 );
|
4085
4540
|
this.context.moveTo( 0, 0 );
|
4086
|
-
this.context.lineTo( iconSize -
|
4541
|
+
this.context.lineTo( iconSize - 4, iconSize / 2 );
|
4087
4542
|
this.context.lineTo( 0, iconSize );
|
4088
4543
|
this.context.fillStyle = '#fff';
|
4089
4544
|
this.context.fill();
|
@@ -4118,6 +4573,8 @@
|
|
4118
4573
|
|
4119
4574
|
|
4120
4575
|
Reveal = {
|
4576
|
+
VERSION: VERSION,
|
4577
|
+
|
4121
4578
|
initialize: initialize,
|
4122
4579
|
configure: configure,
|
4123
4580
|
sync: sync,
|
@@ -4148,6 +4605,9 @@
|
|
4148
4605
|
// Forces an update in slide layout
|
4149
4606
|
layout: layout,
|
4150
4607
|
|
4608
|
+
// Randomizes the order of slides
|
4609
|
+
shuffle: shuffle,
|
4610
|
+
|
4151
4611
|
// Returns an object with the available routes as booleans (left/right/top/bottom)
|
4152
4612
|
availableRoutes: availableRoutes,
|
4153
4613
|
|
@@ -4190,6 +4650,9 @@
|
|
4190
4650
|
// Returns the slide background element at the specified index
|
4191
4651
|
getSlideBackground: getSlideBackground,
|
4192
4652
|
|
4653
|
+
// Returns the speaker notes string for a slide, or null
|
4654
|
+
getSlideNotes: getSlideNotes,
|
4655
|
+
|
4193
4656
|
// Returns the previous slide element, may be null
|
4194
4657
|
getPreviousSlide: function() {
|
4195
4658
|
return previousSlide;
|
@@ -4268,6 +4731,11 @@
|
|
4268
4731
|
// Programatically triggers a keyboard event
|
4269
4732
|
triggerKey: function( keyCode ) {
|
4270
4733
|
onDocumentKeyDown( { keyCode: keyCode } );
|
4734
|
+
},
|
4735
|
+
|
4736
|
+
// Registers a new shortcut to include in the help overlay
|
4737
|
+
registerKeyboardShortcut: function( key, value ) {
|
4738
|
+
keyboardShortcuts[key] = value;
|
4271
4739
|
}
|
4272
4740
|
};
|
4273
4741
|
|