reveal.js/js/reveal.js in reveal-ck-0.1.3 vs reveal.js/js/reveal.js in reveal-ck-0.1.4
- old
+ new
@@ -57,20 +57,24 @@
rtl: false,
// Turns fragments on and off globally
fragments: true,
+ // Flags if the presentation is running in an embedded mode,
+ // i.e. contained within a limited portion of the screen
+ embedded: false,
+
// 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
autoSlide: 0,
// Enable slide navigation via mouse wheel
mouseWheel: false,
// Apply a 3D roll to links on hover
- rollingLinks: true,
+ rollingLinks: false,
// Opens links in an iframe preview overlay
previewLinks: false,
// Theme (see /css/theme)
@@ -81,22 +85,28 @@
// Transition speed
transitionSpeed: 'default', // default/fast/slow
// Transition style for full page slide backgrounds
- backgroundTransition: 'default', // default/linear
+ backgroundTransition: 'default', // default/linear/none
+ // Number of slides away from the current that are visible
+ viewDistance: 3,
+
// Script dependencies to load
dependencies: []
},
+ // Flags if reveal.js is loaded (has dispatched the 'ready' event)
+ loaded = false,
+
// The current auto-slide duration
autoSlide = 0,
// The horizontal and vertical index of the currently active slide
- indexh = 0,
- indexv = 0,
+ indexh,
+ indexv,
// The previous and current slide HTML elements
previousSlide,
currentSlide,
@@ -109,24 +119,19 @@
scale = 1,
// Cached references to DOM elements
dom = {},
- // Detect support for CSS 3D transforms
- supports3DTransforms = 'WebkitPerspective' in document.body.style ||
- 'MozPerspective' in document.body.style ||
- 'msPerspective' in document.body.style ||
- 'OPerspective' in document.body.style ||
- 'perspective' in document.body.style,
+ // Client support for CSS 3D transforms, see #checkCapabilities()
+ supports3DTransforms,
- // Detect support for CSS 2D transforms
- supports2DTransforms = 'WebkitTransform' in document.body.style ||
- 'MozTransform' in document.body.style ||
- 'msTransform' in document.body.style ||
- 'OTransform' in document.body.style ||
- 'transform' in document.body.style,
+ // Client support for CSS 2D transforms, see #checkCapabilities()
+ supports2DTransforms,
+ // Client is a mobile device, see #checkCapabilities()
+ isMobileDevice,
+
// Throttles mouse wheel navigation
lastMouseWheelStep = 0,
// An interval used to automatically move on to the next slide
autoSlideTimeout = 0,
@@ -147,19 +152,21 @@
touch = {
startX: 0,
startY: 0,
startSpan: 0,
startCount: 0,
- handled: false,
- threshold: 80
+ captured: false,
+ threshold: 40
};
/**
* Starts up the presentation if the client is capable.
*/
function initialize( options ) {
+ checkCapabilities();
+
if( !supports2DTransforms && !supports3DTransforms ) {
document.body.setAttribute( 'class', 'no-transforms' );
// If the browser doesn't support core features we won't be
// using JavaScript to control the presentation
@@ -179,10 +186,140 @@
load();
}
/**
+ * Inspect the client to see what it's capable of, this
+ * should only happens once per runtime.
+ */
+ function checkCapabilities() {
+
+ supports3DTransforms = 'WebkitPerspective' in document.body.style ||
+ 'MozPerspective' in document.body.style ||
+ 'msPerspective' in document.body.style ||
+ 'OPerspective' in document.body.style ||
+ 'perspective' in document.body.style;
+
+ supports2DTransforms = 'WebkitTransform' in document.body.style ||
+ 'MozTransform' in document.body.style ||
+ 'msTransform' in document.body.style ||
+ 'OTransform' in document.body.style ||
+ 'transform' in document.body.style;
+
+ isMobileDevice = navigator.userAgent.match( /(iphone|ipod|android)/gi );
+
+ }
+
+ /**
+ * Loads the dependencies of reveal.js. Dependencies are
+ * defined via the configuration option 'dependencies'
+ * and will be loaded prior to starting/binding reveal.js.
+ * Some dependencies may have an 'async' flag, if so they
+ * will load after reveal.js has been started up.
+ */
+ function load() {
+
+ var scripts = [],
+ scriptsAsync = [];
+
+ for( var i = 0, len = config.dependencies.length; i < len; i++ ) {
+ var s = config.dependencies[i];
+
+ // Load if there's no condition or the condition is truthy
+ if( !s.condition || s.condition() ) {
+ if( s.async ) {
+ scriptsAsync.push( s.src );
+ }
+ else {
+ scripts.push( s.src );
+ }
+
+ // Extension may contain callback functions
+ if( typeof s.callback === 'function' ) {
+ head.ready( s.src.match( /([\w\d_\-]*)\.?js$|[^\\\/]*$/i )[0], s.callback );
+ }
+ }
+ }
+
+ // Called once synchronous scripts finish loading
+ function proceed() {
+ if( scriptsAsync.length ) {
+ // Load asynchronous scripts
+ head.js.apply( null, scriptsAsync );
+ }
+
+ start();
+ }
+
+ if( scripts.length ) {
+ head.ready( proceed );
+
+ // Load synchronous scripts
+ head.js.apply( null, scripts );
+ }
+ else {
+ proceed();
+ }
+
+ }
+
+ /**
+ * Starts up reveal.js by binding input events and navigating
+ * to the current URL deeplink if there is one.
+ */
+ function start() {
+
+ // Make sure we've got all the DOM elements we need
+ setupDOM();
+
+ // Decorate the slide DOM elements with state classes (past/future)
+ setupSlides();
+
+ // Updates the presentation to match the current configuration values
+ configure();
+
+ // Read the initial hash
+ readURL();
+
+ // Notify listeners that the presentation is ready but use a 1ms
+ // timeout to ensure it's not fired synchronously after #initialize()
+ setTimeout( function() {
+ // Enable transitions now that we're loaded
+ dom.slides.classList.remove( 'no-transition' );
+
+ loaded = true;
+
+ dispatchEvent( 'ready', {
+ 'indexh': indexh,
+ 'indexv': indexv,
+ 'currentSlide': currentSlide
+ } );
+ }, 1 );
+
+ }
+
+ /**
+ * Iterates through and decorates slides DOM elements with
+ * appropriate classes.
+ */
+ function setupSlides() {
+
+ var horizontalSlides = toArray( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) );
+ horizontalSlides.forEach( function( horizontalSlide ) {
+
+ var verticalSlides = toArray( horizontalSlide.querySelectorAll( 'section' ) );
+ verticalSlides.forEach( function( verticalSlide, y ) {
+
+ if( y > 0 ) verticalSlide.classList.add( 'future' );
+
+ } );
+
+ } );
+
+ }
+
+ /**
* Finds and stores references to DOM elements which are
* required by the presentation. If a required element is
* not found, it is created.
*/
function setupDOM() {
@@ -190,65 +327,63 @@
// Cache references to key DOM elements
dom.theme = document.querySelector( '#theme' );
dom.wrapper = document.querySelector( '.reveal' );
dom.slides = document.querySelector( '.reveal .slides' );
+ // Prevent transitions while we're loading
+ dom.slides.classList.add( 'no-transition' );
+
// Background element
- if( !document.querySelector( '.reveal .backgrounds' ) ) {
- dom.background = document.createElement( 'div' );
- dom.background.classList.add( 'backgrounds' );
- dom.wrapper.appendChild( dom.background );
- }
+ dom.background = createSingletonNode( dom.wrapper, 'div', 'backgrounds', null );
// Progress bar
- if( !dom.wrapper.querySelector( '.progress' ) ) {
- var progressElement = document.createElement( 'div' );
- progressElement.classList.add( 'progress' );
- progressElement.innerHTML = '<span></span>';
- dom.wrapper.appendChild( progressElement );
- }
+ dom.progress = createSingletonNode( dom.wrapper, 'div', 'progress', '<span></span>' );
+ dom.progressbar = dom.progress.querySelector( 'span' );
// Arrow controls
- if( !dom.wrapper.querySelector( '.controls' ) ) {
- var controlsElement = document.createElement( 'aside' );
- controlsElement.classList.add( 'controls' );
- controlsElement.innerHTML = '<div class="navigate-left"></div>' +
- '<div class="navigate-right"></div>' +
- '<div class="navigate-up"></div>' +
- '<div class="navigate-down"></div>';
- dom.wrapper.appendChild( controlsElement );
- }
+ createSingletonNode( dom.wrapper, 'aside', 'controls',
+ '<div class="navigate-left"></div>' +
+ '<div class="navigate-right"></div>' +
+ '<div class="navigate-up"></div>' +
+ '<div class="navigate-down"></div>' );
// State background element [DEPRECATED]
- if( !dom.wrapper.querySelector( '.state-background' ) ) {
- var stateBackgroundElement = document.createElement( 'div' );
- stateBackgroundElement.classList.add( 'state-background' );
- dom.wrapper.appendChild( stateBackgroundElement );
- }
+ createSingletonNode( dom.wrapper, 'div', 'state-background', null );
// Overlay graphic which is displayed during the paused mode
- if( !dom.wrapper.querySelector( '.pause-overlay' ) ) {
- var pausedElement = document.createElement( 'div' );
- pausedElement.classList.add( 'pause-overlay' );
- dom.wrapper.appendChild( pausedElement );
- }
+ createSingletonNode( dom.wrapper, 'div', 'pause-overlay', null );
// Cache references to elements
- dom.progress = document.querySelector( '.reveal .progress' );
- dom.progressbar = document.querySelector( '.reveal .progress span' );
+ dom.controls = document.querySelector( '.reveal .controls' );
- if ( config.controls ) {
- dom.controls = document.querySelector( '.reveal .controls' );
+ // There can be multiple instances of controls throughout the page
+ dom.controlsLeft = toArray( document.querySelectorAll( '.navigate-left' ) );
+ dom.controlsRight = toArray( document.querySelectorAll( '.navigate-right' ) );
+ dom.controlsUp = toArray( document.querySelectorAll( '.navigate-up' ) );
+ dom.controlsDown = toArray( document.querySelectorAll( '.navigate-down' ) );
+ dom.controlsPrev = toArray( document.querySelectorAll( '.navigate-prev' ) );
+ dom.controlsNext = toArray( document.querySelectorAll( '.navigate-next' ) );
- // There can be multiple instances of controls throughout the page
- dom.controlsLeft = toArray( document.querySelectorAll( '.navigate-left' ) );
- dom.controlsRight = toArray( document.querySelectorAll( '.navigate-right' ) );
- dom.controlsUp = toArray( document.querySelectorAll( '.navigate-up' ) );
- dom.controlsDown = toArray( document.querySelectorAll( '.navigate-down' ) );
- dom.controlsPrev = toArray( document.querySelectorAll( '.navigate-prev' ) );
- dom.controlsNext = toArray( document.querySelectorAll( '.navigate-next' ) );
+ }
+
+ /**
+ * Creates an HTML element and returns a reference to it.
+ * If the element already exists the existing instance will
+ * be returned.
+ */
+ function createSingletonNode( container, tagname, classname, innerHTML ) {
+
+ var node = container.querySelector( '.' + classname );
+ if( !node ) {
+ node = document.createElement( tagname );
+ node.classList.add( classname );
+ if( innerHTML !== null ) {
+ node.innerHTML = innerHTML;
+ }
+ container.appendChild( node );
}
+ return node;
}
/**
* Creates the slide background elements and appends them
@@ -333,103 +468,10 @@
} );
}
/**
- * Hides the address bar if we're on a mobile device.
- */
- function hideAddressBar() {
-
- if( /iphone|ipod|android/gi.test( navigator.userAgent ) && !/crios/gi.test( navigator.userAgent ) ) {
- // Events that should trigger the address bar to hide
- window.addEventListener( 'load', removeAddressBar, false );
- window.addEventListener( 'orientationchange', removeAddressBar, false );
- }
-
- }
-
- /**
- * Loads the dependencies of reveal.js. Dependencies are
- * defined via the configuration option 'dependencies'
- * and will be loaded prior to starting/binding reveal.js.
- * Some dependencies may have an 'async' flag, if so they
- * will load after reveal.js has been started up.
- */
- function load() {
-
- var scripts = [],
- scriptsAsync = [];
-
- for( var i = 0, len = config.dependencies.length; i < len; i++ ) {
- var s = config.dependencies[i];
-
- // Load if there's no condition or the condition is truthy
- if( !s.condition || s.condition() ) {
- if( s.async ) {
- scriptsAsync.push( s.src );
- }
- else {
- scripts.push( s.src );
- }
-
- // Extension may contain callback functions
- if( typeof s.callback === 'function' ) {
- head.ready( s.src.match( /([\w\d_\-]*)\.?js$|[^\\\/]*$/i )[0], s.callback );
- }
- }
- }
-
- // Called once synchronous scripts finish loading
- function proceed() {
- if( scriptsAsync.length ) {
- // Load asynchronous scripts
- head.js.apply( null, scriptsAsync );
- }
-
- start();
- }
-
- if( scripts.length ) {
- head.ready( proceed );
-
- // Load synchronous scripts
- head.js.apply( null, scripts );
- }
- else {
- proceed();
- }
-
- }
-
- /**
- * Starts up reveal.js by binding input events and navigating
- * to the current URL deeplink if there is one.
- */
- function start() {
-
- // Make sure we've got all the DOM elements we need
- setupDOM();
-
- // Updates the presentation to match the current configuration values
- configure();
-
- // Read the initial hash
- readURL();
-
- // Notify listeners that the presentation is ready but use a 1ms
- // timeout to ensure it's not fired synchronously after #initialize()
- setTimeout( function() {
- dispatchEvent( 'ready', {
- 'indexh': indexh,
- 'indexv': indexv,
- 'currentSlide': currentSlide
- } );
- }, 1 );
-
- }
-
- /**
* Applies the configuration settings from the config
* object. May be called multiple times.
*/
function configure( options ) {
@@ -445,18 +487,13 @@
dom.wrapper.classList.add( config.transition );
dom.wrapper.setAttribute( 'data-transition-speed', config.transitionSpeed );
dom.wrapper.setAttribute( 'data-background-transition', config.backgroundTransition );
- if( dom.controls ) {
- dom.controls.style.display = ( config.controls && dom.controls ) ? 'block' : 'none';
- }
+ dom.controls.style.display = config.controls ? 'block' : 'none';
+ dom.progress.style.display = config.progress ? 'block' : 'none';
- if( dom.progress ) {
- dom.progress.style.display = ( config.progress && dom.progress ) ? 'block' : 'none';
- }
-
if( config.rtl ) {
dom.wrapper.classList.add( 'rtl' );
}
else {
dom.wrapper.classList.remove( 'rtl' );
@@ -540,20 +577,18 @@
if ( config.progress && dom.progress ) {
dom.progress.addEventListener( 'click', onProgressClicked, false );
}
- if ( config.controls && dom.controls ) {
- [ 'touchstart', 'click' ].forEach( function( eventName ) {
- dom.controlsLeft.forEach( function( el ) { el.addEventListener( eventName, onNavigateLeftClicked, false ); } );
- dom.controlsRight.forEach( function( el ) { el.addEventListener( eventName, onNavigateRightClicked, false ); } );
- dom.controlsUp.forEach( function( el ) { el.addEventListener( eventName, onNavigateUpClicked, false ); } );
- dom.controlsDown.forEach( function( el ) { el.addEventListener( eventName, onNavigateDownClicked, false ); } );
- dom.controlsPrev.forEach( function( el ) { el.addEventListener( eventName, onNavigatePrevClicked, false ); } );
- dom.controlsNext.forEach( function( el ) { el.addEventListener( eventName, onNavigateNextClicked, false ); } );
- } );
- }
+ [ 'touchstart', 'click' ].forEach( function( eventName ) {
+ dom.controlsLeft.forEach( function( el ) { el.addEventListener( eventName, onNavigateLeftClicked, false ); } );
+ dom.controlsRight.forEach( function( el ) { el.addEventListener( eventName, onNavigateRightClicked, false ); } );
+ dom.controlsUp.forEach( function( el ) { el.addEventListener( eventName, onNavigateUpClicked, false ); } );
+ dom.controlsDown.forEach( function( el ) { el.addEventListener( eventName, onNavigateDownClicked, false ); } );
+ dom.controlsPrev.forEach( function( el ) { el.addEventListener( eventName, onNavigatePrevClicked, false ); } );
+ dom.controlsNext.forEach( function( el ) { el.addEventListener( eventName, onNavigateNextClicked, false ); } );
+ } );
}
/**
* Unbinds all event listeners.
@@ -578,20 +613,18 @@
if ( config.progress && dom.progress ) {
dom.progress.removeEventListener( 'click', onProgressClicked, false );
}
- if ( config.controls && dom.controls ) {
- [ 'touchstart', 'click' ].forEach( function( eventName ) {
- dom.controlsLeft.forEach( function( el ) { el.removeEventListener( eventName, onNavigateLeftClicked, false ); } );
- dom.controlsRight.forEach( function( el ) { el.removeEventListener( eventName, onNavigateRightClicked, false ); } );
- dom.controlsUp.forEach( function( el ) { el.removeEventListener( eventName, onNavigateUpClicked, false ); } );
- dom.controlsDown.forEach( function( el ) { el.removeEventListener( eventName, onNavigateDownClicked, false ); } );
- dom.controlsPrev.forEach( function( el ) { el.removeEventListener( eventName, onNavigatePrevClicked, false ); } );
- dom.controlsNext.forEach( function( el ) { el.removeEventListener( eventName, onNavigateNextClicked, false ); } );
- } );
- }
+ [ 'touchstart', 'click' ].forEach( function( eventName ) {
+ dom.controlsLeft.forEach( function( el ) { el.removeEventListener( eventName, onNavigateLeftClicked, false ); } );
+ dom.controlsRight.forEach( function( el ) { el.removeEventListener( eventName, onNavigateRightClicked, false ); } );
+ dom.controlsUp.forEach( function( el ) { el.removeEventListener( eventName, onNavigateUpClicked, false ); } );
+ dom.controlsDown.forEach( function( el ) { el.removeEventListener( eventName, onNavigateDownClicked, false ); } );
+ dom.controlsPrev.forEach( function( el ) { el.removeEventListener( eventName, onNavigatePrevClicked, false ); } );
+ dom.controlsNext.forEach( function( el ) { el.removeEventListener( eventName, onNavigateNextClicked, false ); } );
+ } );
}
/**
* Extend object a with the properties of object b.
@@ -629,10 +662,23 @@
return Math.sqrt( dx*dx + dy*dy );
}
/**
+ * Applies a CSS transform to the target element.
+ */
+ function transformElement( element, transform ) {
+
+ element.style.WebkitTransform = transform;
+ element.style.MozTransform = transform;
+ element.style.msTransform = transform;
+ element.style.OTransform = transform;
+ element.style.transform = transform;
+
+ }
+
+ /**
* Retrieves the height of the given element by looking
* at the position and height of its immediate children.
*/
function getAbsoluteHeight( element ) {
@@ -664,25 +710,81 @@
return height;
}
/**
+ * Returns the remaining height within the parent of the
+ * target element after subtracting the height of all
+ * siblings.
+ *
+ * remaining height = [parent height] - [ siblings height]
+ */
+ function getRemainingHeight( element, height ) {
+
+ height = height || 0;
+
+ if( element ) {
+ var parent = element.parentNode;
+ var siblings = parent.childNodes;
+
+ // Subtract the height of each sibling
+ toArray( siblings ).forEach( function( sibling ) {
+
+ if( typeof sibling.offsetHeight === 'number' && sibling !== element ) {
+
+ var styles = window.getComputedStyle( sibling ),
+ marginTop = parseInt( styles.marginTop, 10 ),
+ marginBottom = parseInt( styles.marginBottom, 10 );
+
+ height -= sibling.offsetHeight + marginTop + marginBottom;
+
+ }
+
+ } );
+
+ var elementStyles = window.getComputedStyle( element );
+
+ // Subtract the margins of the target element
+ height -= parseInt( elementStyles.marginTop, 10 ) +
+ parseInt( elementStyles.marginBottom, 10 );
+
+ }
+
+ return height;
+
+ }
+
+ /**
* Checks if this instance is being used to print a PDF.
*/
function isPrintingPDF() {
return ( /print-pdf/gi ).test( window.location.search );
}
/**
+ * Hides the address bar if we're on a mobile device.
+ */
+ function hideAddressBar() {
+
+ if( isMobileDevice ) {
+ // Events that should trigger the address bar to hide
+ window.addEventListener( 'load', removeAddressBar, false );
+ window.addEventListener( 'orientationchange', removeAddressBar, false );
+ }
+
+ }
+
+ /**
* Causes the address bar to hide on mobile devices,
* more vertical space ftw.
*/
function removeAddressBar() {
- if( window.orientation === 0 ) {
+ // Portrait and not Chrome for iOS
+ if( window.orientation === 0 && !/crios/gi.test( navigator.userAgent ) ) {
document.documentElement.style.overflow = 'scroll';
document.body.style.height = '120%';
}
else {
document.documentElement.style.overflow = '';
@@ -884,12 +986,16 @@
availableWidth -= ( availableHeight * config.margin );
availableHeight -= ( availableHeight * config.margin );
// Dimensions of the content
var slideWidth = config.width,
- slideHeight = config.height;
+ slideHeight = config.height,
+ slidePadding = 20; // TODO Dig this out of DOM
+ // Layout the contents of the slides
+ layoutSlideContents( config.width, config.height, slidePadding );
+
// Slide width may be a percentage of available width
if( typeof slideWidth === 'string' && /%$/.test( slideWidth ) ) {
slideWidth = parseInt( slideWidth, 10 ) / 100 * availableWidth;
}
@@ -913,17 +1019,11 @@
if( typeof dom.slides.style.zoom !== 'undefined' && !navigator.userAgent.match( /(iphone|ipod|ipad|android)/gi ) ) {
dom.slides.style.zoom = scale;
}
// Apply scale transform as a fallback
else {
- var transform = 'translate(-50%, -50%) scale('+ scale +') translate(50%, 50%)';
-
- dom.slides.style.WebkitTransform = transform;
- dom.slides.style.MozTransform = transform;
- dom.slides.style.msTransform = transform;
- dom.slides.style.OTransform = transform;
- dom.slides.style.transform = transform;
+ transformElement( dom.slides, 'translate(-50%, -50%) scale('+ scale +') translate(50%, 50%)' );
}
// Select all slides, vertical and horizontal
var slides = toArray( document.querySelectorAll( SLIDES_SELECTOR ) );
@@ -940,11 +1040,11 @@
// children will be
if( slide.classList.contains( 'stack' ) ) {
slide.style.top = 0;
}
else {
- slide.style.top = Math.max( - ( getAbsoluteHeight( slide ) / 2 ) - 20, -slideHeight / 2 ) + 'px';
+ slide.style.top = Math.max( - ( getAbsoluteHeight( slide ) / 2 ) - slidePadding, -slideHeight / 2 ) + 'px';
}
}
else {
slide.style.top = '';
}
@@ -956,10 +1056,42 @@
}
}
/**
+ * Applies layout logic to the contents of all slides in
+ * the presentation.
+ */
+ function layoutSlideContents( width, height, padding ) {
+
+ // Handle sizing of elements with the 'stretch' class
+ toArray( dom.slides.querySelectorAll( 'section > .stretch' ) ).forEach( function( element ) {
+
+ // Determine how much vertical space we can use
+ var remainingHeight = getRemainingHeight( element, ( height - ( padding * 2 ) ) );
+
+ // Consider the aspect ratio of media elements
+ if( /(img|video)/gi.test( element.nodeName ) ) {
+ var nw = element.naturalWidth || element.videoWidth,
+ nh = element.naturalHeight || element.videoHeight;
+
+ var es = Math.min( width / nw, remainingHeight / nh );
+
+ element.style.width = ( nw * es ) + 'px';
+ element.style.height = ( nh * es ) + 'px';
+
+ }
+ else {
+ element.style.width = width + 'px';
+ element.style.height = remainingHeight + 'px';
+ }
+
+ } );
+
+ }
+
+ /**
* Stores the vertical index of a stack so that the same
* vertical slide can be selected when navigating to and
* from the stack.
*
* @param {HTMLElement} stack The vertical stack element
@@ -1008,55 +1140,50 @@
// Don't auto-slide while in overview mode
cancelAutoSlide();
var wasActive = dom.wrapper.classList.contains( 'overview' );
+ // Vary the depth of the overview based on screen size
+ var depth = window.innerWidth < 400 ? 1000 : 2500;
+
dom.wrapper.classList.add( 'overview' );
- dom.wrapper.classList.remove( 'exit-overview' );
+ dom.wrapper.classList.remove( 'overview-deactivating' );
clearTimeout( activateOverviewTimeout );
clearTimeout( deactivateOverviewTimeout );
// Not the pretties solution, but need to let the overview
// class apply first so that slides are measured accurately
// before we can position them
- activateOverviewTimeout = setTimeout( function(){
+ activateOverviewTimeout = setTimeout( function() {
var horizontalSlides = document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR );
for( var i = 0, len1 = horizontalSlides.length; i < len1; i++ ) {
var hslide = horizontalSlides[i],
- hoffset = config.rtl ? -105 : 105,
- htransform = 'translateZ(-2500px) translate(' + ( ( i - indexh ) * hoffset ) + '%, 0%)';
+ hoffset = config.rtl ? -105 : 105;
hslide.setAttribute( 'data-index-h', i );
- hslide.style.display = 'block';
- hslide.style.WebkitTransform = htransform;
- hslide.style.MozTransform = htransform;
- hslide.style.msTransform = htransform;
- hslide.style.OTransform = htransform;
- hslide.style.transform = htransform;
+ // Apply CSS transform
+ transformElement( hslide, 'translateZ(-'+ depth +'px) translate(' + ( ( i - indexh ) * hoffset ) + '%, 0%)' );
+
if( hslide.classList.contains( 'stack' ) ) {
var verticalSlides = hslide.querySelectorAll( 'section' );
for( var j = 0, len2 = verticalSlides.length; j < len2; j++ ) {
var verticalIndex = i === indexh ? indexv : getPreviousVerticalIndex( hslide );
- var vslide = verticalSlides[j],
- vtransform = 'translate(0%, ' + ( ( j - verticalIndex ) * 105 ) + '%)';
+ var vslide = verticalSlides[j];
vslide.setAttribute( 'data-index-h', i );
vslide.setAttribute( 'data-index-v', j );
- vslide.style.display = 'block';
- vslide.style.WebkitTransform = vtransform;
- vslide.style.MozTransform = vtransform;
- vslide.style.msTransform = vtransform;
- vslide.style.OTransform = vtransform;
- vslide.style.transform = vtransform;
+ // Apply CSS transform
+ transformElement( vslide, 'translate(0%, ' + ( ( j - verticalIndex ) * 105 ) + '%)' );
+
// Navigate to this slide on click
vslide.addEventListener( 'click', onOverviewSlideClicked, true );
}
}
@@ -1066,10 +1193,12 @@
hslide.addEventListener( 'click', onOverviewSlideClicked, true );
}
}
+ updateSlidesVisibility();
+
layout();
if( !wasActive ) {
// Notify observers of the overview showing
dispatchEvent( 'overviewshown', {
@@ -1100,33 +1229,23 @@
dom.wrapper.classList.remove( 'overview' );
// Temporarily add a class so that transitions can do different things
// depending on whether they are exiting/entering overview, or just
// moving from slide to slide
- dom.wrapper.classList.add( 'exit-overview' );
+ dom.wrapper.classList.add( 'overview-deactivating' );
deactivateOverviewTimeout = setTimeout( function () {
- dom.wrapper.classList.remove( 'exit-overview' );
- }, 10);
+ dom.wrapper.classList.remove( 'overview-deactivating' );
+ }, 1 );
// Select all slides
- var slides = toArray( document.querySelectorAll( SLIDES_SELECTOR ) );
-
- for( var i = 0, len = slides.length; i < len; i++ ) {
- var element = slides[i];
-
- element.style.display = '';
-
+ toArray( document.querySelectorAll( SLIDES_SELECTOR ) ).forEach( function( slide ) {
// Resets all transforms to use the external styles
- element.style.WebkitTransform = '';
- element.style.MozTransform = '';
- element.style.msTransform = '';
- element.style.OTransform = '';
- element.style.transform = '';
+ transformElement( slide, '' );
- element.removeEventListener( 'click', onOverviewSlideClicked, true );
- }
+ slide.removeEventListener( 'click', onOverviewSlideClicked, true );
+ } );
slide( indexh, indexv );
cueAutoSlide();
@@ -1180,11 +1299,11 @@
function isVerticalSlide( slide ) {
// Prefer slide argument, otherwise use current slide
slide = slide ? slide : currentSlide;
- return slide && !!slide.parentNode.nodeName.match( /section/i );
+ return slide && slide.parentNode && !!slide.parentNode.nodeName.match( /section/i );
}
/**
* Handling the fullscreen functionality via the fullscreen API
@@ -1300,17 +1419,20 @@
var stateBefore = state.concat();
// Reset the state array
state.length = 0;
- var indexhBefore = indexh,
- indexvBefore = indexv;
+ var indexhBefore = indexh || 0,
+ indexvBefore = indexv || 0;
// Activate and transition to the new slide
indexh = updateSlides( HORIZONTAL_SLIDES_SELECTOR, h === undefined ? indexh : h );
indexv = updateSlides( VERTICAL_SLIDES_SELECTOR, v === undefined ? indexv : v );
+ // Update the visibility of slides now that the indices have changed
+ updateSlidesVisibility();
+
layout();
// Apply the new state
stateLoop: for( var i = 0, len = state.length; i < len; i++ ) {
// Check if this state existed on the previous slide. If it
@@ -1336,14 +1458,10 @@
// If the overview is active, re-activate it to update positions
if( isOverview() ) {
activateOverview();
}
- // Update the URL hash after a delay since updating it mid-transition
- // is likely to cause visual lag
- writeURL( 1500 );
-
// Find the current horizontal slide and any possible vertical slides
// within it
var currentHorizontalSlide = horizontalSlides[ indexh ],
currentVerticalSlides = currentHorizontalSlide.querySelectorAll( 'section' );
@@ -1411,10 +1529,13 @@
updateControls();
updateProgress();
updateBackground();
+ // Update the URL hash
+ writeURL();
+
}
/**
* Syncs the presentation with the current DOM. Useful
* when new slides or control elements are added or when
@@ -1479,20 +1600,10 @@
index = Math.max( Math.min( index, slidesLength - 1 ), 0 );
for( var i = 0; i < slidesLength; i++ ) {
var element = slides[i];
- // Optimization; hide all slides that are three or more steps
- // away from the present slide
- if( isOverview() === false ) {
- // The distance loops so that it measures 1 between the first
- // and last slides
- var distance = Math.abs( ( index - i ) % ( slidesLength - 3 ) ) || 0;
-
- element.style.display = distance > 3 ? 'none' : 'block';
- }
-
var reverse = config.rtl && !isVerticalSlide( element );
element.classList.remove( 'past' );
element.classList.remove( 'present' );
element.classList.remove( 'future' );
@@ -1505,10 +1616,17 @@
element.classList.add( reverse ? 'future' : 'past' );
}
else if( i > index ) {
// Any element subsequent to index is given the 'future' class
element.classList.add( reverse ? 'past' : 'future' );
+
+ var fragments = toArray( element.querySelectorAll( '.fragment.visible' ) );
+
+ // No fragments in future slides should be visible ahead of time
+ while( fragments.length ) {
+ fragments.pop().classList.remove( 'visible' );
+ }
}
// If this element contains vertical slides
if( element.querySelector( 'section' ) ) {
element.classList.add( 'stack' );
@@ -1524,20 +1642,22 @@
var slideState = slides[index].getAttribute( 'data-state' );
if( slideState ) {
state = state.concat( slideState.split( ' ' ) );
}
- // If this slide has a data-autoslide attribtue associated use this as
+ // If this slide has a data-autoslide attribute associated use this as
// autoSlide value otherwise use the global configured time
var slideAutoSlide = slides[index].getAttribute( 'data-autoslide' );
if( slideAutoSlide ) {
autoSlide = parseInt( slideAutoSlide, 10 );
}
else {
autoSlide = config.autoSlide;
}
+ cueAutoSlide();
+
}
else {
// Since there are no slides we can't be anywhere beyond the
// zeroth index
index = 0;
@@ -1546,10 +1666,65 @@
return index;
}
/**
+ * Optimization method; hide all slides that are far away
+ * from the present slide.
+ */
+ function updateSlidesVisibility() {
+
+ // Select all slides and convert the NodeList result to
+ // an array
+ var horizontalSlides = toArray( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ),
+ horizontalSlidesLength = horizontalSlides.length,
+ distanceX,
+ distanceY;
+
+ if( horizontalSlidesLength ) {
+
+ // The number of steps away from the present slide that will
+ // be visible
+ var viewDistance = isOverview() ? 10 : config.viewDistance;
+
+ // Limit view distance on weaker devices
+ if( isMobileDevice ) {
+ viewDistance = isOverview() ? 6 : 1;
+ }
+
+ for( var x = 0; x < horizontalSlidesLength; x++ ) {
+ var horizontalSlide = horizontalSlides[x];
+
+ var verticalSlides = toArray( horizontalSlide.querySelectorAll( 'section' ) ),
+ verticalSlidesLength = verticalSlides.length;
+
+ // Loops so that it measures 1 between the first and last slides
+ distanceX = Math.abs( ( indexh - x ) % ( horizontalSlidesLength - viewDistance ) ) || 0;
+
+ // Show the horizontal slide if it's within the view distance
+ horizontalSlide.style.display = distanceX > viewDistance ? 'none' : 'block';
+
+ if( verticalSlidesLength ) {
+
+ var oy = getPreviousVerticalIndex( horizontalSlide );
+
+ for( var y = 0; y < verticalSlidesLength; y++ ) {
+ var verticalSlide = verticalSlides[y];
+
+ distanceY = x === indexh ? Math.abs( indexv - y ) : Math.abs( y - oy );
+
+ verticalSlide.style.display = ( distanceX + distanceY ) > viewDistance ? 'none' : 'block';
+ }
+
+ }
+ }
+
+ }
+
+ }
+
+ /**
* Updates the progress bar to reflect the current slide.
*/
function updateProgress() {
// Update progress if enabled
@@ -1599,53 +1774,50 @@
/**
* Updates the state of all control/navigation arrows.
*/
function updateControls() {
- if ( config.controls && dom.controls ) {
+ var routes = availableRoutes();
+ var fragments = availableFragments();
- var routes = availableRoutes();
- var fragments = availableFragments();
+ // Remove the 'enabled' class from all directions
+ dom.controlsLeft.concat( dom.controlsRight )
+ .concat( dom.controlsUp )
+ .concat( dom.controlsDown )
+ .concat( dom.controlsPrev )
+ .concat( dom.controlsNext ).forEach( function( node ) {
+ node.classList.remove( 'enabled' );
+ node.classList.remove( 'fragmented' );
+ } );
- // Remove the 'enabled' class from all directions
- dom.controlsLeft.concat( dom.controlsRight )
- .concat( dom.controlsUp )
- .concat( dom.controlsDown )
- .concat( dom.controlsPrev )
- .concat( dom.controlsNext ).forEach( function( node ) {
- node.classList.remove( 'enabled' );
- node.classList.remove( 'fragmented' );
- } );
+ // Add the 'enabled' class to the available routes
+ if( routes.left ) dom.controlsLeft.forEach( function( el ) { el.classList.add( 'enabled' ); } );
+ if( routes.right ) dom.controlsRight.forEach( function( el ) { el.classList.add( 'enabled' ); } );
+ if( routes.up ) dom.controlsUp.forEach( function( el ) { el.classList.add( 'enabled' ); } );
+ if( routes.down ) dom.controlsDown.forEach( function( el ) { el.classList.add( 'enabled' ); } );
- // Add the 'enabled' class to the available routes
- if( routes.left ) dom.controlsLeft.forEach( function( el ) { el.classList.add( 'enabled' ); } );
- if( routes.right ) dom.controlsRight.forEach( function( el ) { el.classList.add( 'enabled' ); } );
- if( routes.up ) dom.controlsUp.forEach( function( el ) { el.classList.add( 'enabled' ); } );
- if( routes.down ) dom.controlsDown.forEach( function( el ) { el.classList.add( 'enabled' ); } );
+ // Prev/next buttons
+ if( routes.left || routes.up ) dom.controlsPrev.forEach( function( el ) { el.classList.add( 'enabled' ); } );
+ if( routes.right || routes.down ) dom.controlsNext.forEach( function( el ) { el.classList.add( 'enabled' ); } );
- // Prev/next buttons
- if( routes.left || routes.up ) dom.controlsPrev.forEach( function( el ) { el.classList.add( 'enabled' ); } );
- if( routes.right || routes.down ) dom.controlsNext.forEach( function( el ) { el.classList.add( 'enabled' ); } );
+ // Highlight fragment directions
+ if( currentSlide ) {
- // Highlight fragment directions
- if( currentSlide ) {
+ // Always apply fragment decorator to prev/next buttons
+ if( fragments.prev ) dom.controlsPrev.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } );
+ if( fragments.next ) dom.controlsNext.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } );
- // Always apply fragment decorator to prev/next buttons
- if( fragments.prev ) dom.controlsPrev.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } );
- if( fragments.next ) dom.controlsNext.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } );
-
- // Apply fragment decorators to directional buttons based on
- // what slide axis they are in
- if( isVerticalSlide( currentSlide ) ) {
- if( fragments.prev ) dom.controlsUp.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } );
- if( fragments.next ) dom.controlsDown.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } );
- }
- else {
- if( fragments.prev ) dom.controlsLeft.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } );
- if( fragments.next ) dom.controlsRight.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } );
- }
+ // Apply fragment decorators to directional buttons based on
+ // what slide axis they are in
+ if( isVerticalSlide( currentSlide ) ) {
+ if( fragments.prev ) dom.controlsUp.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } );
+ if( fragments.next ) dom.controlsDown.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } );
}
+ else {
+ if( fragments.prev ) dom.controlsLeft.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } );
+ if( fragments.next ) dom.controlsRight.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } );
+ }
}
}
@@ -1801,19 +1973,21 @@
var indices = Reveal.getIndices( element );
slide( indices.h, indices.v );
}
// If the slide doesn't exist, navigate to the current slide
else {
- slide( indexh, indexv );
+ slide( indexh || 0, indexv || 0 );
}
}
else {
// Read the index components of the hash
var h = parseInt( bits[0], 10 ) || 0,
v = parseInt( bits[1], 10 ) || 0;
- slide( h, v );
+ if( h !== indexh || v !== indexv ) {
+ slide( h, v );
+ }
}
}
/**
@@ -1886,12 +2060,13 @@
v = Math.max( toArray( slide.parentNode.querySelectorAll( 'section' ) ).indexOf( slide ), 0 );
}
}
if( !slide && currentSlide ) {
- var visibleFragments = currentSlide.querySelectorAll( '.fragment.visible' );
- if( visibleFragments.length ) {
+ var hasFragments = currentSlide.querySelectorAll( '.fragment' ).length > 0;
+ if( hasFragments ) {
+ var visibleFragments = currentSlide.querySelectorAll( '.fragment.visible' );
f = visibleFragments.length;
}
}
return { h: h, v: v, f: f };
@@ -2117,11 +2292,11 @@
// Check if this binding matches the pressed key
if( parseInt( key, 10 ) === event.keyCode ) {
var value = config.keyboard[ key ];
- // Calback function
+ // Callback function
if( typeof value === 'function' ) {
value.apply( null, [ event ] );
}
// String shortcuts to reveal.js API
else if( typeof value === 'string' && typeof Reveal[ value ] === 'function' ) {
@@ -2176,11 +2351,12 @@
// If the input resulted in a triggered action we should prevent
// the browsers default behavior
if( triggered ) {
event.preventDefault();
}
- else if ( event.keyCode === 27 && supports3DTransforms ) {
+ // ESC or O key
+ else if ( ( event.keyCode === 27 || event.keyCode === 79 ) && supports3DTransforms ) {
toggleOverview();
event.preventDefault();
}
@@ -2218,15 +2394,15 @@
* Handler for the 'touchmove' event.
*/
function onTouchMove( event ) {
// Each touch should only trigger one action
- if( !touch.handled ) {
+ if( !touch.captured ) {
var currentX = event.touches[0].clientX;
var currentY = event.touches[0].clientY;
- // If the touch started off with two points and still has
+ // If the touch started with two points and still has
// two active touches; test for the pinch gesture
if( event.touches.length === 2 && touch.startCount === 2 && config.overview ) {
// The current distance in pixels between the two touch points
var currentSpan = distanceBetween( {
@@ -2238,11 +2414,11 @@
} );
// If the span is larger than the desire amount we've got
// ourselves a pinch
if( Math.abs( touch.startSpan - currentSpan ) > touch.threshold ) {
- touch.handled = true;
+ touch.captured = true;
if( currentSpan < touch.startSpan ) {
activateOverview();
}
else {
@@ -2258,27 +2434,38 @@
var deltaX = currentX - touch.startX,
deltaY = currentY - touch.startY;
if( deltaX > touch.threshold && Math.abs( deltaX ) > Math.abs( deltaY ) ) {
- touch.handled = true;
+ touch.captured = true;
navigateLeft();
}
else if( deltaX < -touch.threshold && Math.abs( deltaX ) > Math.abs( deltaY ) ) {
- touch.handled = true;
+ touch.captured = true;
navigateRight();
}
else if( deltaY > touch.threshold ) {
- touch.handled = true;
+ touch.captured = true;
navigateUp();
}
else if( deltaY < -touch.threshold ) {
- touch.handled = true;
+ touch.captured = true;
navigateDown();
}
- event.preventDefault();
+ // If we're embedded, only block touch events if they have
+ // triggered an action
+ if( config.embedded ) {
+ if( touch.captured || isVerticalSlide( currentSlide ) ) {
+ event.preventDefault();
+ }
+ }
+ // Not embedded? Block them all to avoid needless tossing
+ // around of the viewport in iOS
+ else {
+ event.preventDefault();
+ }
}
}
// There's a bug with swiping on some Android devices unless
// the default action is always prevented
@@ -2291,11 +2478,11 @@
/**
* Handler for the 'touchend' event.
*/
function onTouchEnd( event ) {
- touch.handled = false;
+ touch.captured = false;
}
/**
* Convert pointer down to touch start.
@@ -2551,15 +2738,25 @@
return document.querySelector( SLIDES_SELECTOR + '.past' ) == null ? true : false;
},
// Returns true if we're currently on the last slide
isLastSlide: function() {
- if( currentSlide && currentSlide.classList.contains( '.stack' ) ) {
- return currentSlide.querySelector( SLIDES_SELECTOR + '.future' ) == null ? true : false;
+ if( currentSlide ) {
+ // Does this slide has next a sibling?
+ if( currentSlide.nextElementSibling ) return false;
+
+ // If it's vertical, does its parent have a next sibling?
+ if( isVerticalSlide( currentSlide ) && currentSlide.parentNode.nextElementSibling ) return false;
+
+ return true;
}
- else {
- return document.querySelector( SLIDES_SELECTOR + '.future' ) == null ? true : false;
- }
+
+ return false;
+ },
+
+ // Checks if reveal.js has been loaded and is ready for use
+ isReady: function() {
+ return loaded;
},
// Forward event binding to the reveal DOM element
addEventListener: function( type, listener, useCapture ) {
if( 'addEventListener' in window ) {