aboutsummaryrefslogtreecommitdiff
path: root/js
diff options
context:
space:
mode:
Diffstat (limited to 'js')
-rw-r--r--js/reveal.js282
1 files changed, 204 insertions, 78 deletions
diff --git a/js/reveal.js b/js/reveal.js
index 0bdcd12..021c8d9 100644
--- a/js/reveal.js
+++ b/js/reveal.js
@@ -1,6 +1,6 @@
/*!
* reveal.js
- * http://lab.hakim.se/reveal-js
+ * http://revealjs.com
* MIT licensed
*
* Copyright (C) 2017 Hakim El Hattab, http://hakim.se
@@ -26,7 +26,7 @@
var Reveal;
// The reveal.js version
- var VERSION = '3.5.0';
+ var VERSION = '3.6.0';
var SLIDES_SELECTOR = '.slides section',
HORIZONTAL_SLIDES_SELECTOR = '.slides>section',
@@ -49,9 +49,20 @@
minScale: 0.2,
maxScale: 2.0,
- // Display controls in the bottom right corner
+ // Display presentation control arrows
controls: true,
+ // Help the user learn the controls by providing hints, for example by
+ // bouncing the down arrow when they first encounter a vertical slide
+ controlsTutorial: true,
+
+ // Determines where controls appear, "edges" or "bottom-right"
+ controlsLayout: 'bottom-right',
+
+ // Visibility rule for backwards navigation arrows; "faded", "hidden"
+ // or "visible"
+ controlsBackArrows: 'faded',
+
// Display a presentation progress bar
progress: true,
@@ -106,14 +117,16 @@
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
+ // - 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
+ // Controls automatic progression to the next slide
+ // - 0: Auto-sliding only happens if the data-autoslide HTML attribute
+ // is present on the current slide or fragment
+ // - 1+: All slides will progress automatically at the given interval
+ // - false: No auto-sliding, even if data-autoslide is present
autoSlide: 0,
// Stop auto-sliding after user input
@@ -207,6 +220,10 @@
previousBackground,
+ // Remember which directions that the user has navigated towards
+ hasNavigatedRight = false,
+ hasNavigatedDown = false,
+
// Slides may hold a data-state attribute which we pick up and apply
// as a class to the body. This list contains the combined state of
// all current slides.
@@ -447,6 +464,8 @@
*/
function start() {
+ loaded = true;
+
// Make sure we've got all the DOM elements we need
setupDOM();
@@ -474,8 +493,6 @@
// Enable transitions now that we're loaded
dom.slides.classList.remove( 'no-transition' );
- loaded = true;
-
dom.wrapper.classList.add( 'ready' );
dispatchEvent( 'ready', {
@@ -511,6 +528,20 @@
// Prevent transitions while we're loading
dom.slides.classList.add( 'no-transition' );
+ if( isMobileDevice ) {
+ dom.wrapper.classList.add( 'no-hover' );
+ }
+ else {
+ dom.wrapper.classList.remove( 'no-hover' );
+ }
+
+ if( /iphone/gi.test( UA ) ) {
+ dom.wrapper.classList.add( 'ua-iphone' );
+ }
+ else {
+ dom.wrapper.classList.remove( 'ua-iphone' );
+ }
+
// Background element
dom.background = createSingletonNode( dom.wrapper, 'div', 'backgrounds', null );
@@ -519,11 +550,11 @@
dom.progressbar = dom.progress.querySelector( 'span' );
// Arrow controls
- createSingletonNode( dom.wrapper, 'aside', 'controls',
- '<button class="navigate-left" aria-label="previous slide"></button>' +
- '<button class="navigate-right" aria-label="next slide"></button>' +
- '<button class="navigate-up" aria-label="above slide"></button>' +
- '<button class="navigate-down" aria-label="below slide"></button>' );
+ dom.controls = createSingletonNode( dom.wrapper, 'aside', 'controls',
+ '<button class="navigate-left" aria-label="previous slide"><div class="controls-arrow"></div></button>' +
+ '<button class="navigate-right" aria-label="next slide"><div class="controls-arrow"></div></button>' +
+ '<button class="navigate-up" aria-label="above slide"><div class="controls-arrow"></div></button>' +
+ '<button class="navigate-down" aria-label="below slide"><div class="controls-arrow"></div></button>' );
// Slide number
dom.slideNumber = createSingletonNode( dom.wrapper, 'div', 'slide-number', '' );
@@ -536,9 +567,6 @@
// Overlay graphic which is displayed during the paused mode
createSingletonNode( dom.wrapper, 'div', 'pause-overlay', null );
- // Cache references to elements
- dom.controls = document.querySelector( '.reveal .controls' );
-
dom.wrapper.setAttribute( 'role', 'application' );
// There can be multiple instances of controls throughout the page
@@ -549,6 +577,10 @@
dom.controlsPrev = toArray( document.querySelectorAll( '.navigate-prev' ) );
dom.controlsNext = toArray( document.querySelectorAll( '.navigate-next' ) );
+ // The right and down arrows in the standard reveal.js controls
+ dom.controlsRightArrow = dom.controls.querySelector( '.navigate-right' );
+ dom.controlsDownArrow = dom.controls.querySelector( '.navigate-down' );
+
dom.statusDiv = createStatusDiv();
}
@@ -792,7 +824,7 @@
// If no node was found, create it now
var node = document.createElement( tagname );
- node.classList.add( classname );
+ node.className = classname;
if( typeof innerHTML === 'string' ) {
node.innerHTML = innerHTML;
}
@@ -985,14 +1017,22 @@
*/
function configure( options ) {
- var numberOfSlides = dom.wrapper.querySelectorAll( SLIDES_SELECTOR ).length;
-
- dom.wrapper.classList.remove( config.transition );
+ var oldTransition = config.transition;
// New config options may be passed when this method
// is invoked through the API after initialization
if( typeof options === 'object' ) extend( config, options );
+ // Abort if reveal.js hasn't finished loading, config
+ // changes will be applied automatically once loading
+ // finishes
+ if( loaded === false ) return;
+
+ var numberOfSlides = dom.wrapper.querySelectorAll( SLIDES_SELECTOR ).length;
+
+ // Remove the previously configured transition class
+ dom.wrapper.classList.remove( oldTransition );
+
// Force linear transition based on browser capabilities
if( features.transforms3d === false ) config.transition = 'linear';
@@ -1004,6 +1044,9 @@
dom.controls.style.display = config.controls ? 'block' : 'none';
dom.progress.style.display = config.progress ? 'block' : 'none';
+ dom.controls.setAttribute( 'data-controls-layout', config.controlsLayout );
+ dom.controls.setAttribute( 'data-controls-back-arrows', config.controlsBackArrows );
+
if( config.shuffle ) {
shuffle();
}
@@ -1028,12 +1071,8 @@
}
if( config.showNotes ) {
- dom.speakerNotes.classList.add( 'visible' );
dom.speakerNotes.setAttribute( 'data-layout', typeof config.showNotes === 'string' ? config.showNotes : 'inline' );
}
- else {
- dom.speakerNotes.classList.remove( 'visible' );
- }
if( config.mouseWheel ) {
document.addEventListener( 'DOMMouseScroll', onDocumentMouseScroll, false ); // FF
@@ -1265,6 +1304,8 @@
a[ i ] = b[ i ];
}
+ return a;
+
}
/**
@@ -1291,7 +1332,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;
@@ -2500,13 +2541,14 @@
updateSlideNumber();
updateSlidesVisibility();
updateBackground( true );
+ updateNotesVisibility();
updateNotes();
formatEmbeddedContent();
// Start or stop embedded content depending on global config
if( config.autoPlayMedia === false ) {
- stopEmbeddedContent( currentSlide );
+ stopEmbeddedContent( currentSlide, { unloadIframes: false } );
}
else {
startEmbeddedContent( currentSlide );
@@ -2743,10 +2785,10 @@
// Show the horizontal slide if it's within the view distance
if( distanceX < viewDistance ) {
- showSlide( horizontalSlide );
+ loadSlide( horizontalSlide );
}
else {
- hideSlide( horizontalSlide );
+ unloadSlide( horizontalSlide );
}
if( verticalSlidesLength ) {
@@ -2759,16 +2801,32 @@
distanceY = x === ( indexh || 0 ) ? Math.abs( ( indexv || 0 ) - y ) : Math.abs( y - oy );
if( distanceX + distanceY < viewDistance ) {
- showSlide( verticalSlide );
+ loadSlide( verticalSlide );
}
else {
- hideSlide( verticalSlide );
+ unloadSlide( verticalSlide );
}
}
}
}
+ // Flag if there are ANY vertical slides, anywhere in the deck
+ if( dom.wrapper.querySelectorAll( '.slides>section>section' ).length ) {
+ dom.wrapper.classList.add( 'has-vertical-slides' );
+ }
+ else {
+ dom.wrapper.classList.remove( 'has-vertical-slides' );
+ }
+
+ // Flag if there are ANY horizontal slides, anywhere in the deck
+ if( dom.wrapper.querySelectorAll( '.slides>section' ).length > 1 ) {
+ dom.wrapper.classList.add( 'has-horizontal-slides' );
+ }
+ else {
+ dom.wrapper.classList.remove( 'has-horizontal-slides' );
+ }
+
}
}
@@ -2783,13 +2841,40 @@
if( config.showNotes && dom.speakerNotes && currentSlide && !isPrintingPDF() ) {
- dom.speakerNotes.innerHTML = getSlideNotes() || '';
+ dom.speakerNotes.innerHTML = getSlideNotes() || '<span class="notes-placeholder">No notes on this slide.</span>';
}
}
/**
+ * Updates the visibility of the speaker notes sidebar that
+ * is used to share annotated slides. The notes sidebar is
+ * only visible if showNotes is true and there are notes on
+ * one or more slides in the deck.
+ */
+ function updateNotesVisibility() {
+
+ if( config.showNotes && hasNotes() ) {
+ dom.wrapper.classList.add( 'show-notes' );
+ }
+ else {
+ dom.wrapper.classList.remove( 'show-notes' );
+ }
+
+ }
+
+ /**
+ * Checks if there are speaker notes for ANY slide in the
+ * presentation.
+ */
+ function hasNotes() {
+
+ return dom.slides.querySelectorAll( '[data-notes], aside.notes' ).length > 0;
+
+ }
+
+ /**
* Updates the progress bar to reflect the current slide.
*/
function updateProgress() {
@@ -2919,6 +3004,26 @@
}
+ if( config.controlsTutorial ) {
+
+ // Highlight control arrows with an animation to ensure
+ // that the viewer knows how to navigate
+ if( !hasNavigatedDown && routes.down ) {
+ dom.controlsDownArrow.classList.add( 'highlight' );
+ }
+ else {
+ dom.controlsDownArrow.classList.remove( 'highlight' );
+
+ if( !hasNavigatedRight && routes.right && indexv === 0 ) {
+ dom.controlsRightArrow.classList.add( 'highlight' );
+ }
+ else {
+ dom.controlsRightArrow.classList.remove( 'highlight' );
+ }
+ }
+
+ }
+
}
/**
@@ -3098,14 +3203,9 @@
*
* @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
- * that is set to load lazily (data-src).
- *
- * @param {HTMLElement} slide Slide to show
- */
- function showSlide( slide ) {
+ function loadSlide( slide, options ) {
+
+ options = options || {};
// Show the slide element
slide.style.display = config.display;
@@ -3113,6 +3213,7 @@
// Media elements with data-src attributes
toArray( slide.querySelectorAll( 'img[data-src], video[data-src], audio[data-src]' ) ).forEach( function( element ) {
element.setAttribute( 'src', element.getAttribute( 'data-src' ) );
+ element.setAttribute( 'data-lazy-loaded', '' );
element.removeAttribute( 'data-src' );
} );
@@ -3123,6 +3224,7 @@
toArray( media.querySelectorAll( 'source[data-src]' ) ).forEach( function( source ) {
source.setAttribute( 'src', source.getAttribute( 'data-src' ) );
source.removeAttribute( 'data-src' );
+ source.setAttribute( 'data-lazy-loaded', '' );
sources += 1;
} );
@@ -3135,8 +3237,7 @@
// Show the corresponding background element
- var indices = getIndices( slide );
- var background = getSlideBackground( indices.h, indices.v );
+ var background = getSlideBackground( slide );
if( background ) {
background.style.display = 'block';
@@ -3183,7 +3284,7 @@
background.appendChild( video );
}
// Iframes
- else if( backgroundIframe ) {
+ else if( backgroundIframe && options.excludeIframes !== true ) {
var iframe = document.createElement( 'iframe' );
iframe.setAttribute( 'allowfullscreen', '' );
iframe.setAttribute( 'mozallowfullscreen', '' );
@@ -3212,23 +3313,34 @@
}
/**
- * Called when the given slide is moved outside of the
- * configured view distance.
+ * Unloads and hides the given slide. This is called when the
+ * slide is moved outside of the configured view distance.
*
* @param {HTMLElement} slide
*/
- function hideSlide( slide ) {
+ function unloadSlide( slide ) {
// Hide the slide element
slide.style.display = 'none';
// Hide the corresponding background element
- var indices = getIndices( slide );
- var background = getSlideBackground( indices.h, indices.v );
+ var background = getSlideBackground( slide );
if( background ) {
background.style.display = 'none';
}
+ // Reset lazy-loaded media elements with src attributes
+ toArray( slide.querySelectorAll( 'video[data-lazy-loaded][src], audio[data-lazy-loaded][src]' ) ).forEach( function( element ) {
+ element.setAttribute( 'data-src', element.getAttribute( 'src' ) );
+ element.removeAttribute( 'src' );
+ } );
+
+ // Reset lazy-loaded media elements with <source> children
+ toArray( slide.querySelectorAll( 'video[data-lazy-loaded] source[src], audio source[src]' ) ).forEach( function( source ) {
+ source.setAttribute( 'data-src', source.getAttribute( 'src' ) );
+ source.removeAttribute( 'src' );
+ } );
+
}
/**
@@ -3304,6 +3416,13 @@
_appendParamToIframeSource( 'src', 'player.vimeo.com/', 'api=1' );
_appendParamToIframeSource( 'data-src', 'player.vimeo.com/', 'api=1' );
+ // Always show media controls on mobile devices
+ if( isMobileDevice ) {
+ toArray( dom.slides.querySelectorAll( 'video, audio' ) ).forEach( function( el ) {
+ el.controls = true;
+ } );
+ }
+
}
/**
@@ -3448,7 +3567,12 @@
*
* @param {HTMLElement} element
*/
- function stopEmbeddedContent( element ) {
+ function stopEmbeddedContent( element, options ) {
+
+ options = extend( {
+ // Defaults
+ unloadIframes: true
+ }, options || {} );
if( element && element.parentNode ) {
// HTML5 media elements
@@ -3479,13 +3603,15 @@
}
});
- // Lazy loading iframes
- 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' );
- el.removeAttribute( 'src' );
- } );
+ if( options.unloadIframes === true ) {
+ // Unload lazy-loaded iframes
+ 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' );
+ el.removeAttribute( 'src' );
+ } );
+ }
}
}
@@ -3767,31 +3893,19 @@
* defined, have a background element so as long as the
* index is valid an element will be returned.
*
- * @param {number} x Horizontal background index
+ * @param {mixed} x Horizontal background index OR a slide
+ * HTML element
* @param {number} y Vertical background index
* @return {(HTMLElement[]|*)}
*/
function getSlideBackground( x, y ) {
- // When printing to PDF the slide backgrounds are nested
- // inside of the slides
- if( isPrintingPDF() ) {
- var slide = getSlide( x, y );
- if( slide ) {
- return slide.slideBackgroundElement;
- }
-
- return undefined;
- }
-
- var horizontalBackground = dom.wrapper.querySelectorAll( '.backgrounds>.slide-background' )[ x ];
- var verticalBackgrounds = horizontalBackground && horizontalBackground.querySelectorAll( '.slide-background' );
-
- if( verticalBackgrounds && verticalBackgrounds.length && typeof y === 'number' ) {
- return verticalBackgrounds ? verticalBackgrounds[ y ] : undefined;
+ var slide = typeof x === 'number' ? getSlide( x, y ) : x;
+ if( slide ) {
+ return slide.slideBackgroundElement;
}
- return horizontalBackground;
+ return undefined;
}
@@ -4053,7 +4167,7 @@
cancelAutoSlide();
- if( currentSlide ) {
+ if( currentSlide && config.autoSlide !== false ) {
var fragment = currentSlide.querySelector( '.current-fragment' );
@@ -4171,6 +4285,8 @@
function navigateRight() {
+ hasNavigatedRight = true;
+
// Reverse for RTL
if( config.rtl ) {
if( ( isOverview() || previousFragment() === false ) && availableRoutes().right ) {
@@ -4195,6 +4311,8 @@
function navigateDown() {
+ hasNavigatedDown = true;
+
// Prioritize revealing fragments
if( ( isOverview() || nextFragment() === false ) && availableRoutes().down ) {
slide( indexh, indexv + 1 );
@@ -4241,6 +4359,9 @@
*/
function navigateNext() {
+ hasNavigatedRight = true;
+ hasNavigatedDown = true;
+
// Prioritize revealing fragments
if( nextFragment() === false ) {
if( availableRoutes().down ) {
@@ -4933,7 +5054,7 @@
this.context.beginPath();
this.context.arc( x, y, radius, 0, Math.PI * 2, false );
this.context.lineWidth = this.thickness;
- this.context.strokeStyle = '#666';
+ this.context.strokeStyle = 'rgba( 255, 255, 255, 0.2 )';
this.context.stroke();
if( this.playing ) {
@@ -5049,6 +5170,11 @@
isOverview: isOverview,
isPaused: isPaused,
isAutoSliding: isAutoSliding,
+ isSpeakerNotes: isSpeakerNotes,
+
+ // Slide preloading
+ loadSlide: loadSlide,
+ unloadSlide: unloadSlide,
// Adds or removes all internal event listeners (such as keyboard)
addEventListeners: addEventListeners,