|
@@ -1217,6 +1217,8 @@
|
|
|
if( data.backgroundColor ) element.style.backgroundColor = data.backgroundColor;
|
|
|
if( data.backgroundTransition ) element.setAttribute( 'data-background-transition', data.backgroundTransition );
|
|
|
|
|
|
+ if( slide.hasAttribute( 'data-preload' ) ) element.setAttribute( 'data-preload', '' );
|
|
|
+
|
|
|
// Background image options are set on the content wrapper
|
|
|
if( data.backgroundSize ) contentElement.style.backgroundSize = data.backgroundSize;
|
|
|
if( data.backgroundRepeat ) contentElement.style.backgroundRepeat = data.backgroundRepeat;
|
|
@@ -1276,7 +1278,11 @@
|
|
|
|
|
|
// Check if the requested method can be found
|
|
|
if( data.method && typeof Reveal[data.method] === 'function' ) {
|
|
|
- Reveal[data.method].apply( Reveal, data.args );
|
|
|
+ var result = Reveal[data.method].apply( Reveal, data.args );
|
|
|
+
|
|
|
+ // Dispatch a postMessage event with the returned value from
|
|
|
+ // our method invocation for getter functions
|
|
|
+ dispatchPostMessage( 'callback', { method: data.method, result: result } );
|
|
|
}
|
|
|
}
|
|
|
}, false );
|
|
@@ -1447,8 +1453,8 @@
|
|
|
keyboardShortcuts['↓ , J'] = 'Navigate down';
|
|
|
}
|
|
|
|
|
|
- keyboardShortcuts['Home , ⌘/CTRL ←'] = 'First slide';
|
|
|
- keyboardShortcuts['End , ⌘/CTRL →'] = 'Last slide';
|
|
|
+ keyboardShortcuts['Home , Shift ←'] = 'First slide';
|
|
|
+ keyboardShortcuts['End , Shift →'] = 'Last slide';
|
|
|
keyboardShortcuts['B , .'] = 'Pause';
|
|
|
keyboardShortcuts['F'] = 'Fullscreen';
|
|
|
keyboardShortcuts['ESC, O'] = 'Slide overview';
|
|
@@ -1981,8 +1987,25 @@
|
|
|
|
|
|
// If we're in an iframe, post each reveal.js event to the
|
|
|
// parent window. Used by the notes plugin
|
|
|
+ dispatchPostMessage( type );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Dispatched a postMessage of the given type from our window.
|
|
|
+ */
|
|
|
+ function dispatchPostMessage( type, data ) {
|
|
|
+
|
|
|
if( config.postMessageEvents && window.parent !== window.self ) {
|
|
|
- window.parent.postMessage( JSON.stringify({ namespace: 'reveal', eventName: type, state: getState() }), '*' );
|
|
|
+ var message = {
|
|
|
+ namespace: 'reveal',
|
|
|
+ eventName: type,
|
|
|
+ state: getState()
|
|
|
+ };
|
|
|
+
|
|
|
+ extend( message, data );
|
|
|
+
|
|
|
+ window.parent.postMessage( JSON.stringify( message ), '*' );
|
|
|
}
|
|
|
|
|
|
}
|
|
@@ -2243,10 +2266,12 @@
|
|
|
transformSlides( { layout: '' } );
|
|
|
}
|
|
|
else {
|
|
|
- // Prefer zoom for scaling up so that content remains crisp.
|
|
|
- // Don't use zoom to scale down since that can lead to shifts
|
|
|
- // in text layout/line breaks.
|
|
|
- if( scale > 1 && features.zoom ) {
|
|
|
+ // Zoom Scaling
|
|
|
+ // Content remains crisp no matter how much we scale. Side
|
|
|
+ // effects are minor differences in text layout and iframe
|
|
|
+ // viewports changing size. A 200x200 iframe viewport in a
|
|
|
+ // 2x zoomed presentation ends up having a 400x400 viewport.
|
|
|
+ if( scale > 1 && features.zoom && window.devicePixelRatio < 2 ) {
|
|
|
dom.slides.style.zoom = scale;
|
|
|
dom.slides.style.left = '';
|
|
|
dom.slides.style.top = '';
|
|
@@ -2254,7 +2279,10 @@
|
|
|
dom.slides.style.right = '';
|
|
|
transformSlides( { layout: '' } );
|
|
|
}
|
|
|
- // Apply scale transform as a fallback
|
|
|
+ // Transform Scaling
|
|
|
+ // Content layout remains the exact same when scaled up.
|
|
|
+ // Side effect is content becoming blurred, especially with
|
|
|
+ // high scale values on ldpi screens.
|
|
|
else {
|
|
|
dom.slides.style.zoom = '';
|
|
|
dom.slides.style.left = '50%';
|
|
@@ -3039,11 +3067,11 @@
|
|
|
syncBackground( slide );
|
|
|
syncFragments( slide );
|
|
|
|
|
|
+ loadSlide( slide );
|
|
|
+
|
|
|
updateBackground();
|
|
|
updateNotes();
|
|
|
|
|
|
- loadSlide( slide );
|
|
|
-
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -3309,7 +3337,7 @@
|
|
|
}
|
|
|
|
|
|
// Flag if there are ANY vertical slides, anywhere in the deck
|
|
|
- if( dom.wrapper.querySelectorAll( '.slides>section>section' ).length ) {
|
|
|
+ if( hasVerticalSlides() ) {
|
|
|
dom.wrapper.classList.add( 'has-vertical-slides' );
|
|
|
}
|
|
|
else {
|
|
@@ -3317,7 +3345,7 @@
|
|
|
}
|
|
|
|
|
|
// Flag if there are ANY horizontal slides, anywhere in the deck
|
|
|
- if( dom.wrapper.querySelectorAll( '.slides>section' ).length > 1 ) {
|
|
|
+ if( hasHorizontalSlides() ) {
|
|
|
dom.wrapper.classList.add( 'has-horizontal-slides' );
|
|
|
}
|
|
|
else {
|
|
@@ -3599,7 +3627,7 @@
|
|
|
// Stop content inside of previous backgrounds
|
|
|
if( previousBackground ) {
|
|
|
|
|
|
- stopEmbeddedContent( previousBackground );
|
|
|
+ stopEmbeddedContent( previousBackground, { unloadIframes: !shouldPreload( previousBackground ) } );
|
|
|
|
|
|
}
|
|
|
|
|
@@ -3778,6 +3806,7 @@
|
|
|
background.style.display = 'block';
|
|
|
|
|
|
var backgroundContent = slide.slideBackgroundContentElement;
|
|
|
+ var backgroundIframe = slide.getAttribute( 'data-background-iframe' );
|
|
|
|
|
|
// If the background contains media, load it
|
|
|
if( background.hasAttribute( 'data-loaded' ) === false ) {
|
|
@@ -3786,8 +3815,7 @@
|
|
|
var backgroundImage = slide.getAttribute( 'data-background-image' ),
|
|
|
backgroundVideo = slide.getAttribute( 'data-background-video' ),
|
|
|
backgroundVideoLoop = slide.hasAttribute( 'data-background-video-loop' ),
|
|
|
- backgroundVideoMuted = slide.hasAttribute( 'data-background-video-muted' ),
|
|
|
- backgroundIframe = slide.getAttribute( 'data-background-iframe' );
|
|
|
+ backgroundVideoMuted = slide.hasAttribute( 'data-background-video-muted' );
|
|
|
|
|
|
// Images
|
|
|
if( backgroundImage ) {
|
|
@@ -3828,14 +3856,7 @@
|
|
|
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.setAttribute( 'data-src', backgroundIframe );
|
|
|
|
|
|
iframe.style.width = '100%';
|
|
|
iframe.style.height = '100%';
|
|
@@ -3846,6 +3867,19 @@
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // Start loading preloadable iframes
|
|
|
+ var backgroundIframeElement = backgroundContent.querySelector( 'iframe[data-src]' );
|
|
|
+ if( backgroundIframeElement ) {
|
|
|
+
|
|
|
+ // Check if this iframe is eligible to be preloaded
|
|
|
+ if( shouldPreload( background ) && !/autoplay=(1|true|yes)/gi.test( backgroundIframe ) ) {
|
|
|
+ if( backgroundIframeElement.getAttribute( 'src' ) !== backgroundIframe ) {
|
|
|
+ backgroundIframeElement.setAttribute( 'src', backgroundIframe );
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
}
|
|
|
|
|
|
}
|
|
@@ -3865,6 +3899,11 @@
|
|
|
var background = getSlideBackground( slide );
|
|
|
if( background ) {
|
|
|
background.style.display = 'none';
|
|
|
+
|
|
|
+ // Unload any background iframes
|
|
|
+ toArray( background.querySelectorAll( 'iframe[src]' ) ).forEach( function( element ) {
|
|
|
+ element.removeAttribute( 'src' );
|
|
|
+ } );
|
|
|
}
|
|
|
|
|
|
// Reset lazy-loaded media elements with src attributes
|
|
@@ -4429,7 +4468,44 @@
|
|
|
*/
|
|
|
function getSlides() {
|
|
|
|
|
|
- return toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR + ':not(.stack)' ));
|
|
|
+ return toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR + ':not(.stack)' ) );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns a list of all horizontal slides in the deck. Each
|
|
|
+ * vertical stack is included as one horizontal slide in the
|
|
|
+ * resulting array.
|
|
|
+ */
|
|
|
+ function getHorizontalSlides() {
|
|
|
+
|
|
|
+ return toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns all vertical slides that exist within this deck.
|
|
|
+ */
|
|
|
+ function getVerticalSlides() {
|
|
|
+
|
|
|
+ return toArray( dom.wrapper.querySelectorAll( '.slides>section>section' ) );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns true if there are at least two horizontal slides.
|
|
|
+ */
|
|
|
+ function hasHorizontalSlides() {
|
|
|
+
|
|
|
+ return getHorizontalSlides().length > 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns true if there are at least two vertical slides.
|
|
|
+ */
|
|
|
+ function hasVerticalSlides() {
|
|
|
+
|
|
|
+ return getVerticalSlides().length > 1;
|
|
|
|
|
|
}
|
|
|
|
|
@@ -5116,8 +5192,8 @@
|
|
|
|
|
|
// Whitelist specific modified + keycode combinations
|
|
|
var prevSlideShortcut = event.shiftKey && event.keyCode === 32;
|
|
|
- var firstSlideShortcut = ( event.metaKey || event.ctrlKey ) && keyCode === 37;
|
|
|
- var lastSlideShortcut = ( event.metaKey || event.ctrlKey ) && keyCode === 39;
|
|
|
+ var firstSlideShortcut = event.shiftKey && keyCode === 37;
|
|
|
+ var lastSlideShortcut = event.shiftKey && keyCode === 39;
|
|
|
|
|
|
// Prevent all other events when a modifier is pressed
|
|
|
var unusedModifier = !prevSlideShortcut && !firstSlideShortcut && !lastSlideShortcut &&
|
|
@@ -5144,6 +5220,10 @@
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+ // Use linear navigation if we're configured to OR if
|
|
|
+ // the presentation is one-dimensional
|
|
|
+ var useLinearMode = config.navigationMode === 'linear' || !hasHorizontalSlides() || !hasVerticalSlides();
|
|
|
+
|
|
|
var triggered = false;
|
|
|
|
|
|
// 1. User defined key bindings
|
|
@@ -5216,7 +5296,7 @@
|
|
|
if( firstSlideShortcut ) {
|
|
|
slide( 0 );
|
|
|
}
|
|
|
- else if( !isOverview() && config.navigationMode === 'linear' ) {
|
|
|
+ else if( !isOverview() && useLinearMode ) {
|
|
|
navigatePrev();
|
|
|
}
|
|
|
else {
|
|
@@ -5228,7 +5308,7 @@
|
|
|
if( lastSlideShortcut ) {
|
|
|
slide( Number.MAX_VALUE );
|
|
|
}
|
|
|
- else if( !isOverview() && config.navigationMode === 'linear' ) {
|
|
|
+ else if( !isOverview() && useLinearMode ) {
|
|
|
navigateNext();
|
|
|
}
|
|
|
else {
|
|
@@ -5237,7 +5317,7 @@
|
|
|
}
|
|
|
// K, UP
|
|
|
else if( keyCode === 75 || keyCode === 38 ) {
|
|
|
- if( !isOverview() && config.navigationMode === 'linear' ) {
|
|
|
+ if( !isOverview() && useLinearMode ) {
|
|
|
navigatePrev();
|
|
|
}
|
|
|
else {
|
|
@@ -5246,7 +5326,7 @@
|
|
|
}
|
|
|
// J, DOWN
|
|
|
else if( keyCode === 74 || keyCode === 40 ) {
|
|
|
- if( !isOverview() && config.navigationMode === 'linear' ) {
|
|
|
+ if( !isOverview() && useLinearMode ) {
|
|
|
navigateNext();
|
|
|
}
|
|
|
else {
|
|
@@ -5356,19 +5436,49 @@
|
|
|
|
|
|
if( deltaX > touch.threshold && Math.abs( deltaX ) > Math.abs( deltaY ) ) {
|
|
|
touch.captured = true;
|
|
|
- navigateLeft();
|
|
|
+ if( config.navigationMode === 'linear' ) {
|
|
|
+ if( config.rtl ) {
|
|
|
+ navigateNext();
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ navigatePrev();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ navigateLeft();
|
|
|
+ }
|
|
|
}
|
|
|
else if( deltaX < -touch.threshold && Math.abs( deltaX ) > Math.abs( deltaY ) ) {
|
|
|
touch.captured = true;
|
|
|
- navigateRight();
|
|
|
+ if( config.navigationMode === 'linear' ) {
|
|
|
+ if( config.rtl ) {
|
|
|
+ navigatePrev();
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ navigateNext();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ navigateRight();
|
|
|
+ }
|
|
|
}
|
|
|
else if( deltaY > touch.threshold ) {
|
|
|
touch.captured = true;
|
|
|
- navigateUp();
|
|
|
+ if( config.navigationMode === 'linear' ) {
|
|
|
+ navigatePrev();
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ navigateUp();
|
|
|
+ }
|
|
|
}
|
|
|
else if( deltaY < -touch.threshold ) {
|
|
|
touch.captured = true;
|
|
|
- navigateDown();
|
|
|
+ if( config.navigationMode === 'linear' ) {
|
|
|
+ navigateNext();
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ navigateDown();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// If we're embedded, only block touch events if they have
|
|
@@ -5905,6 +6015,15 @@
|
|
|
// Returns the speaker notes string for a slide, or null
|
|
|
getSlideNotes: getSlideNotes,
|
|
|
|
|
|
+ // Returns an array with all horizontal/vertical slides in the deck
|
|
|
+ getHorizontalSlides: getHorizontalSlides,
|
|
|
+ getVerticalSlides: getVerticalSlides,
|
|
|
+
|
|
|
+ // Checks if the presentation contains two or more
|
|
|
+ // horizontal/vertical slides
|
|
|
+ hasHorizontalSlides: hasHorizontalSlides,
|
|
|
+ hasVerticalSlides: hasVerticalSlides,
|
|
|
+
|
|
|
// Returns the previous slide element, may be null
|
|
|
getPreviousSlide: function() {
|
|
|
return previousSlide;
|