// Enable the slide overview mode
overview: true,
- // Vertical centering of slides
+ // Vertical centring of slides
center: true,
// Enables touch navigation on devices with touch input
// Transition style
transition: 'default', // default/cube/page/concave/zoom/linear/fade/none
+ // Transition speed
+ transitionSpeed: 'default', // default/fast/slow
+
// Script dependencies to load
dependencies: []
},
- // Stores if the next slide should be shown automatically
- // after n milliseconds
- autoSlide = config.autoSlide,
+ // The current auto-slide duration
+ autoSlide = 0,
- // The horizontal and verical index of the currently active slide
+ // The horizontal and vertical index of the currently active slide
indexh = 0,
indexv = 0,
dom.slides = document.querySelector( '.reveal .slides' );
// Progress bar
- if( !dom.wrapper.querySelector( '.progress' ) && config.progress ) {
+ if( !dom.wrapper.querySelector( '.progress' ) ) {
var progressElement = document.createElement( 'div' );
progressElement.classList.add( 'progress' );
progressElement.innerHTML = '<span></span>';
}
// Arrow controls
- if( !dom.wrapper.querySelector( '.controls' ) && config.controls ) {
+ if( !dom.wrapper.querySelector( '.controls' ) ) {
var controlsElement = document.createElement( 'aside' );
controlsElement.classList.add( 'controls' );
controlsElement.innerHTML = '<div class="navigate-left"></div>' +
}
}
- // Called once synchronous scritps finish loading
+ // Called once synchronous scripts finish loading
function proceed() {
if( scriptsAsync.length ) {
// Load asynchronous scripts
// Make sure we've got all the DOM elements we need
setupDOM();
- // Subscribe to input
- addEventListeners();
-
// Updates the presentation to match the current configuration values
configure();
// Read the initial hash
readURL();
- // Start auto-sliding if it's enabled
- cueAutoSlide();
-
// Notify listeners that the presentation is ready but use a 1ms
// timeout to ensure it's not fired synchronously after #initialize()
setTimeout( function() {
dom.wrapper.classList.add( config.transition );
- dom.controls.style.display = ( config.controls && dom.controls ) ? 'block' : 'none';
- dom.progress.style.display = ( config.progress && dom.progress ) ? 'block' : 'none';
+ dom.wrapper.setAttribute( 'data-transition-speed', config.transitionSpeed );
+
+ if( dom.controls ) {
+ dom.controls.style.display = ( config.controls && dom.controls ) ? 'block' : 'none';
+ }
+
+ if( dom.progress ) {
+ dom.progress.style.display = ( config.progress && dom.progress ) ? 'block' : 'none';
+ }
if( config.rtl ) {
dom.wrapper.classList.add( 'rtl' );
}
}
+ postConfigure();
+
+ }
+
+ /**
+ * Updates various parts of the presentatio after the
+ * configuration has changed.
+ */
+ function postConfigure() {
+
+ // Subscribe to input
+ removeEventListeners();
+ addEventListeners();
+
// Force a layout to make sure the current config is accounted for
layout();
+ // Reflect the current autoSlide value
+ autoSlide = config.autoSlide;
+
+ // Start auto-sliding if it's enabled
+ cueAutoSlide();
+
}
/**
window.addEventListener( 'resize', onWindowResize, false );
if( config.touch ) {
- document.addEventListener( 'touchstart', onDocumentTouchStart, false );
- document.addEventListener( 'touchmove', onDocumentTouchMove, false );
- document.addEventListener( 'touchend', onDocumentTouchEnd, false );
+ dom.wrapper.addEventListener( 'touchstart', onTouchStart, false );
+ dom.wrapper.addEventListener( 'touchmove', onTouchMove, false );
+ dom.wrapper.addEventListener( 'touchend', onTouchEnd, false );
+
+ // Support pointer-style touch interaction as well
+ if( window.navigator.msPointerEnabled ) {
+ dom.wrapper.addEventListener( 'MSPointerDown', onPointerDown, false );
+ dom.wrapper.addEventListener( 'MSPointerMove', onPointerMove, false );
+ dom.wrapper.addEventListener( 'MSPointerUp', onPointerUp, false );
+ }
}
if( config.keyboard ) {
}
if ( config.controls && dom.controls ) {
- var actionEvent = 'ontouchstart' in window && window.ontouchstart != null ? 'touchstart' : 'click';
- dom.controlsLeft.forEach( function( el ) { el.addEventListener( actionEvent, onNavigateLeftClicked, false ); } );
- dom.controlsRight.forEach( function( el ) { el.addEventListener( actionEvent, onNavigateRightClicked, false ); } );
- dom.controlsUp.forEach( function( el ) { el.addEventListener( actionEvent, onNavigateUpClicked, false ); } );
- dom.controlsDown.forEach( function( el ) { el.addEventListener( actionEvent, onNavigateDownClicked, false ); } );
- dom.controlsPrev.forEach( function( el ) { el.addEventListener( actionEvent, onNavigatePrevClicked, false ); } );
- dom.controlsNext.forEach( function( el ) { el.addEventListener( actionEvent, 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 ); } );
+ } );
}
}
window.removeEventListener( 'hashchange', onWindowHashChange, false );
window.removeEventListener( 'resize', onWindowResize, false );
- if( config.touch ) {
- document.removeEventListener( 'touchstart', onDocumentTouchStart, false );
- document.removeEventListener( 'touchmove', onDocumentTouchMove, false );
- document.removeEventListener( 'touchend', onDocumentTouchEnd, false );
+ dom.wrapper.removeEventListener( 'touchstart', onTouchStart, false );
+ dom.wrapper.removeEventListener( 'touchmove', onTouchMove, false );
+ dom.wrapper.removeEventListener( 'touchend', onTouchEnd, false );
+
+ if( window.navigator.msPointerEnabled ) {
+ dom.wrapper.removeEventListener( 'MSPointerDown', onPointerDown, false );
+ dom.wrapper.removeEventListener( 'MSPointerMove', onPointerMove, false );
+ dom.wrapper.removeEventListener( 'MSPointerUp', onPointerUp, false );
}
if ( config.progress && dom.progress ) {
}
if ( config.controls && dom.controls ) {
- var actionEvent = 'ontouchstart' in window && window.ontouchstart != null ? 'touchstart' : 'click';
- dom.controlsLeft.forEach( function( el ) { el.removeEventListener( actionEvent, onNavigateLeftClicked, false ); } );
- dom.controlsRight.forEach( function( el ) { el.removeEventListener( actionEvent, onNavigateRightClicked, false ); } );
- dom.controlsUp.forEach( function( el ) { el.removeEventListener( actionEvent, onNavigateUpClicked, false ); } );
- dom.controlsDown.forEach( function( el ) { el.removeEventListener( actionEvent, onNavigateDownClicked, false ); } );
- dom.controlsPrev.forEach( function( el ) { el.removeEventListener( actionEvent, onNavigatePrevClicked, false ); } );
- dom.controlsNext.forEach( function( el ) { el.removeEventListener( actionEvent, 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 ); } );
+ } );
}
}
* "data-fragment-index" attribute.
*
* Fragments will be revealed in the order that they are returned by
- * this function, so you can use the index attributes to control the
+ * this function, so you can use the index attributes to control the
* order of fragment appearance.
*
* To maintain a sensible default fragment order, fragments are presumed
return l.getAttribute( 'data-fragment-index' ) - r.getAttribute( 'data-fragment-index');
} );
- return a
+ return a;
}
var availableWidth = dom.wrapper.offsetWidth,
availableHeight = dom.wrapper.offsetHeight;
- // Reduce availabe space by margin
+ // Reduce available space by margin
availableWidth -= ( availableHeight * config.margin );
availableHeight -= ( availableHeight * config.margin );
}
if( config.center ) {
- // Vertical stacks are not centered since their section
+ // Vertical stacks are not centred since their section
// children will be
if( slide.classList.contains( 'stack' ) ) {
slide.style.top = 0;
*/
function setPreviousVerticalIndex( stack, v ) {
- if( stack ) {
+ if( typeof stack === 'object' && typeof stack.setAttribute === 'function' ) {
stack.setAttribute( 'data-previous-indexv', v || 0 );
}
*/
function getPreviousVerticalIndex( stack ) {
- if( stack && stack.classList.contains( 'stack' ) ) {
+ if( typeof stack === 'object' && typeof stack.setAttribute === 'function' && stack.classList.contains( 'stack' ) ) {
return parseInt( stack.getAttribute( 'data-previous-indexv' ) || 0, 10 );
}
function resume() {
var wasPaused = dom.wrapper.classList.contains( 'paused' );
+ dom.wrapper.classList.remove( 'paused' );
cueAutoSlide();
- dom.wrapper.classList.remove( 'paused' );
if( wasPaused ) {
dispatchEvent( 'resumed' );
* @param {int} v Vertical index of the target slide
* @param {int} f Optional index of a fragment within the
* target slide to activate
+ * @param {int} o Optional origin for use in multimaster environments
*/
- function slide( h, v, f ) {
+ function slide( h, v, f, o ) {
// Remember where we were at before
previousSlide = currentSlide;
'indexh': indexh,
'indexv': indexv,
'previousSlide': previousSlide,
- 'currentSlide': currentSlide
+ 'currentSlide': currentSlide,
+ 'origin': o
} );
}
else {
verticalSlides = document.querySelectorAll( VERTICAL_SLIDES_SELECTOR );
return {
- left: indexh > 0,
- right: indexh < horizontalSlides.length - 1,
+ left: indexh > 0 || config.loop,
+ right: indexh < horizontalSlides.length - 1 || config.loop,
up: indexv > 0,
down: indexv < verticalSlides.length - 1
};
function navigateLeft() {
// Prioritize hiding fragments
- if( availableRoutes().left && isOverview() || previousFragment() === false ) {
+ if( availableRoutes().left && ( isOverview() || previousFragment() === false ) ) {
slide( indexh - 1 );
}
function navigateRight() {
// Prioritize revealing fragments
- if( availableRoutes().right && isOverview() || nextFragment() === false ) {
+ if( availableRoutes().right && ( isOverview() || nextFragment() === false ) ) {
slide( indexh + 1 );
}
if( previousSlide ) {
indexv = ( previousSlide.querySelectorAll( 'section' ).length + 1 ) || undefined;
indexh --;
- slide();
+ slide( indexh, indexv );
}
}
}
// Disregard the event if there's a focused element or a
// keyboard modifier key is present
- if( hasFocus || event.shiftKey || event.altKey || event.ctrlKey || event.metaKey ) return;
+ if( hasFocus || (event.shiftKey && event.keyCode !== 32) || event.altKey || event.ctrlKey || event.metaKey ) return;
var triggered = true;
// end
case 35: slide( Number.MAX_VALUE ); break;
// space
- case 32: isOverview() ? deactivateOverview() : navigateNext(); break;
+ case 32: isOverview() ? deactivateOverview() : event.shiftKey ? navigatePrev() : navigateNext(); break;
// return
case 13: isOverview() ? deactivateOverview() : triggered = false; break;
// b, period, Logitech presenter tools "black screen" button
}
/**
- * Handler for the document level 'touchstart' event,
- * enables support for swipe and pinch gestures.
+ * Handler for the 'touchstart' event, enables support for
+ * swipe and pinch gestures.
*/
- function onDocumentTouchStart( event ) {
+ function onTouchStart( event ) {
touch.startX = event.touches[0].clientX;
touch.startY = event.touches[0].clientY;
}
/**
- * Handler for the document level 'touchmove' event.
+ * Handler for the 'touchmove' event.
*/
- function onDocumentTouchMove( event ) {
+ function onTouchMove( event ) {
// Each touch should only trigger one action
if( !touch.handled ) {
}
/**
- * Handler for the document level 'touchend' event.
+ * Handler for the 'touchend' event.
*/
- function onDocumentTouchEnd( event ) {
+ function onTouchEnd( event ) {
touch.handled = false;
}
+ /**
+ * Convert pointer down to touch start.
+ */
+ function onPointerDown( event ) {
+
+ if( event.pointerType === event.MSPOINTER_TYPE_TOUCH ) {
+ event.touches = [{ clientX: event.clientX, clientY: event.clientY }];
+ onTouchStart( event );
+ }
+
+ }
+
+ /**
+ * Convert pointer move to touch move.
+ */
+ function onPointerMove( event ) {
+
+ if( event.pointerType === event.MSPOINTER_TYPE_TOUCH ) {
+ event.touches = [{ clientX: event.clientX, clientY: event.clientY }];
+ onTouchMove( event );
+ }
+
+ }
+
+ /**
+ * Convert pointer up to touch end.
+ */
+ function onPointerUp( event ) {
+
+ if( event.pointerType === event.MSPOINTER_TYPE_TOUCH ) {
+ event.touches = [{ clientX: event.clientX, clientY: event.clientY }];
+ onTouchEnd( event );
+ }
+
+ }
+
/**
* Handles mouse wheel scrolling, throttled to avoid skipping
* multiple slides.
// Forces an update in slide layout
layout: layout,
+ // Returns an object with the available routes as booleans (left/right/top/bottom)
+ availableRoutes: availableRoutes,
+
// Toggles the overview mode on/off
toggleOverview: toggleOverview,
return scale;
},
+ // Returns the current configuration object
+ getConfig: function() {
+ return config;
+ },
+
+ // Returns an index (1-based) of the current fragment
+ getCurrentFragmentIndex : function() {
+ if( currentSlide ) {
+ var visibleFragments = currentSlide.querySelectorAll( '.fragment.visible' );
+
+ if( visibleFragments.length ) {
+ return visibleFragments.length;
+ }
+ }
+ },
+
// Helper method, retrieves query string as a key/value hash
getQueryHash: function() {
var query = {};