text: import { isAndroid } from '../utils/device.js'
  
  const SWIPE_THRESHOLD = 40;
  
  /**
   * Controls all touch interactions and navigations for
   * a presentation.
   */
  export default class Touch {
  
  	constructor( Reveal ) {
  
  		this.Reveal = Reveal;
  
  		// Holds information about the currently ongoing touch interaction
  		this.touchStartX = 0;
  		this.touchStartY = 0;
  		this.touchStartCount = 0;
  		this.touchCaptured = false;
  
  		this.onPointerDown = this.onPointerDown.bind( this );
  		this.onPointerMove = this.onPointerMove.bind( this );
  		this.onPointerUp = this.onPointerUp.bind( this );
  		this.onTouchStart = this.onTouchStart.bind( this );
  		this.onTouchMove = this.onTouchMove.bind( this );
  		this.onTouchEnd = this.onTouchEnd.bind( this );
  
  	}
  
  	/**
  	 *
  	 */
  	bind() {
  
  		let revealElement = this.Reveal.getRevealElement();
  
  		if( 'onpointerdown' in window ) {
  			// Use W3C pointer events
  			revealElement.addEventListener( 'pointerdown', this.onPointerDown, false );
  			revealElement.addEventListener( 'pointermove', this.onPointerMove, false );
  			revealElement.addEventListener( 'pointerup', this.onPointerUp, false );
  		}
  		else if( window.navigator.msPointerEnabled ) {
  			// IE 10 uses prefixed version of pointer events
  			revealElement.addEventListener( 'MSPointerDown', this.onPointerDown, false );
  			revealElement.addEventListener( 'MSPointerMove', this.onPointerMove, false );
  			revealElement.addEventListener( 'MSPointerUp', this.onPointerUp, false );
  		}
  		else {
  			// Fall back to touch events
  			revealElement.addEventListener( 'touchstart', this.onTouchStart, false );
  			revealElement.addEventListener( 'touchmove', this.onTouchMove, false );
  			revealElement.addEventListener( 'touchend', this.onTouchEnd, false );
  		}
  
  	}
  
  	/**
  	 *
  	 */
  	unbind() {
  
  		let revealElement = this.Reveal.getRevealElement();
  
  		revealElement.removeEventListener( 'pointerdown', this.onPointerDown, false );
  		revealElement.removeEventListener( 'pointermove', this.onPointerMove, false );
  		revealElement.removeEventListener( 'pointerup', this.onPointerUp, false );
  
  		revealElement.removeEventListener( 'MSPointerDown', this.onPointerDown, false );
  		revealElement.removeEventListener( 'MSPointerMove', this.onPointerMove, false );
  		revealElement.removeEventListener( 'MSPointerUp', this.onPointerUp, false );
  
  		revealElement.removeEventListener( 'touchstart', this.onTouchStart, false );
  		revealElement.removeEventListener( 'touchmove', this.onTouchMove, false );
  		revealElement.removeEventListener( 'touchend', this.onTouchEnd, false );
  
  	}
  
  	/**
  	 * Checks if the target element prevents the triggering of
  	 * swipe navigation.
  	 */
  	isSwipePrevented( target ) {
  
  		while( target && typeof target.hasAttribute === 'function' ) {
  			if( target.hasAttribute( 'data-prevent-swipe' ) ) return true;
  			target = target.parentNode;
  		}
  
  		return false;
  
  	}
  
  	/**
  	 * Handler for the 'touchstart' event, enables support for
  	 * swipe and pinch gestures.
  	 *
  	 * @param {object} event
  	 */
  	onTouchStart( event ) {
  
  		if( this.isSwipePrevented( event.target ) ) return true;
  
  		this.touchStartX = event.touches[0].clientX;
  		this.touchStartY = event.touches[0].clientY;
  		this.touchStartCount = event.touches.length;
  
  	}
  
  	/**
  	 * Handler for the 'touchmove' event.
  	 *
  	 * @param {object} event
  	 */
  	onTouchMove( event ) {
  
  		if( this.isSwipePrevented( event.target ) ) return true;
  
  		let config = this.Reveal.getConfig();
  
  		// Each touch should only trigger one action
  		if( !this.touchCaptured ) {
  			this.Reveal.onUserInput( event );
  
  			let currentX = event.touches[0].clientX;
  			let currentY = event.touches[0].clientY;
  
  			// There was only one touch point, look for a swipe
  			if( event.touches.length === 1 && this.touchStartCount !== 2 ) {
  
  				let availableRoutes = this.Reveal.availableRoutes({ includeFragments: true });
  
  				let deltaX = currentX - this.touchStartX,
  					deltaY = currentY - this.touchStartY;
  
  				if( deltaX > SWIPE_THRESHOLD && Math.abs( deltaX ) > Math.abs( deltaY ) ) {
  					this.touchCaptured = true;
  					if( config.navigationMode === 'linear' ) {
  						if( config.rtl ) {
  							this.Reveal.next();
  						}
  						else {
  							this.Reveal.prev();
  						}
  					}
  					else {
  						this.Reveal.left();
  					}
  				}
  				else if( deltaX < -SWIPE_THRESHOLD && Math.abs( deltaX ) > Math.abs( deltaY ) ) {
  					this.touchCaptured = true;
  					if( config.navigationMode === 'linear' ) {
  						if( config.rtl ) {
  							this.Reveal.prev();
  						}
  						else {
  							this.Reveal.next();
  						}
  					}
  					else {
  						this.Reveal.right();
  					}
  				}
  				else if( deltaY > SWIPE_THRESHOLD && availableRoutes.up ) {
  					this.touchCaptured = true;
  					if( config.navigationMode === 'linear' ) {
  						this.Reveal.prev();
  					}
  					else {
  						this.Reveal.up();
  					}
  				}
  				else if( deltaY < -SWIPE_THRESHOLD && availableRoutes.down ) {
  					this.touchCaptured = true;
  					if( config.navigationMode === 'linear' ) {
  						this.Reveal.next();
  					}
  					else {
  						this.Reveal.down();
  					}
  				}
  
  				// If we're embedded, only block touch events if they have
  				// triggered an action
  				if( config.embedded ) {
  					if( this.touchCaptured || this.Reveal.isVerticalSlide() ) {
  						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
  		else if( isAndroid ) {
  			event.preventDefault();
  		}
  
  	}
  
  	/**
  	 * Handler for the 'touchend' event.
  	 *
  	 * @param {object} event
  	 */
  	onTouchEnd( event ) {
  
  		this.touchCaptured = false;
  
  	}
  
  	/**
  	 * Convert pointer down to touch start.
  	 *
  	 * @param {object} event
  	 */
  	onPointerDown( event ) {
  
  		if( event.pointerType === event.MSPOINTER_TYPE_TOUCH || event.pointerType === "touch" ) {
  			event.touches = [{ clientX: event.clientX, clientY: event.clientY }];
  			this.onTouchStart( event );
  		}
  
  	}
  
  	/**
  	 * Convert pointer move to touch move.
  	 *
  	 * @param {object} event
  	 */
  	onPointerMove( event ) {
  
  		if( event.pointerType === event.MSPOINTER_TYPE_TOUCH || event.pointerType === "touch" )  {
  			event.touches = [{ clientX: event.clientX, clientY: event.clientY }];
  			this.onTouchMove( event );
  		}
  
  	}
  
  	/**
  	 * Convert pointer up to touch end.
  	 *
  	 * @param {object} event
  	 */
  	onPointerUp( event ) {
  
  		if( event.pointerType === event.MSPOINTER_TYPE_TOUCH || event.pointerType === "touch" )  {
  			event.touches = [{ clientX: event.clientX, clientY: event.clientY }];
  			this.onTouchEnd( event );
  		}
  
  	}
  
  }