|
@@ -3,7 +3,7 @@
|
|
|
* http://lab.hakim.se/reveal-js
|
|
|
* MIT licensed
|
|
|
*
|
|
|
- * Copyright (C) 2016 Hakim El Hattab, http://hakim.se
|
|
|
+ * Copyright (C) 2017 Hakim El Hattab, http://hakim.se
|
|
|
*/
|
|
|
(function( root, factory ) {
|
|
|
if( typeof define === 'function' && define.amd ) {
|
|
@@ -26,7 +26,7 @@
|
|
|
var Reveal;
|
|
|
|
|
|
// The reveal.js version
|
|
|
- var VERSION = '3.4.1';
|
|
|
+ var VERSION = '3.5.0';
|
|
|
|
|
|
var SLIDES_SELECTOR = '.slides section',
|
|
|
HORIZONTAL_SLIDES_SELECTOR = '.slides>section',
|
|
@@ -58,6 +58,9 @@
|
|
|
// Display the page number of the current slide
|
|
|
slideNumber: false,
|
|
|
|
|
|
+ // Determine which displays to show the slide number on
|
|
|
+ showSlideNumber: 'all',
|
|
|
+
|
|
|
// Push each slide change to the browser history
|
|
|
history: false,
|
|
|
|
|
@@ -102,6 +105,12 @@
|
|
|
// Flags if speaker notes should be visible to all viewers
|
|
|
showNotes: false,
|
|
|
|
|
|
+ // Global override for autolaying embedded media (video/audio/iframe)
|
|
|
+ // - null: Media will only autoplay if data-autoplay is present
|
|
|
+ // - true: All media will autoplay, regardless of individual setting
|
|
|
+ // - false: No media will autoplay, regardless of individual setting
|
|
|
+ autoPlayMedia: null,
|
|
|
+
|
|
|
// Number of milliseconds between automatically proceeding to the
|
|
|
// next slide, disabled when set to 0, this value can be overwritten
|
|
|
// by using a data-autoslide attribute on your slides
|
|
@@ -157,9 +166,19 @@
|
|
|
// to PDF, unlimited by default
|
|
|
pdfMaxPagesPerSlide: Number.POSITIVE_INFINITY,
|
|
|
|
|
|
+ // Offset used to reduce the height of content within exported PDF pages.
|
|
|
+ // This exists to account for environment differences based on how you
|
|
|
+ // print to PDF. CLI printing options, like phantomjs and wkpdf, can end
|
|
|
+ // on precisely the total height of the document whereas in-browser
|
|
|
+ // printing has to end one pixel before.
|
|
|
+ pdfPageHeightOffset: -1,
|
|
|
+
|
|
|
// Number of slides away from the current that are visible
|
|
|
viewDistance: 3,
|
|
|
|
|
|
+ // The display mode that will be used to show slides
|
|
|
+ display: 'block',
|
|
|
+
|
|
|
// Script dependencies to load
|
|
|
dependencies: []
|
|
|
|
|
@@ -605,7 +624,7 @@
|
|
|
slideHeight = slideSize.height;
|
|
|
|
|
|
// Let the browser know what page size we want to print
|
|
|
- injectStyleSheet( '@page{size:'+ pageWidth +'px '+ pageHeight +'px; margin: 0 0 -1px 0;}' );
|
|
|
+ injectStyleSheet( '@page{size:'+ pageWidth +'px '+ pageHeight +'px; margin: 0px;}' );
|
|
|
|
|
|
// Limit the size of certain elements to the dimensions of the slide
|
|
|
injectStyleSheet( '.reveal section>img, .reveal section>video, .reveal section>iframe{max-width: '+ slideWidth +'px; max-height:'+ slideHeight +'px}' );
|
|
@@ -614,6 +633,9 @@
|
|
|
document.body.style.width = pageWidth + 'px';
|
|
|
document.body.style.height = pageHeight + 'px';
|
|
|
|
|
|
+ // Make sure stretch elements fit on slide
|
|
|
+ layoutSlideContents( slideWidth, slideHeight );
|
|
|
+
|
|
|
// Add each slide's index as attributes on itself, we need these
|
|
|
// indices to generate slide numbers below
|
|
|
toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ).forEach( function( hslide, h ) {
|
|
@@ -652,7 +674,7 @@
|
|
|
// so that no page ever flows onto another
|
|
|
var page = document.createElement( 'div' );
|
|
|
page.className = 'pdf-page';
|
|
|
- page.style.height = ( pageHeight * numberOfPages ) + 'px';
|
|
|
+ page.style.height = ( ( pageHeight + config.pdfPageHeightOffset ) * numberOfPages ) + 'px';
|
|
|
slide.parentNode.insertBefore( page, slide );
|
|
|
page.appendChild( slide );
|
|
|
|
|
@@ -695,7 +717,7 @@
|
|
|
}
|
|
|
|
|
|
// Inject slide numbers if `slideNumbers` are enabled
|
|
|
- if( config.slideNumber ) {
|
|
|
+ if( config.slideNumber && /all|print/i.test( config.showSlideNumber ) ) {
|
|
|
var slideNumberH = parseInt( slide.getAttribute( 'data-index-h' ), 10 ) + 1,
|
|
|
slideNumberV = parseInt( slide.getAttribute( 'data-index-v' ), 10 ) + 1;
|
|
|
|
|
@@ -859,7 +881,7 @@
|
|
|
|
|
|
if( data.background ) {
|
|
|
// Auto-wrap image urls in url(...)
|
|
|
- if( /^(http|file|\/\/)/gi.test( data.background ) || /\.(svg|png|jpg|jpeg|gif|bmp)$/gi.test( data.background ) ) {
|
|
|
+ if( /^(http|file|\/\/)/gi.test( data.background ) || /\.(svg|png|jpg|jpeg|gif|bmp)([?#]|$)/gi.test( data.background ) ) {
|
|
|
slide.setAttribute( 'data-background-image', data.background );
|
|
|
}
|
|
|
else {
|
|
@@ -884,6 +906,7 @@
|
|
|
|
|
|
// Additional and optional background properties
|
|
|
if( data.backgroundSize ) element.style.backgroundSize = data.backgroundSize;
|
|
|
+ if( data.backgroundSize ) element.setAttribute( 'data-background-size', data.backgroundSize );
|
|
|
if( data.backgroundColor ) element.style.backgroundColor = data.backgroundColor;
|
|
|
if( data.backgroundRepeat ) element.style.backgroundRepeat = data.backgroundRepeat;
|
|
|
if( data.backgroundPosition ) element.style.backgroundPosition = data.backgroundPosition;
|
|
@@ -977,7 +1000,6 @@
|
|
|
|
|
|
dom.controls.style.display = config.controls ? 'block' : 'none';
|
|
|
dom.progress.style.display = config.progress ? 'block' : 'none';
|
|
|
- dom.slideNumber.style.display = config.slideNumber && !isPrintingPDF() ? 'block' : 'none';
|
|
|
|
|
|
if( config.shuffle ) {
|
|
|
shuffle();
|
|
@@ -1030,10 +1052,11 @@
|
|
|
// Iframe link previews
|
|
|
if( config.previewLinks ) {
|
|
|
enablePreviewLinks();
|
|
|
+ disablePreviewLinks( '[data-preview-link=false]' );
|
|
|
}
|
|
|
else {
|
|
|
disablePreviewLinks();
|
|
|
- enablePreviewLinks( '[data-preview-link]' );
|
|
|
+ enablePreviewLinks( '[data-preview-link]:not([data-preview-link=false])' );
|
|
|
}
|
|
|
|
|
|
// Remove existing auto-slide controls
|
|
@@ -1060,6 +1083,19 @@
|
|
|
} );
|
|
|
}
|
|
|
|
|
|
+ // Slide numbers
|
|
|
+ var slideNumberDisplay = 'none';
|
|
|
+ if( config.slideNumber && !isPrintingPDF() ) {
|
|
|
+ if( config.showSlideNumber === 'all' ) {
|
|
|
+ slideNumberDisplay = 'block';
|
|
|
+ }
|
|
|
+ else if( config.showSlideNumber === 'speaker' && isSpeakerNotes() ) {
|
|
|
+ slideNumberDisplay = 'block';
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ dom.slideNumber.style.display = slideNumberDisplay;
|
|
|
+
|
|
|
sync();
|
|
|
|
|
|
}
|
|
@@ -1225,7 +1261,7 @@
|
|
|
if( value === 'null' ) return null;
|
|
|
else if( value === 'true' ) return true;
|
|
|
else if( value === 'false' ) return false;
|
|
|
- else if( value.match( /^\d+$/ ) ) return parseFloat( value );
|
|
|
+ else if( value.match( /^[\d\.]+$/ ) ) return parseFloat( value );
|
|
|
}
|
|
|
|
|
|
return value;
|
|
@@ -1426,7 +1462,7 @@
|
|
|
* target element.
|
|
|
*
|
|
|
* remaining height = [ configured parent height ] - [ current parent height ]
|
|
|
- *
|
|
|
+ *
|
|
|
* @param {HTMLElement} element
|
|
|
* @param {number} [height]
|
|
|
*/
|
|
@@ -1569,9 +1605,9 @@
|
|
|
/**
|
|
|
* Unbind preview frame links.
|
|
|
*/
|
|
|
- function disablePreviewLinks() {
|
|
|
+ function disablePreviewLinks( selector ) {
|
|
|
|
|
|
- var anchors = toArray( document.querySelectorAll( 'a' ) );
|
|
|
+ var anchors = toArray( document.querySelectorAll( selector ? selector : 'a' ) );
|
|
|
|
|
|
anchors.forEach( function( element ) {
|
|
|
if( /^(http|www)/gi.test( element.getAttribute( 'href' ) ) ) {
|
|
@@ -1628,6 +1664,28 @@
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Open or close help overlay window.
|
|
|
+ *
|
|
|
+ * @param {Boolean} [override] Flag which overrides the
|
|
|
+ * toggle logic and forcibly sets the desired state. True means
|
|
|
+ * help is open, false means it's closed.
|
|
|
+ */
|
|
|
+ function toggleHelp( override ){
|
|
|
+
|
|
|
+ if( typeof override === 'boolean' ) {
|
|
|
+ override ? showHelp() : closeOverlay();
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ if( dom.overlay ) {
|
|
|
+ closeOverlay();
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ showHelp();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Opens an overlay window with help material.
|
|
|
*/
|
|
@@ -1770,6 +1828,10 @@
|
|
|
updateProgress();
|
|
|
updateParallax();
|
|
|
|
|
|
+ if( isOverview() ) {
|
|
|
+ updateOverview();
|
|
|
+ }
|
|
|
+
|
|
|
}
|
|
|
|
|
|
}
|
|
@@ -1985,11 +2047,14 @@
|
|
|
*/
|
|
|
function updateOverview() {
|
|
|
|
|
|
+ var vmin = Math.min( window.innerWidth, window.innerHeight );
|
|
|
+ var scale = Math.max( vmin / 5, 150 ) / vmin;
|
|
|
+
|
|
|
transformSlides( {
|
|
|
overview: [
|
|
|
+ 'scale('+ scale +')',
|
|
|
'translateX('+ ( -indexh * overviewSlideWidth ) +'px)',
|
|
|
- 'translateY('+ ( -indexv * overviewSlideHeight ) +'px)',
|
|
|
- 'translateZ('+ ( window.innerWidth < 400 ? -1000 : -2500 ) +'px)'
|
|
|
+ 'translateY('+ ( -indexv * overviewSlideHeight ) +'px)'
|
|
|
].join( ' ' )
|
|
|
} );
|
|
|
|
|
@@ -2395,13 +2460,20 @@
|
|
|
|
|
|
updateControls();
|
|
|
updateProgress();
|
|
|
- updateBackground( true );
|
|
|
updateSlideNumber();
|
|
|
updateSlidesVisibility();
|
|
|
+ updateBackground( true );
|
|
|
updateNotes();
|
|
|
|
|
|
formatEmbeddedContent();
|
|
|
- startEmbeddedContent( currentSlide );
|
|
|
+
|
|
|
+ // Start or stop embedded content depending on global config
|
|
|
+ if( config.autoPlayMedia === false ) {
|
|
|
+ stopEmbeddedContent( currentSlide );
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ startEmbeddedContent( currentSlide );
|
|
|
+ }
|
|
|
|
|
|
if( isOverview() ) {
|
|
|
layoutOverview();
|
|
@@ -2873,34 +2945,17 @@
|
|
|
|
|
|
} );
|
|
|
|
|
|
- // Stop any currently playing video background
|
|
|
+ // Stop content inside of previous backgrounds
|
|
|
if( previousBackground ) {
|
|
|
|
|
|
- var previousVideo = previousBackground.querySelector( 'video' );
|
|
|
- if( previousVideo ) previousVideo.pause();
|
|
|
+ stopEmbeddedContent( previousBackground );
|
|
|
|
|
|
}
|
|
|
|
|
|
+ // Start content in the current background
|
|
|
if( currentBackground ) {
|
|
|
|
|
|
- // Start video playback
|
|
|
- var currentVideo = currentBackground.querySelector( 'video' );
|
|
|
- if( currentVideo ) {
|
|
|
-
|
|
|
- var startVideo = function() {
|
|
|
- currentVideo.currentTime = 0;
|
|
|
- currentVideo.play();
|
|
|
- currentVideo.removeEventListener( 'loadeddata', startVideo );
|
|
|
- };
|
|
|
-
|
|
|
- if( currentVideo.readyState > 1 ) {
|
|
|
- startVideo();
|
|
|
- }
|
|
|
- else {
|
|
|
- currentVideo.addEventListener( 'loadeddata', startVideo );
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
+ startEmbeddedContent( currentBackground );
|
|
|
|
|
|
var backgroundImageURL = currentBackground.style.backgroundImage || '';
|
|
|
|
|
@@ -2999,6 +3054,13 @@
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Called when the given slide is within the configured view
|
|
|
+ * distance. Shows the slide element and loads any content
|
|
|
+ * that is set to load lazily (data-src).
|
|
|
+ *
|
|
|
+ * @param {HTMLElement} slide Slide to show
|
|
|
+ */
|
|
|
/**
|
|
|
* Called when the given slide is within the configured view
|
|
|
* distance. Shows the slide element and loads any content
|
|
@@ -3009,7 +3071,7 @@
|
|
|
function showSlide( slide ) {
|
|
|
|
|
|
// Show the slide element
|
|
|
- slide.style.display = 'block';
|
|
|
+ slide.style.display = config.display;
|
|
|
|
|
|
// Media elements with data-src attributes
|
|
|
toArray( slide.querySelectorAll( 'img[data-src], video[data-src], audio[data-src]' ) ).forEach( function( element ) {
|
|
@@ -3067,6 +3129,15 @@
|
|
|
video.muted = true;
|
|
|
}
|
|
|
|
|
|
+ // Inline video playback works (at least in Mobile Safari) as
|
|
|
+ // long as the video is muted and the `playsinline` attribute is
|
|
|
+ // present
|
|
|
+ if( isMobileDevice ) {
|
|
|
+ video.muted = true;
|
|
|
+ video.autoplay = true;
|
|
|
+ video.setAttribute( 'playsinline', '' );
|
|
|
+ }
|
|
|
+
|
|
|
// Support comma separated lists of video sources
|
|
|
backgroundVideo.split( ',' ).forEach( function( source ) {
|
|
|
video.innerHTML += '<source src="'+ source +'">';
|
|
@@ -3077,15 +3148,28 @@
|
|
|
// Iframes
|
|
|
else if( backgroundIframe ) {
|
|
|
var iframe = document.createElement( 'iframe' );
|
|
|
+ iframe.setAttribute( 'allowfullscreen', '' );
|
|
|
+ iframe.setAttribute( 'mozallowfullscreen', '' );
|
|
|
+ iframe.setAttribute( 'webkitallowfullscreen', '' );
|
|
|
+
|
|
|
+ // Only load autoplaying content when the slide is shown to
|
|
|
+ // avoid having it play in the background
|
|
|
+ if( /autoplay=(1|true|yes)/gi.test( backgroundIframe ) ) {
|
|
|
+ iframe.setAttribute( 'data-src', backgroundIframe );
|
|
|
+ }
|
|
|
+ else {
|
|
|
iframe.setAttribute( 'src', backgroundIframe );
|
|
|
- iframe.style.width = '100%';
|
|
|
- iframe.style.height = '100%';
|
|
|
- iframe.style.maxHeight = '100%';
|
|
|
- iframe.style.maxWidth = '100%';
|
|
|
+ }
|
|
|
+
|
|
|
+ iframe.style.width = '100%';
|
|
|
+ iframe.style.height = '100%';
|
|
|
+ iframe.style.maxHeight = '100%';
|
|
|
+ iframe.style.maxWidth = '100%';
|
|
|
|
|
|
background.appendChild( iframe );
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
}
|
|
|
|
|
|
}
|
|
@@ -3189,11 +3273,12 @@
|
|
|
* Start playback of any embedded content inside of
|
|
|
* the given element.
|
|
|
*
|
|
|
- * @param {HTMLElement} slide
|
|
|
+ * @param {HTMLElement} element
|
|
|
*/
|
|
|
function startEmbeddedContent( element ) {
|
|
|
|
|
|
if( element && !isSpeakerNotes() ) {
|
|
|
+
|
|
|
// Restart GIFs
|
|
|
toArray( element.querySelectorAll( 'img[src$=".gif"]' ) ).forEach( function( el ) {
|
|
|
// Setting the same unchanged source like this was confirmed
|
|
@@ -3207,8 +3292,25 @@
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- if( el.hasAttribute( 'data-autoplay' ) && typeof el.play === 'function' ) {
|
|
|
- el.play();
|
|
|
+ // Prefer an explicit global autoplay setting
|
|
|
+ var autoplay = config.autoPlayMedia;
|
|
|
+
|
|
|
+ // If no global setting is available, fall back on the element's
|
|
|
+ // own autoplay setting
|
|
|
+ if( typeof autoplay !== 'boolean' ) {
|
|
|
+ autoplay = el.hasAttribute( 'data-autoplay' ) || !!closestParent( el, '.slide-background' );
|
|
|
+ }
|
|
|
+
|
|
|
+ if( autoplay && typeof el.play === 'function' ) {
|
|
|
+
|
|
|
+ if( el.readyState > 1 ) {
|
|
|
+ startEmbeddedMedia( { target: el } );
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ el.removeEventListener( 'loadeddata', startEmbeddedMedia ); // remove first to avoid dupes
|
|
|
+ el.addEventListener( 'loadeddata', startEmbeddedMedia );
|
|
|
+ }
|
|
|
+
|
|
|
}
|
|
|
} );
|
|
|
|
|
@@ -3233,15 +3335,36 @@
|
|
|
el.setAttribute( 'src', el.getAttribute( 'data-src' ) );
|
|
|
}
|
|
|
} );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Starts playing an embedded video/audio element after
|
|
|
+ * it has finished loading.
|
|
|
+ *
|
|
|
+ * @param {object} event
|
|
|
+ */
|
|
|
+ function startEmbeddedMedia( event ) {
|
|
|
+
|
|
|
+ var isAttachedToDOM = !!closestParent( event.target, 'html' ),
|
|
|
+ isVisible = !!closestParent( event.target, '.present' );
|
|
|
+
|
|
|
+ if( isAttachedToDOM && isVisible ) {
|
|
|
+ event.target.currentTime = 0;
|
|
|
+ event.target.play();
|
|
|
}
|
|
|
|
|
|
+ event.target.removeEventListener( 'loadeddata', startEmbeddedMedia );
|
|
|
+
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* "Starts" the content of an embedded iframe using the
|
|
|
* postMessage API.
|
|
|
*
|
|
|
- * @param {object} event - postMessage API event
|
|
|
+ * @param {object} event
|
|
|
*/
|
|
|
function startEmbeddedIframe( event ) {
|
|
|
|
|
@@ -3249,17 +3372,33 @@
|
|
|
|
|
|
if( iframe && iframe.contentWindow ) {
|
|
|
|
|
|
- // YouTube postMessage API
|
|
|
- if( /youtube\.com\/embed\//.test( iframe.getAttribute( 'src' ) ) && iframe.hasAttribute( 'data-autoplay' ) ) {
|
|
|
- iframe.contentWindow.postMessage( '{"event":"command","func":"playVideo","args":""}', '*' );
|
|
|
- }
|
|
|
- // Vimeo postMessage API
|
|
|
- else if( /player\.vimeo\.com\//.test( iframe.getAttribute( 'src' ) ) && iframe.hasAttribute( 'data-autoplay' ) ) {
|
|
|
- iframe.contentWindow.postMessage( '{"method":"play"}', '*' );
|
|
|
- }
|
|
|
- // Generic postMessage API
|
|
|
- else {
|
|
|
- iframe.contentWindow.postMessage( 'slide:start', '*' );
|
|
|
+ var isAttachedToDOM = !!closestParent( event.target, 'html' ),
|
|
|
+ isVisible = !!closestParent( event.target, '.present' );
|
|
|
+
|
|
|
+ if( isAttachedToDOM && isVisible ) {
|
|
|
+
|
|
|
+ // Prefer an explicit global autoplay setting
|
|
|
+ var autoplay = config.autoPlayMedia;
|
|
|
+
|
|
|
+ // If no global setting is available, fall back on the element's
|
|
|
+ // own autoplay setting
|
|
|
+ if( typeof autoplay !== 'boolean' ) {
|
|
|
+ autoplay = iframe.hasAttribute( 'data-autoplay' ) || !!closestParent( iframe, '.slide-background' );
|
|
|
+ }
|
|
|
+
|
|
|
+ // YouTube postMessage API
|
|
|
+ if( /youtube\.com\/embed\//.test( iframe.getAttribute( 'src' ) ) && autoplay ) {
|
|
|
+ iframe.contentWindow.postMessage( '{"event":"command","func":"playVideo","args":""}', '*' );
|
|
|
+ }
|
|
|
+ // Vimeo postMessage API
|
|
|
+ else if( /player\.vimeo\.com\//.test( iframe.getAttribute( 'src' ) ) && autoplay ) {
|
|
|
+ iframe.contentWindow.postMessage( '{"method":"play"}', '*' );
|
|
|
+ }
|
|
|
+ // Generic postMessage API
|
|
|
+ else {
|
|
|
+ iframe.contentWindow.postMessage( 'slide:start', '*' );
|
|
|
+ }
|
|
|
+
|
|
|
}
|
|
|
|
|
|
}
|
|
@@ -3270,40 +3409,41 @@
|
|
|
* Stop playback of any embedded content inside of
|
|
|
* the targeted slide.
|
|
|
*
|
|
|
- * @param {HTMLElement} slide
|
|
|
+ * @param {HTMLElement} element
|
|
|
*/
|
|
|
- function stopEmbeddedContent( slide ) {
|
|
|
+ function stopEmbeddedContent( element ) {
|
|
|
|
|
|
- if( slide && slide.parentNode ) {
|
|
|
+ if( element && element.parentNode ) {
|
|
|
// HTML5 media elements
|
|
|
- toArray( slide.querySelectorAll( 'video, audio' ) ).forEach( function( el ) {
|
|
|
+ toArray( element.querySelectorAll( 'video, audio' ) ).forEach( function( el ) {
|
|
|
if( !el.hasAttribute( 'data-ignore' ) && typeof el.pause === 'function' ) {
|
|
|
+ el.setAttribute('data-paused-by-reveal', '');
|
|
|
el.pause();
|
|
|
}
|
|
|
} );
|
|
|
|
|
|
// Generic postMessage API for non-lazy loaded iframes
|
|
|
- toArray( slide.querySelectorAll( 'iframe' ) ).forEach( function( el ) {
|
|
|
- el.contentWindow.postMessage( 'slide:stop', '*' );
|
|
|
+ toArray( element.querySelectorAll( 'iframe' ) ).forEach( function( el ) {
|
|
|
+ if( el.contentWindow ) el.contentWindow.postMessage( 'slide:stop', '*' );
|
|
|
el.removeEventListener( 'load', startEmbeddedIframe );
|
|
|
});
|
|
|
|
|
|
// YouTube postMessage API
|
|
|
- toArray( slide.querySelectorAll( 'iframe[src*="youtube.com/embed/"]' ) ).forEach( function( el ) {
|
|
|
- if( !el.hasAttribute( 'data-ignore' ) && typeof el.contentWindow.postMessage === 'function' ) {
|
|
|
+ toArray( element.querySelectorAll( 'iframe[src*="youtube.com/embed/"]' ) ).forEach( function( el ) {
|
|
|
+ if( !el.hasAttribute( 'data-ignore' ) && el.contentWindow && typeof el.contentWindow.postMessage === 'function' ) {
|
|
|
el.contentWindow.postMessage( '{"event":"command","func":"pauseVideo","args":""}', '*' );
|
|
|
}
|
|
|
});
|
|
|
|
|
|
// Vimeo postMessage API
|
|
|
- toArray( slide.querySelectorAll( 'iframe[src*="player.vimeo.com/"]' ) ).forEach( function( el ) {
|
|
|
- if( !el.hasAttribute( 'data-ignore' ) && typeof el.contentWindow.postMessage === 'function' ) {
|
|
|
+ toArray( element.querySelectorAll( 'iframe[src*="player.vimeo.com/"]' ) ).forEach( function( el ) {
|
|
|
+ if( !el.hasAttribute( 'data-ignore' ) && el.contentWindow && typeof el.contentWindow.postMessage === 'function' ) {
|
|
|
el.contentWindow.postMessage( '{"method":"pause"}', '*' );
|
|
|
}
|
|
|
});
|
|
|
|
|
|
// Lazy loading iframes
|
|
|
- toArray( slide.querySelectorAll( 'iframe[data-src]' ) ).forEach( function( el ) {
|
|
|
+ toArray( element.querySelectorAll( 'iframe[data-src]' ) ).forEach( function( el ) {
|
|
|
// Only removing the src doesn't actually unload the frame
|
|
|
// in all browsers (Firefox) so we set it to blank first
|
|
|
el.setAttribute( 'src', 'about:blank' );
|
|
@@ -3546,6 +3686,15 @@
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Retrieves all slides in this presentation.
|
|
|
+ */
|
|
|
+ function getSlides() {
|
|
|
+
|
|
|
+ return toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR + ':not(.stack)' ));
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Retrieves the total number of slides in this presentation.
|
|
|
*
|
|
@@ -3553,7 +3702,7 @@
|
|
|
*/
|
|
|
function getTotalSlides() {
|
|
|
|
|
|
- return dom.wrapper.querySelectorAll( SLIDES_SELECTOR + ':not(.stack)' ).length;
|
|
|
+ return getSlides().length;
|
|
|
|
|
|
}
|
|
|
|
|
@@ -3900,7 +4049,7 @@
|
|
|
// If there are media elements with data-autoplay,
|
|
|
// automatically set the autoSlide duration to the
|
|
|
// length of that media. Not applicable if the slide
|
|
|
- // is divided up into fragments.
|
|
|
+ // is divided up into fragments.
|
|
|
// playbackRate is accounted for in the duration.
|
|
|
if( currentSlide.querySelectorAll( '.fragment' ).length === 0 ) {
|
|
|
toArray( currentSlide.querySelectorAll( 'video, audio' ) ).forEach( function( el ) {
|
|
@@ -4113,12 +4262,7 @@
|
|
|
|
|
|
// Check if the pressed key is question mark
|
|
|
if( event.shiftKey && event.charCode === 63 ) {
|
|
|
- if( dom.overlay ) {
|
|
|
- closeOverlay();
|
|
|
- }
|
|
|
- else {
|
|
|
- showHelp( true );
|
|
|
- }
|
|
|
+ toggleHelp();
|
|
|
}
|
|
|
|
|
|
}
|
|
@@ -4816,9 +4960,6 @@
|
|
|
navigatePrev: navigatePrev,
|
|
|
navigateNext: navigateNext,
|
|
|
|
|
|
- // Shows a help overlay with keyboard shortcuts
|
|
|
- showHelp: showHelp,
|
|
|
-
|
|
|
// Forces an update in slide layout
|
|
|
layout: layout,
|
|
|
|
|
@@ -4831,6 +4972,9 @@
|
|
|
// Returns an object with the available fragments as booleans (prev/next)
|
|
|
availableFragments: availableFragments,
|
|
|
|
|
|
+ // Toggles a help overlay with keyboard shortcuts
|
|
|
+ toggleHelp: toggleHelp,
|
|
|
+
|
|
|
// Toggles the overview mode on/off
|
|
|
toggleOverview: toggleOverview,
|
|
|
|
|
@@ -4853,12 +4997,19 @@
|
|
|
getState: getState,
|
|
|
setState: setState,
|
|
|
|
|
|
+ // Presentation progress
|
|
|
+ getSlidePastCount: getSlidePastCount,
|
|
|
+
|
|
|
// Presentation progress on range of 0-1
|
|
|
getProgress: getProgress,
|
|
|
|
|
|
// Returns the indices of the current, or specified, slide
|
|
|
getIndices: getIndices,
|
|
|
|
|
|
+ // Returns an Array of all slides
|
|
|
+ getSlides: getSlides,
|
|
|
+
|
|
|
+ // Returns the total number of slides
|
|
|
getTotalSlides: getTotalSlides,
|
|
|
|
|
|
// Returns the slide element at the specified index
|