|  | @@ -53,7 +53,18 @@ var Reveal = (function(){
 | 
	
		
			
				|  |  |  		mouseWheelTimeout = 0,
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		// Delays updates to the URL due to a Chrome thumbnailer bug
 | 
	
		
			
				|  |  | -		writeURLTimeout = 0;
 | 
	
		
			
				|  |  | +		writeURLTimeout = 0,
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		// Holds information about the currently ongoing touch input
 | 
	
		
			
				|  |  | +		touch = {
 | 
	
		
			
				|  |  | +			startX: 0,
 | 
	
		
			
				|  |  | +			startY: 0,
 | 
	
		
			
				|  |  | +			startSpan: 0,
 | 
	
		
			
				|  |  | +			startCount: 0,
 | 
	
		
			
				|  |  | +			handled: false,
 | 
	
		
			
				|  |  | +			threshold: 40
 | 
	
		
			
				|  |  | +		};
 | 
	
		
			
				|  |  | +	
 | 
	
		
			
				|  |  |  	
 | 
	
		
			
				|  |  |  	/**
 | 
	
		
			
				|  |  |  	 * Starts up the slideshow by applying configuration
 | 
	
	
		
			
				|  | @@ -79,14 +90,7 @@ var Reveal = (function(){
 | 
	
		
			
				|  |  |  		dom.controlsUp = document.querySelector( '#reveal .controls .up' );
 | 
	
		
			
				|  |  |  		dom.controlsDown = document.querySelector( '#reveal .controls .down' );
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		// Bind all view events
 | 
	
		
			
				|  |  | -		document.addEventListener( 'keydown', onDocumentKeyDown, false );
 | 
	
		
			
				|  |  | -		document.addEventListener( 'touchstart', onDocumentTouchStart, false );
 | 
	
		
			
				|  |  | -		window.addEventListener( 'hashchange', onWindowHashChange, false );
 | 
	
		
			
				|  |  | -		dom.controlsLeft.addEventListener( 'click', preventAndForward( navigateLeft ), false );
 | 
	
		
			
				|  |  | -		dom.controlsRight.addEventListener( 'click', preventAndForward( navigateRight ), false );
 | 
	
		
			
				|  |  | -		dom.controlsUp.addEventListener( 'click', preventAndForward( navigateUp ), false );
 | 
	
		
			
				|  |  | -		dom.controlsDown.addEventListener( 'click', preventAndForward( navigateDown ), false );
 | 
	
		
			
				|  |  | +		addEventListeners();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		// Copy options over to our config object
 | 
	
		
			
				|  |  |  		extend( config, options );
 | 
	
	
		
			
				|  | @@ -135,6 +139,33 @@ var Reveal = (function(){
 | 
	
		
			
				|  |  |  			window.addEventListener( 'load', removeAddressBar, false );
 | 
	
		
			
				|  |  |  			window.addEventListener( 'orientationchange', removeAddressBar, false );
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  | +		
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	function addEventListeners() {
 | 
	
		
			
				|  |  | +		document.addEventListener( 'keydown', onDocumentKeyDown, false );
 | 
	
		
			
				|  |  | +		document.addEventListener( 'touchstart', onDocumentTouchStart, false );
 | 
	
		
			
				|  |  | +		document.addEventListener( 'touchmove', onDocumentTouchMove, false );
 | 
	
		
			
				|  |  | +		document.addEventListener( 'touchend', onDocumentTouchEnd, false );
 | 
	
		
			
				|  |  | +		window.addEventListener( 'hashchange', onWindowHashChange, false );
 | 
	
		
			
				|  |  | +		
 | 
	
		
			
				|  |  | +		dom.controlsLeft.addEventListener( 'click', preventAndForward( navigateLeft ), false );
 | 
	
		
			
				|  |  | +		dom.controlsRight.addEventListener( 'click', preventAndForward( navigateRight ), false );
 | 
	
		
			
				|  |  | +		dom.controlsUp.addEventListener( 'click', preventAndForward( navigateUp ), false );
 | 
	
		
			
				|  |  | +		dom.controlsDown.addEventListener( 'click', preventAndForward( navigateDown ), false );
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	function removeEventListeners() {
 | 
	
		
			
				|  |  | +		document.removeEventListener( 'keydown', onDocumentKeyDown, false );
 | 
	
		
			
				|  |  | +		document.removeEventListener( 'touchstart', onDocumentTouchStart, false );
 | 
	
		
			
				|  |  | +		document.removeEventListener( 'touchmove', onDocumentTouchMove, false );
 | 
	
		
			
				|  |  | +		document.removeEventListener( 'touchend', onDocumentTouchEnd, false );
 | 
	
		
			
				|  |  | +		window.removeEventListener( 'hashchange', onWindowHashChange, false );
 | 
	
		
			
				|  |  | +		
 | 
	
		
			
				|  |  | +		dom.controlsLeft.removeEventListener( 'click', preventAndForward( navigateLeft ), false );
 | 
	
		
			
				|  |  | +		dom.controlsRight.removeEventListener( 'click', preventAndForward( navigateRight ), false );
 | 
	
		
			
				|  |  | +		dom.controlsUp.removeEventListener( 'click', preventAndForward( navigateUp ), false );
 | 
	
		
			
				|  |  | +		dom.controlsDown.removeEventListener( 'click', preventAndForward( navigateDown ), false );
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	/**
 | 
	
	
		
			
				|  | @@ -147,6 +178,13 @@ var Reveal = (function(){
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	function distanceBetween( a, b ) {
 | 
	
		
			
				|  |  | +		var dx = a.x - b.x,
 | 
	
		
			
				|  |  | +			dy = a.y - b.y;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		return Math.sqrt( dx*dx + dy*dy );
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  	/**
 | 
	
		
			
				|  |  |  	 * Prevents an events defaults behavior calls the 
 | 
	
		
			
				|  |  |  	 * specified delegate.
 | 
	
	
		
			
				|  | @@ -224,54 +262,98 @@ var Reveal = (function(){
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  | -	
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  	/**
 | 
	
		
			
				|  |  | -	 * Handler for the document level 'touchstart' event.
 | 
	
		
			
				|  |  | -	 * 
 | 
	
		
			
				|  |  | -	 * This enables very basic tap interaction for touch
 | 
	
		
			
				|  |  | -	 * devices. Added mainly for performance testing of 3D
 | 
	
		
			
				|  |  | -	 * transforms on iOS but was so happily surprised with
 | 
	
		
			
				|  |  | -	 * how smoothly it runs so I left it in here. Apple +1
 | 
	
		
			
				|  |  | -	 * 
 | 
	
		
			
				|  |  | -	 * @param {Object} event
 | 
	
		
			
				|  |  | +	 * Handler for the document level 'touchstart' event,
 | 
	
		
			
				|  |  | +	 * enables support for swipe and pinch gestures.
 | 
	
		
			
				|  |  |  	 */
 | 
	
		
			
				|  |  |  	function onDocumentTouchStart( event ) {
 | 
	
		
			
				|  |  | -		// We're only interested in one point taps
 | 
	
		
			
				|  |  | -		if (event.touches.length === 1) {
 | 
	
		
			
				|  |  | -			// Never prevent taps on anchors and images
 | 
	
		
			
				|  |  | -			if( event.target.tagName.toLowerCase() === 'a' || event.target.tagName.toLowerCase() === 'img' ) {
 | 
	
		
			
				|  |  | -				return;
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | -			
 | 
	
		
			
				|  |  | -			event.preventDefault();
 | 
	
		
			
				|  |  | -			
 | 
	
		
			
				|  |  | -			var point = {
 | 
	
		
			
				|  |  | -				x: event.touches[0].clientX,
 | 
	
		
			
				|  |  | -				y: event.touches[0].clientY
 | 
	
		
			
				|  |  | -			};
 | 
	
		
			
				|  |  | -			
 | 
	
		
			
				|  |  | -			// Define the extent of the areas that may be tapped
 | 
	
		
			
				|  |  | -			// to navigate
 | 
	
		
			
				|  |  | -			var wt = window.innerWidth * 0.3;
 | 
	
		
			
				|  |  | -			var ht = window.innerHeight * 0.3;
 | 
	
		
			
				|  |  | -			
 | 
	
		
			
				|  |  | -			if( point.x < wt ) {
 | 
	
		
			
				|  |  | -				navigateLeft();
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | -			else if( point.x > window.innerWidth - wt ) {
 | 
	
		
			
				|  |  | -				navigateRight();
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | -			else if( point.y < ht ) {
 | 
	
		
			
				|  |  | -				navigateUp();
 | 
	
		
			
				|  |  | +		touch.startX = event.touches[0].clientX;
 | 
	
		
			
				|  |  | +		touch.startY = event.touches[0].clientY;
 | 
	
		
			
				|  |  | +		touch.startCount = event.touches.length;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		// If there's two touches we need to memorize the distance 
 | 
	
		
			
				|  |  | +		// between those two points to detect pinching
 | 
	
		
			
				|  |  | +		if( event.touches.length === 2 ) {
 | 
	
		
			
				|  |  | +			touch.startSpan = distanceBetween( {
 | 
	
		
			
				|  |  | +				x: event.touches[1].clientX,
 | 
	
		
			
				|  |  | +				y: event.touches[1].clientY
 | 
	
		
			
				|  |  | +			}, {
 | 
	
		
			
				|  |  | +				x: touch.startX,
 | 
	
		
			
				|  |  | +				y: touch.startY
 | 
	
		
			
				|  |  | +			} );
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	
 | 
	
		
			
				|  |  | +	/**
 | 
	
		
			
				|  |  | +	 * Handler for the document level 'touchmove' event.
 | 
	
		
			
				|  |  | +	 */
 | 
	
		
			
				|  |  | +	function onDocumentTouchMove( event ) {
 | 
	
		
			
				|  |  | +		// Each touch should only trigger one action
 | 
	
		
			
				|  |  | +		if( !touch.handled ) {
 | 
	
		
			
				|  |  | +			var currentX = event.touches[0].clientX;
 | 
	
		
			
				|  |  | +			var currentY = event.touches[0].clientY;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +			// If the touch started off with two points and still has 
 | 
	
		
			
				|  |  | +			// two active touches; test for the pinch gesture
 | 
	
		
			
				|  |  | +			if( event.touches.length === 2 && touch.startCount === 2 ) {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +				// The current distance in pixels between the two touch points
 | 
	
		
			
				|  |  | +				var currentSpan = distanceBetween( {
 | 
	
		
			
				|  |  | +					x: event.touches[1].clientX,
 | 
	
		
			
				|  |  | +					y: event.touches[1].clientY
 | 
	
		
			
				|  |  | +				}, {
 | 
	
		
			
				|  |  | +					x: touch.startX,
 | 
	
		
			
				|  |  | +					y: touch.startY
 | 
	
		
			
				|  |  | +				} );
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +				// 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;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +					if( currentSpan < touch.startSpan ) {
 | 
	
		
			
				|  |  | +						activateOverview();
 | 
	
		
			
				|  |  | +					}
 | 
	
		
			
				|  |  | +					else {
 | 
	
		
			
				|  |  | +						deactivateOverview();
 | 
	
		
			
				|  |  | +					}
 | 
	
		
			
				|  |  | +				}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  			}
 | 
	
		
			
				|  |  | -			else if( point.y > window.innerHeight - ht ) {
 | 
	
		
			
				|  |  | -				navigateDown();
 | 
	
		
			
				|  |  | +			// There was only one touch point, look for a swipe
 | 
	
		
			
				|  |  | +			else if( event.touches.length === 1 ) {
 | 
	
		
			
				|  |  | +				var deltaX = currentX - touch.startX,
 | 
	
		
			
				|  |  | +					deltaY = currentY - touch.startY;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +				if( deltaX > touch.threshold && Math.abs( deltaX ) > Math.abs( deltaY ) ) {
 | 
	
		
			
				|  |  | +					touch.handled = true;
 | 
	
		
			
				|  |  | +					navigateLeft();
 | 
	
		
			
				|  |  | +				} 
 | 
	
		
			
				|  |  | +				else if( deltaX < -touch.threshold && Math.abs( deltaX ) > Math.abs( deltaY ) ) {
 | 
	
		
			
				|  |  | +					touch.handled = true;
 | 
	
		
			
				|  |  | +					navigateRight();
 | 
	
		
			
				|  |  | +				} 
 | 
	
		
			
				|  |  | +				else if( deltaY > touch.threshold ) {
 | 
	
		
			
				|  |  | +					touch.handled = true;
 | 
	
		
			
				|  |  | +					navigateUp();
 | 
	
		
			
				|  |  | +				} 
 | 
	
		
			
				|  |  | +				else if( deltaY < -touch.threshold ) {
 | 
	
		
			
				|  |  | +					touch.handled = true;
 | 
	
		
			
				|  |  | +					navigateDown();
 | 
	
		
			
				|  |  | +				}
 | 
	
		
			
				|  |  |  			}
 | 
	
		
			
				|  |  | -			
 | 
	
		
			
				|  |  | -			slide();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +			event.preventDefault();
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	/**
 | 
	
		
			
				|  |  | +	 * Handler for the document level 'touchend' event.
 | 
	
		
			
				|  |  | +	 */
 | 
	
		
			
				|  |  | +	function onDocumentTouchEnd( event ) {
 | 
	
		
			
				|  |  | +		touch.handled = false;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	/**
 | 
	
		
			
				|  |  |  	 * Handles mouse wheel scrolling, throttled to avoid 
 | 
	
	
		
			
				|  | @@ -326,6 +408,7 @@ var Reveal = (function(){
 | 
	
		
			
				|  |  |  	 * can't be improved.
 | 
	
		
			
				|  |  |  	 */
 | 
	
		
			
				|  |  |  	function activateOverview() {
 | 
	
		
			
				|  |  | +		
 | 
	
		
			
				|  |  |  		dom.wrapper.classList.add( 'overview' );
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		var horizontalSlides = Array.prototype.slice.call( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) );
 | 
	
	
		
			
				|  | @@ -341,12 +424,12 @@ var Reveal = (function(){
 | 
	
		
			
				|  |  |  			hslide.style.msTransform = htransform;
 | 
	
		
			
				|  |  |  			hslide.style.OTransform = htransform;
 | 
	
		
			
				|  |  |  			hslide.style.transform = htransform;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | +		
 | 
	
		
			
				|  |  |  			if( !hslide.classList.contains( 'stack' ) ) {
 | 
	
		
			
				|  |  |  				// Navigate to this slide on click
 | 
	
		
			
				|  |  |  				hslide.addEventListener( 'click', onOverviewSlideClicked, true );
 | 
	
		
			
				|  |  |  			}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | +	
 | 
	
		
			
				|  |  |  			var verticalSlides = Array.prototype.slice.call( hslide.querySelectorAll( 'section' ) );
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  			for( var j = 0, len2 = verticalSlides.length; j < len2; j++ ) {
 | 
	
	
		
			
				|  | @@ -365,6 +448,7 @@ var Reveal = (function(){
 | 
	
		
			
				|  |  |  				// Navigate to this slide on click
 | 
	
		
			
				|  |  |  				vslide.addEventListener( 'click', onOverviewSlideClicked, true );
 | 
	
		
			
				|  |  |  			}
 | 
	
		
			
				|  |  | +			
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  	
 | 
	
	
		
			
				|  | @@ -786,6 +870,18 @@ var Reveal = (function(){
 | 
	
		
			
				|  |  |  			availableRoutes().down ? navigateDown() : navigateRight();
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/**
 | 
	
		
			
				|  |  | +	 * Toggles the slide overview mode on and off.
 | 
	
		
			
				|  |  | +	 */
 | 
	
		
			
				|  |  | +	function toggleOverview() {
 | 
	
		
			
				|  |  | +		if( overviewIsActive() ) {
 | 
	
		
			
				|  |  | +			deactivateOverview();
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +		else {
 | 
	
		
			
				|  |  | +			activateOverview();
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  |  	
 | 
	
		
			
				|  |  |  	// Expose some methods publicly
 | 
	
		
			
				|  |  |  	return {
 | 
	
	
		
			
				|  | @@ -795,6 +891,7 @@ var Reveal = (function(){
 | 
	
		
			
				|  |  |  		navigateRight: navigateRight,
 | 
	
		
			
				|  |  |  		navigateUp: navigateUp,
 | 
	
		
			
				|  |  |  		navigateDown: navigateDown,
 | 
	
		
			
				|  |  | +		toggleOverview: toggleOverview,
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		// Forward event binding to the reveal DOM element
 | 
	
		
			
				|  |  |  		addEventListener: function( type, listener, useCapture ) {
 |