text: // This is a custom build which contains touch/swipe events support only.
  //
  // To create this build do the following:
  // 1. Go to http://jquerymobile.com/download-builder/
  // 2. Select "Touch" under "Events"
  // 3. click "Build My Download"
  
  /*
  * jQuery Mobile v1.5.0-alpha.1
  * http://jquerymobile.com
  *
  * Copyright jQuery Foundation, Inc. and other contributors
  * Released under the MIT license.
  * http://jquery.org/license
  *
  */
  
  (function ( root, doc, factory ) {
  	if ( typeof define === "function" && define.amd ) {
  		// AMD. Register as an anonymous module.
  		define( [ "jquery" ], function ( $ ) {
  			factory( $, root, doc );
  			return $.mobile;
  		});
  	} else {
  		// Browser globals
  		factory( root.jQuery, root, doc );
  	}
  }( this, document, function ( jQuery, window, document, undefined ) {/*!
   * jQuery Mobile Virtual Mouse @VERSION
   * http://jquerymobile.com
   *
   * Copyright jQuery Foundation and other contributors
   * Released under the MIT license.
   * http://jquery.org/license
   */
  
  //>>label: Virtual Mouse (vmouse) Bindings
  //>>group: Core
  //>>description: Normalizes touch/mouse events.
  //>>docs: http://api.jquerymobile.com/?s=vmouse
  
  // This plugin is an experiment for abstracting away the touch and mouse
  // events so that developers don't have to worry about which method of input
  // the device their document is loaded on supports.
  //
  // The idea here is to allow the developer to register listeners for the
  // basic mouse events, such as mousedown, mousemove, mouseup, and click,
  // and the plugin will take care of registering the correct listeners
  // behind the scenes to invoke the listener at the fastest possible time
  // for that device, while still retaining the order of event firing in
  // the traditional mouse environment, should multiple handlers be registered
  // on the same element for different events.
  //
  // The current version exposes the following virtual events to jQuery bind methods:
  // "vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel"
  
  ( function( factory ) {
  	if ( typeof define === "function" && define.amd ) {
  
  		// AMD. Register as an anonymous module.
  		define( 'vmouse',[ "jquery" ], factory );
  	} else {
  
  		// Browser globals
  		factory( jQuery );
  	}
  } )( function( $ ) {
  
  var dataPropertyName = "virtualMouseBindings",
  	touchTargetPropertyName = "virtualTouchID",
  	touchEventProps = "clientX clientY pageX pageY screenX screenY".split( " " ),
  	virtualEventNames = "vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel".split( " " ),
  	generalProps = ( "altKey bubbles cancelable ctrlKey currentTarget detail eventPhase " +
  		"metaKey relatedTarget shiftKey target timeStamp view which" ).split( " " ),
  	mouseHookProps = $.event.mouseHooks ? $.event.mouseHooks.props : [],
  	mouseEventProps = generalProps.concat( mouseHookProps ),
  	activeDocHandlers = {},
  	resetTimerID = 0,
  	startX = 0,
  	startY = 0,
  	didScroll = false,
  	clickBlockList = [],
  	blockMouseTriggers = false,
  	blockTouchTriggers = false,
  	eventCaptureSupported = "addEventListener" in document,
  	$document = $( document ),
  	nextTouchID = 1,
  	lastTouchID = 0, threshold,
  	i;
  
  $.vmouse = {
  	moveDistanceThreshold: 10,
  	clickDistanceThreshold: 10,
  	resetTimerDuration: 1500,
  	maximumTimeBetweenTouches: 100
  };
  
  function getNativeEvent( event ) {
  
  	while ( event && typeof event.originalEvent !== "undefined" ) {
  		event = event.originalEvent;
  	}
  	return event;
  }
  
  function createVirtualEvent( event, eventType ) {
  
  	var t = event.type,
  		oe, props, ne, prop, ct, touch, i, j, len;
  
  	event = $.Event( event );
  	event.type = eventType;
  
  	oe = event.originalEvent;
  	props = generalProps;
  
  	// addresses separation of $.event.props in to $.event.mouseHook.props and Issue 3280
  	// https://github.com/jquery/jquery-mobile/issues/3280
  	if ( t.search( /^(mouse|click)/ ) > -1 ) {
  		props = mouseEventProps;
  	}
  
  	// copy original event properties over to the new event
  	// this would happen if we could call $.event.fix instead of $.Event
  	// but we don't have a way to force an event to be fixed multiple times
  	if ( oe ) {
  		for ( i = props.length; i; ) {
  			prop = props[ --i ];
  			event[ prop ] = oe[ prop ];
  		}
  	}
  
  	// make sure that if the mouse and click virtual events are generated
  	// without a .which one is defined
  	if ( t.search( /mouse(down|up)|click/ ) > -1 && !event.which ) {
  		event.which = 1;
  	}
  
  	if ( t.search( /^touch/ ) !== -1 ) {
  		ne = getNativeEvent( oe );
  		t = ne.touches;
  		ct = ne.changedTouches;
  		touch = ( t && t.length ) ? t[ 0 ] : ( ( ct && ct.length ) ? ct[ 0 ] : undefined );
  
  		if ( touch ) {
  			for ( j = 0, len = touchEventProps.length; j < len; j++ ) {
  				prop = touchEventProps[ j ];
  				event[ prop ] = touch[ prop ];
  			}
  		}
  	}
  
  	return event;
  }
  
  function getVirtualBindingFlags( element ) {
  
  	var flags = {},
  		b, k;
  
  	while ( element ) {
  
  		b = $.data( element, dataPropertyName );
  
  		for ( k in b ) {
  			if ( b[ k ] ) {
  				flags[ k ] = flags.hasVirtualBinding = true;
  			}
  		}
  		element = element.parentNode;
  	}
  	return flags;
  }
  
  function getClosestElementWithVirtualBinding( element, eventType ) {
  	var b;
  	while ( element ) {
  
  		b = $.data( element, dataPropertyName );
  
  		if ( b && ( !eventType || b[ eventType ] ) ) {
  			return element;
  		}
  		element = element.parentNode;
  	}
  	return null;
  }
  
  function enableTouchBindings() {
  	blockTouchTriggers = false;
  }
  
  function disableTouchBindings() {
  	blockTouchTriggers = true;
  }
  
  function enableMouseBindings() {
  	lastTouchID = 0;
  	clickBlockList.length = 0;
  	blockMouseTriggers = false;
  
  	// When mouse bindings are enabled, our
  	// touch bindings are disabled.
  	disableTouchBindings();
  }
  
  function disableMouseBindings() {
  	// When mouse bindings are disabled, our
  	// touch bindings are enabled.
  	enableTouchBindings();
  }
  
  function clearResetTimer() {
  	if ( resetTimerID ) {
  		clearTimeout( resetTimerID );
  		resetTimerID = 0;
  	}
  }
  
  function startResetTimer() {
  	clearResetTimer();
  	resetTimerID = setTimeout( function() {
  		resetTimerID = 0;
  		enableMouseBindings();
  	}, $.vmouse.resetTimerDuration );
  }
  
  function triggerVirtualEvent( eventType, event, flags ) {
  	var ve;
  
  	if ( ( flags && flags[ eventType ] ) ||
  			( !flags && getClosestElementWithVirtualBinding( event.target, eventType ) ) ) {
  
  		ve = createVirtualEvent( event, eventType );
  
  		$( event.target ).trigger( ve );
  	}
  
  	return ve;
  }
  
  function mouseEventCallback( event ) {
  	var touchID = $.data( event.target, touchTargetPropertyName ),
  		ve;
  
  	// It is unexpected if a click event is received before a touchend
  	// or touchmove event, however this is a known behavior in Mobile
  	// Safari when Mobile VoiceOver (as of iOS 8) is enabled and the user
  	// double taps to activate a link element. In these cases if a touch
  	// event is not received within the maximum time between touches,
  	// re-enable mouse bindings and call the mouse event handler again.
  	if ( event.type === "click" && $.data( event.target, "lastTouchType" ) === "touchstart" ) {
  		setTimeout( function() {
  			if ( $.data( event.target, "lastTouchType" ) === "touchstart" ) {
  				enableMouseBindings();
  				delete $.data( event.target ).lastTouchType;
  				mouseEventCallback( event );
  			}
  		}, $.vmouse.maximumTimeBetweenTouches );
  	}
  
  	if ( !blockMouseTriggers && ( !lastTouchID || lastTouchID !== touchID ) ) {
  		ve = triggerVirtualEvent( "v" + event.type, event );
  		if ( ve ) {
  			if ( ve.isDefaultPrevented() ) {
  				event.preventDefault();
  			}
  			if ( ve.isPropagationStopped() ) {
  				event.stopPropagation();
  			}
  			if ( ve.isImmediatePropagationStopped() ) {
  				event.stopImmediatePropagation();
  			}
  		}
  	}
  }
  
  function handleTouchStart( event ) {
  
  	var touches = getNativeEvent( event ).touches,
  		target, flags, t;
  
  	if ( touches && touches.length === 1 ) {
  
  		target = event.target;
  		flags = getVirtualBindingFlags( target );
  
  		$.data( event.target, "lastTouchType", event.type );
  
  		if ( flags.hasVirtualBinding ) {
  
  			lastTouchID = nextTouchID++;
  			$.data( target, touchTargetPropertyName, lastTouchID );
  
  			clearResetTimer();
  
  			disableMouseBindings();
  			didScroll = false;
  
  			t = getNativeEvent( event ).touches[ 0 ];
  			startX = t.pageX;
  			startY = t.pageY;
  
  			triggerVirtualEvent( "vmouseover", event, flags );
  			triggerVirtualEvent( "vmousedown", event, flags );
  		}
  	}
  }
  
  function handleScroll( event ) {
  	if ( blockTouchTriggers ) {
  		return;
  	}
  
  	if ( !didScroll ) {
  		triggerVirtualEvent( "vmousecancel", event, getVirtualBindingFlags( event.target ) );
  	}
  
  	$.data( event.target, "lastTouchType", event.type );
  
  	didScroll = true;
  	startResetTimer();
  }
  
  function handleTouchMove( event ) {
  	if ( blockTouchTriggers ) {
  		return;
  	}
  
  	var t = getNativeEvent( event ).touches[ 0 ],
  		didCancel = didScroll,
  		moveThreshold = $.vmouse.moveDistanceThreshold,
  		flags = getVirtualBindingFlags( event.target );
  
  	$.data( event.target, "lastTouchType", event.type );
  
  	didScroll = didScroll ||
  		( Math.abs( t.pageX - startX ) > moveThreshold ||
  		Math.abs( t.pageY - startY ) > moveThreshold );
  
  	if ( didScroll && !didCancel ) {
  		triggerVirtualEvent( "vmousecancel", event, flags );
  	}
  
  	triggerVirtualEvent( "vmousemove", event, flags );
  	startResetTimer();
  }
  
  function handleTouchEnd( event ) {
  	if ( blockTouchTriggers || $.data( event.target, "lastTouchType" ) === undefined ) {
  		return;
  	}
  
  	disableTouchBindings();
  	delete $.data( event.target ).lastTouchType;
  
  	var flags = getVirtualBindingFlags( event.target ),
  		ve, t;
  	triggerVirtualEvent( "vmouseup", event, flags );
  
  	if ( !didScroll ) {
  		ve = triggerVirtualEvent( "vclick", event, flags );
  		if ( ve && ve.isDefaultPrevented() ) {
  			// The target of the mouse events that follow the touchend
  			// event don't necessarily match the target used during the
  			// touch. This means we need to rely on coordinates for blocking
  			// any click that is generated.
  			t = getNativeEvent( event ).changedTouches[ 0 ];
  			clickBlockList.push( {
  				touchID: lastTouchID,
  				x: t.clientX,
  				y: t.clientY
  			} );
  
  			// Prevent any mouse events that follow from triggering
  			// virtual event notifications.
  			blockMouseTriggers = true;
  		}
  	}
  	triggerVirtualEvent( "vmouseout", event, flags );
  	didScroll = false;
  
  	startResetTimer();
  }
  
  function hasVirtualBindings( ele ) {
  	var bindings = $.data( ele, dataPropertyName ),
  		k;
  
  	if ( bindings ) {
  		for ( k in bindings ) {
  			if ( bindings[ k ] ) {
  				return true;
  			}
  		}
  	}
  	return false;
  }
  
  function dummyMouseHandler() {
  }
  
  function getSpecialEventObject( eventType ) {
  	var realType = eventType.substr( 1 );
  
  	return {
  		setup: function( /* data, namespace */ ) {
  			// If this is the first virtual mouse binding for this element,
  			// add a bindings object to its data.
  
  			if ( !hasVirtualBindings( this ) ) {
  				$.data( this, dataPropertyName, {} );
  			}
  
  			// If setup is called, we know it is the first binding for this
  			// eventType, so initialize the count for the eventType to zero.
  			var bindings = $.data( this, dataPropertyName );
  			bindings[ eventType ] = true;
  
  			// If this is the first virtual mouse event for this type,
  			// register a global handler on the document.
  
  			activeDocHandlers[ eventType ] = ( activeDocHandlers[ eventType ] || 0 ) + 1;
  
  			if ( activeDocHandlers[ eventType ] === 1 ) {
  				$document.bind( realType, mouseEventCallback );
  			}
  
  			// Some browsers, like Opera Mini, won't dispatch mouse/click events
  			// for elements unless they actually have handlers registered on them.
  			// To get around this, we register dummy handlers on the elements.
  
  			$( this ).bind( realType, dummyMouseHandler );
  
  			// For now, if event capture is not supported, we rely on mouse handlers.
  			if ( eventCaptureSupported ) {
  				// If this is the first virtual mouse binding for the document,
  				// register our touchstart handler on the document.
  
  				activeDocHandlers[ "touchstart" ] = ( activeDocHandlers[ "touchstart" ] || 0 ) + 1;
  
  				if ( activeDocHandlers[ "touchstart" ] === 1 ) {
  					$document.bind( "touchstart", handleTouchStart )
  						.bind( "touchend", handleTouchEnd )
  
  						// On touch platforms, touching the screen and then dragging your finger
  						// causes the window content to scroll after some distance threshold is
  						// exceeded. On these platforms, a scroll prevents a click event from being
  						// dispatched, and on some platforms, even the touchend is suppressed. To
  						// mimic the suppression of the click event, we need to watch for a scroll
  						// event. Unfortunately, some platforms like iOS don't dispatch scroll
  						// events until *AFTER* the user lifts their finger (touchend). This means
  						// we need to watch both scroll and touchmove events to figure out whether
  						// or not a scroll happenens before the touchend event is fired.
  
  						.bind( "touchmove", handleTouchMove )
  						.bind( "scroll", handleScroll );
  				}
  			}
  		},
  
  		teardown: function( /* data, namespace */ ) {
  			// If this is the last virtual binding for this eventType,
  			// remove its global handler from the document.
  
  			--activeDocHandlers[eventType];
  
  			if ( !activeDocHandlers[ eventType ] ) {
  				$document.unbind( realType, mouseEventCallback );
  			}
  
  			if ( eventCaptureSupported ) {
  				// If this is the last virtual mouse binding in existence,
  				// remove our document touchstart listener.
  
  				--activeDocHandlers["touchstart"];
  
  				if ( !activeDocHandlers[ "touchstart" ] ) {
  					$document.unbind( "touchstart", handleTouchStart )
  						.unbind( "touchmove", handleTouchMove )
  						.unbind( "touchend", handleTouchEnd )
  						.unbind( "scroll", handleScroll );
  				}
  			}
  
  			var $this = $( this ),
  				bindings = $.data( this, dataPropertyName );
  
  			// teardown may be called when an element was
  			// removed from the DOM. If this is the case,
  			// jQuery core may have already stripped the element
  			// of any data bindings so we need to check it before
  			// using it.
  			if ( bindings ) {
  				bindings[ eventType ] = false;
  			}
  
  			// Unregister the dummy event handler.
  
  			$this.unbind( realType, dummyMouseHandler );
  
  			// If this is the last virtual mouse binding on the
  			// element, remove the binding data from the element.
  
  			if ( !hasVirtualBindings( this ) ) {
  				$this.removeData( dataPropertyName );
  			}
  		}
  	};
  }
  
  // Expose our custom events to the jQuery bind/unbind mechanism.
  
  for ( i = 0; i < virtualEventNames.length; i++ ) {
  	$.event.special[ virtualEventNames[ i ] ] = getSpecialEventObject( virtualEventNames[ i ] );
  }
  
  // Add a capture click handler to block clicks.
  // Note that we require event capture support for this so if the device
  // doesn't support it, we punt for now and rely solely on mouse events.
  if ( eventCaptureSupported ) {
  	document.addEventListener( "click", function( e ) {
  		var cnt = clickBlockList.length,
  			target = e.target,
  			x, y, ele, i, o, touchID;
  
  		if ( cnt ) {
  			x = e.clientX;
  			y = e.clientY;
  			threshold = $.vmouse.clickDistanceThreshold;
  
  			// The idea here is to run through the clickBlockList to see if
  			// the current click event is in the proximity of one of our
  			// vclick events that had preventDefault() called on it. If we find
  			// one, then we block the click.
  			//
  			// Why do we have to rely on proximity?
  			//
  			// Because the target of the touch event that triggered the vclick
  			// can be different from the target of the click event synthesized
  			// by the browser. The target of a mouse/click event that is synthesized
  			// from a touch event seems to be implementation specific. For example,
  			// some browsers will fire mouse/click events for a link that is near
  			// a touch event, even though the target of the touchstart/touchend event
  			// says the user touched outside the link. Also, it seems that with most
  			// browsers, the target of the mouse/click event is not calculated until the
  			// time it is dispatched, so if you replace an element that you touched
  			// with another element, the target of the mouse/click will be the new
  			// element underneath that point.
  			//
  			// Aside from proximity, we also check to see if the target and any
  			// of its ancestors were the ones that blocked a click. This is necessary
  			// because of the strange mouse/click target calculation done in the
  			// Android 2.1 browser, where if you click on an element, and there is a
  			// mouse/click handler on one of its ancestors, the target will be the
  			// innermost child of the touched element, even if that child is no where
  			// near the point of touch.
  
  			ele = target;
  
  			while ( ele ) {
  				for ( i = 0; i < cnt; i++ ) {
  					o = clickBlockList[ i ];
  					touchID = 0;
  
  					if ( ( ele === target && Math.abs( o.x - x ) < threshold && Math.abs( o.y - y ) < threshold ) ||
  							$.data( ele, touchTargetPropertyName ) === o.touchID ) {
  						// XXX: We may want to consider removing matches from the block list
  						//      instead of waiting for the reset timer to fire.
  						e.preventDefault();
  						e.stopPropagation();
  						return;
  					}
  				}
  				ele = ele.parentNode;
  			}
  		}
  	}, true );
  }
  } );
  
  /*!
   * jQuery Mobile Namespace @VERSION
   * http://jquerymobile.com
   *
   * Copyright jQuery Foundation and other contributors
   * Released under the MIT license.
   * http://jquery.org/license
   */
  
  //>>label: Namespace
  //>>group: Core
  //>>description: The mobile namespace on the jQuery object
  
  ( function( factory ) {
  	if ( typeof define === "function" && define.amd ) {
  
  		// AMD. Register as an anonymous module.
  		define( 'ns',[ "jquery" ], factory );
  	} else {
  
  		// Browser globals
  		factory( jQuery );
  	}
  } )( function( $ ) {
  
  $.mobile = { version: "@VERSION" };
  
  return $.mobile;
  } );
  
  /*!
   * jQuery Mobile Touch Support Test @VERSION
   * http://jquerymobile.com
   *
   * Copyright jQuery Foundation and other contributors
   * Released under the MIT license.
   * http://jquery.org/license
   */
  
  //>>label: Touch support test
  //>>group: Core
  //>>description: Touch feature test
  
  ( function( factory ) {
  	if ( typeof define === "function" && define.amd ) {
  
  		// AMD. Register as an anonymous module.
  		define( 'support/touch',[
  			"jquery",
  			"../ns" ], factory );
  	} else {
  
  		// Browser globals
  		factory( jQuery );
  	}
  } )( function( $ ) {
  
  var support = {
  	touch: "ontouchend" in document
  };
  
  $.mobile.support = $.mobile.support || {};
  $.extend( $.support, support );
  $.extend( $.mobile.support, support );
  
  return $.support;
  } );
  
  /*!
   * jQuery Mobile Touch Events @VERSION
   * http://jquerymobile.com
   *
   * Copyright jQuery Foundation and other contributors
   * Released under the MIT license.
   * http://jquery.org/license
   */
  
  //>>label: Touch
  //>>group: Events
  //>>description: Touch events including: touchstart, touchmove, touchend, tap, taphold, swipe, swipeleft, swiperight
  
  ( function( factory ) {
  	if ( typeof define === "function" && define.amd ) {
  
  		// AMD. Register as an anonymous module.
  		define( 'events/touch',[
  			"jquery",
  			"../vmouse",
  			"../support/touch" ], factory );
  	} else {
  
  		// Browser globals
  		factory( jQuery );
  	}
  } )( function( $ ) {
  var $document = $( document ),
  	supportTouch = $.mobile.support.touch,
  	touchStartEvent = supportTouch ? "touchstart" : "mousedown",
  	touchStopEvent = supportTouch ? "touchend" : "mouseup",
  	touchMoveEvent = supportTouch ? "touchmove" : "mousemove";
  
  // setup new event shortcuts
  $.each( ( "touchstart touchmove touchend " +
  "tap taphold " +
  "swipe swipeleft swiperight" ).split( " " ), function( i, name ) {
  
  	$.fn[ name ] = function( fn ) {
  		return fn ? this.bind( name, fn ) : this.trigger( name );
  	};
  
  	// jQuery < 1.8
  	if ( $.attrFn ) {
  		$.attrFn[ name ] = true;
  	}
  } );
  
  function triggerCustomEvent( obj, eventType, event, bubble ) {
  	var originalType = event.type;
  	event.type = eventType;
  	if ( bubble ) {
  		$.event.trigger( event, undefined, obj );
  	} else {
  		$.event.dispatch.call( obj, event );
  	}
  	event.type = originalType;
  }
  
  // also handles taphold
  $.event.special.tap = {
  	tapholdThreshold: 750,
  	emitTapOnTaphold: true,
  	setup: function() {
  		var thisObject = this,
  			$this = $( thisObject ),
  			isTaphold = false;
  
  		$this.bind( "vmousedown", function( event ) {
  			isTaphold = false;
  			if ( event.which && event.which !== 1 ) {
  				return true;
  			}
  
  			var origTarget = event.target,
  				timer, clickHandler;
  
  			function clearTapTimer() {
  				if ( timer ) {
  					$this.bind( "vclick", clickHandler );
  					clearTimeout( timer );
  				}
  			}
  
  			function clearTapHandlers() {
  				clearTapTimer();
  
  				$this.unbind( "vclick", clickHandler )
  					.unbind( "vmouseup", clearTapTimer );
  				$document.unbind( "vmousecancel", clearTapHandlers );
  			}
  
  			clickHandler = function( event ) {
  				clearTapHandlers();
  
  				// ONLY trigger a 'tap' event if the start target is
  				// the same as the stop target.
  				if ( !isTaphold && origTarget === event.target ) {
  					triggerCustomEvent( thisObject, "tap", event );
  				} else if ( isTaphold ) {
  					event.preventDefault();
  				}
  			};
  
  			$this.bind( "vmouseup", clearTapTimer );
  
  			$document.bind( "vmousecancel", clearTapHandlers );
  
  			timer = setTimeout( function() {
  				if ( !$.event.special.tap.emitTapOnTaphold ) {
  					isTaphold = true;
  				}
  				timer = 0;
  				triggerCustomEvent( thisObject, "taphold", $.Event( "taphold", { target: origTarget } ) );
  			}, $.event.special.tap.tapholdThreshold );
  		} );
  	},
  	teardown: function() {
  		$( this ).unbind( "vmousedown" ).unbind( "vclick" ).unbind( "vmouseup" );
  		$document.unbind( "vmousecancel" );
  	}
  };
  
  // Also handles swipeleft, swiperight
  $.event.special.swipe = {
  
  	// More than this horizontal displacement, and we will suppress scrolling.
  	scrollSupressionThreshold: 30,
  
  	// More time than this, and it isn't a swipe.
  	durationThreshold: 1000,
  
  	// Swipe horizontal displacement must be more than this.
  	horizontalDistanceThreshold: window.devicePixelRatio >= 2 ? 15 : 30,
  
  	// Swipe vertical displacement must be less than this.
  	verticalDistanceThreshold: window.devicePixelRatio >= 2 ? 15 : 30,
  
  	getLocation: function( event ) {
  		var winPageX = window.pageXOffset,
  			winPageY = window.pageYOffset,
  			x = event.clientX,
  			y = event.clientY;
  
  		if ( event.pageY === 0 && Math.floor( y ) > Math.floor( event.pageY ) ||
  				event.pageX === 0 && Math.floor( x ) > Math.floor( event.pageX ) ) {
  
  			// iOS4 clientX/clientY have the value that should have been
  			// in pageX/pageY. While pageX/page/ have the value 0
  			x = x - winPageX;
  			y = y - winPageY;
  		} else if ( y < ( event.pageY - winPageY ) || x < ( event.pageX - winPageX ) ) {
  
  			// Some Android browsers have totally bogus values for clientX/Y
  			// when scrolling/zooming a page. Detectable since clientX/clientY
  			// should never be smaller than pageX/pageY minus page scroll
  			x = event.pageX - winPageX;
  			y = event.pageY - winPageY;
  		}
  
  		return {
  			x: x,
  			y: y
  		};
  	},
  
  	start: function( event ) {
  		var data = event.originalEvent.touches ?
  				event.originalEvent.touches[ 0 ] : event,
  			location = $.event.special.swipe.getLocation( data );
  		return {
  			time: ( new Date() ).getTime(),
  			coords: [ location.x, location.y ],
  			origin: $( event.target )
  		};
  	},
  
  	stop: function( event ) {
  		var data = event.originalEvent.touches ?
  				event.originalEvent.touches[ 0 ] : event,
  			location = $.event.special.swipe.getLocation( data );
  		return {
  			time: ( new Date() ).getTime(),
  			coords: [ location.x, location.y ]
  		};
  	},
  
  	handleSwipe: function( start, stop, thisObject, origTarget ) {
  		if ( stop.time - start.time < $.event.special.swipe.durationThreshold &&
  				Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.horizontalDistanceThreshold &&
  				Math.abs( start.coords[ 1 ] - stop.coords[ 1 ] ) < $.event.special.swipe.verticalDistanceThreshold ) {
  			var direction = start.coords[ 0 ] > stop.coords[ 0 ] ? "swipeleft" : "swiperight";
  
  			triggerCustomEvent( thisObject, "swipe", $.Event( "swipe", { target: origTarget, swipestart: start, swipestop: stop } ), true );
  			triggerCustomEvent( thisObject, direction, $.Event( direction, { target: origTarget, swipestart: start, swipestop: stop } ), true );
  			return true;
  		}
  		return false;
  
  	},
  
  	// This serves as a flag to ensure that at most one swipe event event is
  	// in work at any given time
  	eventInProgress: false,
  
  	setup: function() {
  		var events,
  			thisObject = this,
  			$this = $( thisObject ),
  			context = {};
  
  		// Retrieve the events data for this element and add the swipe context
  		events = $.data( this, "mobile-events" );
  		if ( !events ) {
  			events = { length: 0 };
  			$.data( this, "mobile-events", events );
  		}
  		events.length++;
  		events.swipe = context;
  
  		context.start = function( event ) {
  
  			// Bail if we're already working on a swipe event
  			if ( $.event.special.swipe.eventInProgress ) {
  				return;
  			}
  			$.event.special.swipe.eventInProgress = true;
  
  			var stop,
  				start = $.event.special.swipe.start( event ),
  				origTarget = event.target,
  				emitted = false;
  
  			context.move = function( event ) {
  				if ( !start || event.isDefaultPrevented() ) {
  					return;
  				}
  
  				stop = $.event.special.swipe.stop( event );
  				if ( !emitted ) {
  					emitted = $.event.special.swipe.handleSwipe( start, stop, thisObject, origTarget );
  					if ( emitted ) {
  
  						// Reset the context to make way for the next swipe event
  						$.event.special.swipe.eventInProgress = false;
  					}
  				}
  				// prevent scrolling
  				if ( Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.scrollSupressionThreshold ) {
  					event.preventDefault();
  				}
  			};
  
  			context.stop = function() {
  				emitted = true;
  
  				// Reset the context to make way for the next swipe event
  				$.event.special.swipe.eventInProgress = false;
  				$document.off( touchMoveEvent, context.move );
  				context.move = null;
  			};
  
  			$document.on( touchMoveEvent, context.move )
  				.one( touchStopEvent, context.stop );
  		};
  		$this.on( touchStartEvent, context.start );
  	},
  
  	teardown: function() {
  		var events, context;
  
  		events = $.data( this, "mobile-events" );
  		if ( events ) {
  			context = events.swipe;
  			delete events.swipe;
  			events.length--;
  			if ( events.length === 0 ) {
  				$.removeData( this, "mobile-events" );
  			}
  		}
  
  		if ( context ) {
  			if ( context.start ) {
  				$( this ).off( touchStartEvent, context.start );
  			}
  			if ( context.move ) {
  				$document.off( touchMoveEvent, context.move );
  			}
  			if ( context.stop ) {
  				$document.off( touchStopEvent, context.stop );
  			}
  		}
  	}
  };
  $.each( {
  	taphold: "tap",
  	swipeleft: "swipe.left",
  	swiperight: "swipe.right"
  }, function( event, sourceEvent ) {
  
  	$.event.special[ event ] = {
  		setup: function() {
  			$( this ).bind( sourceEvent, $.noop );
  		},
  		teardown: function() {
  			$( this ).unbind( sourceEvent );
  		}
  	};
  } );
  
  return $.event.special;
  } );
  
  
  
  }));
  