slide_hero 0.0.10 → 0.0.11
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|