From 79728e25cd6c98193d8e6247e73a2a65a62ce04e Mon Sep 17 00:00:00 2001 From: Hakim El Hattab Date: Tue, 20 Sep 2016 13:41:38 +0200 Subject: [PATCH] autoplay support for content inside of fragments --- js/reveal.js | 64 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 8 deletions(-) diff --git a/js/reveal.js b/js/reveal.js index b975ff4..2fc2f78 100644 --- a/js/reveal.js +++ b/js/reveal.js @@ -1242,6 +1242,42 @@ } + /** + * Find the closest parent that matches the given + * selector. + * + * @param {HTMLElement} target The child element + * @param {String} selector The CSS selector to match + * the parents against + * + * @return {HTMLElement} The matched parent or null + * if no matching parent was found + */ + function closestParent( target, selector ) { + + var parent = target.parentNode; + + while( parent ) { + + // There's some overhead doing this each time, we don't + // want to rewrite the element prototype but should still + // be enough to feature detect once at startup... + var matchesMethod = parent.matches || parent.matchesSelector || parent.msMatchesSelector; + + // If we find a match, we're all set + if( matchesMethod && matchesMethod.call( parent, selector ) ) { + return parent; + } + + // Keep searching + parent = parent.parentNode; + + } + + return null; + + } + /** * Converts various color input formats to an {r:0,g:0,b:0} object. * @@ -3046,32 +3082,44 @@ /** * Start playback of any embedded content inside of - * the targeted slide. + * the given element. */ - function startEmbeddedContent( slide ) { + function startEmbeddedContent( element ) { - if( slide && !isSpeakerNotes() ) { + if( element && !isSpeakerNotes() ) { // Restart GIFs - toArray( slide.querySelectorAll( 'img[src$=".gif"]' ) ).forEach( function( el ) { + toArray( element.querySelectorAll( 'img[src$=".gif"]' ) ).forEach( function( el ) { // Setting the same unchanged source like this was confirmed // to work in Chrome, FF & Safari el.setAttribute( 'src', el.getAttribute( 'src' ) ); } ); // HTML5 media elements - toArray( slide.querySelectorAll( 'video, audio' ) ).forEach( function( el ) { + toArray( element.querySelectorAll( 'video, audio' ) ).forEach( function( el ) { + if( closestParent( el, '.fragment' ) && !closestParent( el, '.fragment.visible' ) ) { + return; + } + if( el.hasAttribute( 'data-autoplay' ) && typeof el.play === 'function' ) { el.play(); } } ); // Normal iframes - toArray( slide.querySelectorAll( 'iframe[src]' ) ).forEach( function( el ) { + toArray( element.querySelectorAll( 'iframe[src]' ) ).forEach( function( el ) { + if( closestParent( el, '.fragment' ) && !closestParent( el, '.fragment.visible' ) ) { + return; + } + startEmbeddedIframe( { target: el } ); } ); // Lazy loading iframes - toArray( slide.querySelectorAll( 'iframe[data-src]' ) ).forEach( function( el ) { + toArray( element.querySelectorAll( 'iframe[data-src]' ) ).forEach( function( el ) { + if( closestParent( el, '.fragment' ) && !closestParent( el, '.fragment.visible' ) ) { + return; + } + if( el.getAttribute( 'src' ) !== el.getAttribute( 'data-src' ) ) { el.removeEventListener( 'load', startEmbeddedIframe ); // remove first to avoid dupes el.addEventListener( 'load', startEmbeddedIframe ); @@ -3622,6 +3670,7 @@ if( i === index ) { element.classList.add( 'current-fragment' ); + startEmbeddedContent( element ); } } // Hidden fragments @@ -3631,7 +3680,6 @@ element.classList.remove( 'current-fragment' ); } - } ); if( fragmentsHidden.length ) {