text: // jquery.event.move
  //
  // 1.3.1
  //
  // Stephen Band
  //
  // Triggers 'movestart', 'move' and 'moveend' events after
  // mousemoves following a mousedown cross a distance threshold,
  // similar to the native 'dragstart', 'drag' and 'dragend' events.
  // Move events are throttled to animation frames. Move event objects
  // have the properties:
  //
  // pageX:
  // pageY:   Page coordinates of pointer.
  // startX:
  // startY:  Page coordinates of pointer at movestart.
  // distX:
  // distY:  Distance the pointer has moved since movestart.
  // deltaX:
  // deltaY:  Distance the finger has moved since last event.
  // velocityX:
  // velocityY:  Average velocity over last few events.
  
  
  (function (module) {
  	if (typeof define === 'function' && define.amd) {
  		// AMD. Register as an anonymous module.
  		define(['jquery'], module);
  	} else {
  		// Browser globals
  		module(jQuery);
  	}
  })(function(jQuery, undefined){
  
  	var // Number of pixels a pressed pointer travels before movestart
  	    // event is fired.
  	    threshold = 6,
  	
  	    add = jQuery.event.add,
  	
  	    remove = jQuery.event.remove,
  
  	    // Just sugar, so we can have arguments in the same order as
  	    // add and remove.
  	    trigger = function(node, type, data) {
  	    	jQuery.event.trigger(type, data, node);
  	    },
  
  	    // Shim for requestAnimationFrame, falling back to timer. See:
  	    // see http://paulirish.com/2011/requestanimationframe-for-smart-animating/
  	    requestFrame = (function(){
  	    	return (
  	    		window.requestAnimationFrame ||
  	    		window.webkitRequestAnimationFrame ||
  	    		window.mozRequestAnimationFrame ||
  	    		window.oRequestAnimationFrame ||
  	    		window.msRequestAnimationFrame ||
  	    		function(fn, element){
  	    			return window.setTimeout(function(){
  	    				fn();
  	    			}, 25);
  	    		}
  	    	);
  	    })(),
  	    
  	    ignoreTags = {
  	    	textarea: true,
  	    	input: true,
  	    	select: true,
  	    	button: true
  	    },
  	    
  	    mouseevents = {
  	    	move: 'mousemove',
  	    	cancel: 'mouseup dragstart',
  	    	end: 'mouseup'
  	    },
  	    
  	    touchevents = {
  	    	move: 'touchmove',
  	    	cancel: 'touchend',
  	    	end: 'touchend'
  	    };
  
  
  	// Constructors
  	
  	function Timer(fn){
  		var callback = fn,
  				active = false,
  				running = false;
  		
  		function trigger(time) {
  			if (active){
  				callback();
  				requestFrame(trigger);
  				running = true;
  				active = false;
  			}
  			else {
  				running = false;
  			}
  		}
  		
  		this.kick = function(fn) {
  			active = true;
  			if (!running) { trigger(); }
  		};
  		
  		this.end = function(fn) {
  			var cb = callback;
  			
  			if (!fn) { return; }
  			
  			// If the timer is not running, simply call the end callback.
  			if (!running) {
  				fn();
  			}
  			// If the timer is running, and has been kicked lately, then
  			// queue up the current callback and the end callback, otherwise
  			// just the end callback.
  			else {
  				callback = active ?
  					function(){ cb(); fn(); } : 
  					fn ;
  				
  				active = true;
  			}
  		};
  	}
  
  
  	// Functions
  	
  	function returnTrue() {
  		return true;
  	}
  	
  	function returnFalse() {
  		return false;
  	}
  	
  	function preventDefault(e) {
  		e.preventDefault();
  	}
  	
  	function preventIgnoreTags(e) {
  		// Don't prevent interaction with form elements.
  		if (ignoreTags[ e.target.tagName.toLowerCase() ]) { return; }
  		
  		e.preventDefault();
  	}
  
  	function isLeftButton(e) {
  		// Ignore mousedowns on any button other than the left (or primary)
  		// mouse button, or when a modifier key is pressed.
  		return (e.which === 1 && !e.ctrlKey && !e.altKey);
  	}
  
  	function identifiedTouch(touchList, id) {
  		var i, l;
  
  		if (touchList.identifiedTouch) {
  			return touchList.identifiedTouch(id);
  		}
  		
  		// touchList.identifiedTouch() does not exist in
  		// webkit yet… we must do the search ourselves...
  		
  		i = -1;
  		l = touchList.length;
  		
  		while (++i < l) {
  			if (touchList[i].identifier === id) {
  				return touchList[i];
  			}
  		}
  	}
  
  	function changedTouch(e, event) {
  		var touch = identifiedTouch(e.changedTouches, event.identifier);
  
  		// This isn't the touch you're looking for.
  		if (!touch) { return; }
  
  		// Chrome Android (at least) includes touches that have not
  		// changed in e.changedTouches. That's a bit annoying. Check
  		// that this touch has changed.
  		if (touch.pageX === event.pageX && touch.pageY === event.pageY) { return; }
  
  		return touch;
  	}
  
  
  	// Handlers that decide when the first movestart is triggered
  	
  	function mousedown(e){
  		var data;
  
  		if (!isLeftButton(e)) { return; }
  
  		data = {
  			target: e.target,
  			startX: e.pageX,
  			startY: e.pageY,
  			timeStamp: e.timeStamp
  		};
  
  		add(document, mouseevents.move, mousemove, data);
  		add(document, mouseevents.cancel, mouseend, data);
  	}
  
  	function mousemove(e){
  		var data = e.data;
  
  		checkThreshold(e, data, e, removeMouse);
  	}
  
  	function mouseend(e) {
  		removeMouse();
  	}
  
  	function removeMouse() {
  		remove(document, mouseevents.move, mousemove);
  		remove(document, mouseevents.cancel, mouseend);
  	}
  
  	function touchstart(e) {
  		var touch, template;
  
  		// Don't get in the way of interaction with form elements.
  		if (ignoreTags[ e.target.tagName.toLowerCase() ]) { return; }
  
  		touch = e.changedTouches[0];
  		
  		// iOS live updates the touch objects whereas Android gives us copies.
  		// That means we can't trust the touchstart object to stay the same,
  		// so we must copy the data. This object acts as a template for
  		// movestart, move and moveend event objects.
  		template = {
  			target: touch.target,
  			startX: touch.pageX,
  			startY: touch.pageY,
  			timeStamp: e.timeStamp,
  			identifier: touch.identifier
  		};
  
  		// Use the touch identifier as a namespace, so that we can later
  		// remove handlers pertaining only to this touch.
  		add(document, touchevents.move + '.' + touch.identifier, touchmove, template);
  		add(document, touchevents.cancel + '.' + touch.identifier, touchend, template);
  	}
  
  	function touchmove(e){
  		var data = e.data,
  		    touch = changedTouch(e, data);
  
  		if (!touch) { return; }
  
  		checkThreshold(e, data, touch, removeTouch);
  	}
  
  	function touchend(e) {
  		var template = e.data,
  		    touch = identifiedTouch(e.changedTouches, template.identifier);
  
  		if (!touch) { return; }
  
  		removeTouch(template.identifier);
  	}
  
  	function removeTouch(identifier) {
  		remove(document, '.' + identifier, touchmove);
  		remove(document, '.' + identifier, touchend);
  	}
  
  
  	// Logic for deciding when to trigger a movestart.
  
  	function checkThreshold(e, template, touch, fn) {
  		var distX = touch.pageX - template.startX,
  		    distY = touch.pageY - template.startY;
  
  		// Do nothing if the threshold has not been crossed.
  		if ((distX * distX) + (distY * distY) < (threshold * threshold)) { return; }
  
  		triggerStart(e, template, touch, distX, distY, fn);
  	}
  
  	function handled() {
  		// this._handled should return false once, and after return true.
  		this._handled = returnTrue;
  		return false;
  	}
  
  	function flagAsHandled(e) {
  		e._handled();
  	}
  
  	function triggerStart(e, template, touch, distX, distY, fn) {
  		var node = template.target,
  		    touches, time;
  
  		touches = e.targetTouches;
  		time = e.timeStamp - template.timeStamp;
  
  		// Create a movestart object with some special properties that
  		// are passed only to the movestart handlers.
  		template.type = 'movestart';
  		template.distX = distX;
  		template.distY = distY;
  		template.deltaX = distX;
  		template.deltaY = distY;
  		template.pageX = touch.pageX;
  		template.pageY = touch.pageY;
  		template.velocityX = distX / time;
  		template.velocityY = distY / time;
  		template.targetTouches = touches;
  		template.finger = touches ?
  			touches.length :
  			1 ;
  
  		// The _handled method is fired to tell the default movestart
  		// handler that one of the move events is bound.
  		template._handled = handled;
  			
  		// Pass the touchmove event so it can be prevented if or when
  		// movestart is handled.
  		template._preventTouchmoveDefault = function() {
  			e.preventDefault();
  		};
  
  		// Trigger the movestart event.
  		trigger(template.target, template);
  
  		// Unbind handlers that tracked the touch or mouse up till now.
  		fn(template.identifier);
  	}
  
  
  	// Handlers that control what happens following a movestart
  
  	function activeMousemove(e) {
  		var event = e.data.event,
  		    timer = e.data.timer;
  
  		updateEvent(event, e, e.timeStamp, timer);
  	}
  
  	function activeMouseend(e) {
  		var event = e.data.event,
  		    timer = e.data.timer;
  		
  		removeActiveMouse();
  
  		endEvent(event, timer, function() {
  			// Unbind the click suppressor, waiting until after mouseup
  			// has been handled.
  			setTimeout(function(){
  				remove(event.target, 'click', returnFalse);
  			}, 0);
  		});
  	}
  
  	function removeActiveMouse(event) {
  		remove(document, mouseevents.move, activeMousemove);
  		remove(document, mouseevents.end, activeMouseend);
  	}
  
  	function activeTouchmove(e) {
  		var event = e.data.event,
  		    timer = e.data.timer,
  		    touch = changedTouch(e, event);
  
  		if (!touch) { return; }
  
  		// Stop the interface from gesturing
  		e.preventDefault();
  
  		event.targetTouches = e.targetTouches;
  		updateEvent(event, touch, e.timeStamp, timer);
  	}
  
  	function activeTouchend(e) {
  		var event = e.data.event,
  		    timer = e.data.timer,
  		    touch = identifiedTouch(e.changedTouches, event.identifier);
  
  		// This isn't the touch you're looking for.
  		if (!touch) { return; }
  
  		removeActiveTouch(event);
  		endEvent(event, timer);
  	}
  
  	function removeActiveTouch(event) {
  		remove(document, '.' + event.identifier, activeTouchmove);
  		remove(document, '.' + event.identifier, activeTouchend);
  	}
  
  
  	// Logic for triggering move and moveend events
  
  	function updateEvent(event, touch, timeStamp, timer) {
  		var time = timeStamp - event.timeStamp;
  
  		event.type = 'move';
  		event.distX =  touch.pageX - event.startX;
  		event.distY =  touch.pageY - event.startY;
  		event.deltaX = touch.pageX - event.pageX;
  		event.deltaY = touch.pageY - event.pageY;
  		
  		// Average the velocity of the last few events using a decay
  		// curve to even out spurious jumps in values.
  		event.velocityX = 0.3 * event.velocityX + 0.7 * event.deltaX / time;
  		event.velocityY = 0.3 * event.velocityY + 0.7 * event.deltaY / time;
  		event.pageX =  touch.pageX;
  		event.pageY =  touch.pageY;
  
  		timer.kick();
  	}
  
  	function endEvent(event, timer, fn) {
  		timer.end(function(){
  			event.type = 'moveend';
  
  			trigger(event.target, event);
  			
  			return fn && fn();
  		});
  	}
  
  
  	// jQuery special event definition
  
  	function setup(data, namespaces, eventHandle) {
  		// Stop the node from being dragged
  		//add(this, 'dragstart.move drag.move', preventDefault);
  		
  		// Prevent text selection and touch interface scrolling
  		//add(this, 'mousedown.move', preventIgnoreTags);
  		
  		// Tell movestart default handler that we've handled this
  		add(this, 'movestart.move', flagAsHandled);
  
  		// Don't bind to the DOM. For speed.
  		return true;
  	}
  	
  	function teardown(namespaces) {
  		remove(this, 'dragstart drag', preventDefault);
  		remove(this, 'mousedown touchstart', preventIgnoreTags);
  		remove(this, 'movestart', flagAsHandled);
  		
  		// Don't bind to the DOM. For speed.
  		return true;
  	}
  	
  	function addMethod(handleObj) {
  		// We're not interested in preventing defaults for handlers that
  		// come from internal move or moveend bindings
  		if (handleObj.namespace === "move" || handleObj.namespace === "moveend") {
  			return;
  		}
  		
  		// Stop the node from being dragged
  		add(this, 'dragstart.' + handleObj.guid + ' drag.' + handleObj.guid, preventDefault, undefined, handleObj.selector);
  		
  		// Prevent text selection and touch interface scrolling
  		add(this, 'mousedown.' + handleObj.guid, preventIgnoreTags, undefined, handleObj.selector);
  	}
  	
  	function removeMethod(handleObj) {
  		if (handleObj.namespace === "move" || handleObj.namespace === "moveend") {
  			return;
  		}
  		
  		remove(this, 'dragstart.' + handleObj.guid + ' drag.' + handleObj.guid);
  		remove(this, 'mousedown.' + handleObj.guid);
  	}
  	
  	jQuery.event.special.movestart = {
  		setup: setup,
  		teardown: teardown,
  		add: addMethod,
  		remove: removeMethod,
  
  		_default: function(e) {
  			var template, data;
  			
  			// If no move events were bound to any ancestors of this
  			// target, high tail it out of here.
  			if (!e._handled()) { return; }
  
  			template = {
  				target: e.target,
  				startX: e.startX,
  				startY: e.startY,
  				pageX: e.pageX,
  				pageY: e.pageY,
  				distX: e.distX,
  				distY: e.distY,
  				deltaX: e.deltaX,
  				deltaY: e.deltaY,
  				velocityX: e.velocityX,
  				velocityY: e.velocityY,
  				timeStamp: e.timeStamp,
  				identifier: e.identifier,
  				targetTouches: e.targetTouches,
  				finger: e.finger
  			};
  
  			data = {
  				event: template,
  				timer: new Timer(function(time){
  					trigger(e.target, template);
  				})
  			};
  			
  			if (e.identifier === undefined) {
  				// We're dealing with a mouse
  				// Stop clicks from propagating during a move
  				add(e.target, 'click', returnFalse);
  				add(document, mouseevents.move, activeMousemove, data);
  				add(document, mouseevents.end, activeMouseend, data);
  			}
  			else {
  				// We're dealing with a touch. Stop touchmove doing
  				// anything defaulty.
  				e._preventTouchmoveDefault();
  				add(document, touchevents.move + '.' + e.identifier, activeTouchmove, data);
  				add(document, touchevents.end + '.' + e.identifier, activeTouchend, data);
  			}
  		}
  	};
  
  	jQuery.event.special.move = {
  		setup: function() {
  			// Bind a noop to movestart. Why? It's the movestart
  			// setup that decides whether other move events are fired.
  			add(this, 'movestart.move', jQuery.noop);
  		},
  		
  		teardown: function() {
  			remove(this, 'movestart.move', jQuery.noop);
  		}
  	};
  	
  	jQuery.event.special.moveend = {
  		setup: function() {
  			// Bind a noop to movestart. Why? It's the movestart
  			// setup that decides whether other move events are fired.
  			add(this, 'movestart.moveend', jQuery.noop);
  		},
  		
  		teardown: function() {
  			remove(this, 'movestart.moveend', jQuery.noop);
  		}
  	};
  
  	add(document, 'mousedown.move', mousedown);
  	add(document, 'touchstart.move', touchstart);
  
  	// Make jQuery copy touch event properties over to the jQuery event
  	// object, if they are not already listed. But only do the ones we
  	// really need. IE7/8 do not have Array#indexOf(), but nor do they
  	// have touch events, so let's assume we can ignore them.
  	// if (typeof Array.prototype.indexOf === 'function') {
  	// 	(function(jQuery, undefined){
  	// 		var props = ["changedTouches", "targetTouches"],
  	// 		    l = props.length;
  	//
  	// 		while (l--) {
  	// 			if (jQuery.event.props.indexOf([l]) === -1) {
  	// 				jQuery.event.props.push(props[l]);
  	// 			}
  	// 		}
  	// 	})(jQuery);
  	// }
  });
  
  // jQuery.event.swipe
  // 0.5
  // Stephen Band
  
  // Dependencies
  // jQuery.event.move 1.2
  
  // One of swipeleft, swiperight, swipeup or swipedown is triggered on
  // moveend, when the move has covered a threshold ratio of the dimension
  // of the target node, or has gone really fast. Threshold and velocity
  // sensitivity changed with:
  //
  // jQuery.event.special.swipe.settings.threshold
  // jQuery.event.special.swipe.settings.sensitivity
  
  (function (module) {
  	if (typeof define === 'function' && define.amd) {
  		// AMD. Register as an anonymous module.
  		define(['jquery'], module);
  	} else {
  		// Browser globals
  		module(jQuery);
  	}
  })(function(jQuery, undefined){
  	var add = jQuery.event.add,
  	   
  	    remove = jQuery.event.remove,
  
  	    // Just sugar, so we can have arguments in the same order as
  	    // add and remove.
  	    trigger = function(node, type, data) {
  	    	jQuery.event.trigger(type, data, node);
  	    },
  
  	    settings = {
  	    	// Ratio of distance over target finger must travel to be
  	    	// considered a swipe.
  	    	threshold: 0.4,
  	    	// Faster fingers can travel shorter distances to be considered
  	    	// swipes. 'sensitivity' controls how much. Bigger is shorter.
  	    	sensitivity: 6
  	    };
  
  	function moveend(e) {
  		var w, h, event;
  
  		w = e.currentTarget.offsetWidth;
  		h = e.currentTarget.offsetHeight;
  
  		// Copy over some useful properties from the move event
  		event = {
  			distX: e.distX,
  			distY: e.distY,
  			velocityX: e.velocityX,
  			velocityY: e.velocityY,
  			finger: e.finger
  		};
  
  		// Find out which of the four directions was swiped
  		if (e.distX > e.distY) {
  			if (e.distX > -e.distY) {
  				if (e.distX/w > settings.threshold || e.velocityX * e.distX/w * settings.sensitivity > 1) {
  					event.type = 'swiperight';
  					trigger(e.currentTarget, event);
  				}
  			}
  			else {
  				if (-e.distY/h > settings.threshold || e.velocityY * e.distY/w * settings.sensitivity > 1) {
  					event.type = 'swipeup';
  					trigger(e.currentTarget, event);
  				}
  			}
  		}
  		else {
  			if (e.distX > -e.distY) {
  				if (e.distY/h > settings.threshold || e.velocityY * e.distY/w * settings.sensitivity > 1) {
  					event.type = 'swipedown';
  					trigger(e.currentTarget, event);
  				}
  			}
  			else {
  				if (-e.distX/w > settings.threshold || e.velocityX * e.distX/w * settings.sensitivity > 1) {
  					event.type = 'swipeleft';
  					trigger(e.currentTarget, event);
  				}
  			}
  		}
  	}
  
  	function getData(node) {
  		var data = jQuery.data(node, 'event_swipe');
  		
  		if (!data) {
  			data = { count: 0 };
  			jQuery.data(node, 'event_swipe', data);
  		}
  		
  		return data;
  	}
  
  	jQuery.event.special.swipe =
  	jQuery.event.special.swipeleft =
  	jQuery.event.special.swiperight =
  	jQuery.event.special.swipeup =
  	jQuery.event.special.swipedown = {
  		setup: function( data, namespaces, eventHandle ) {
  			var data = getData(this);
  
  			// If another swipe event is already setup, don't setup again.
  			if (data.count++ > 0) { return; }
  
  			add(this, 'moveend', moveend);
  
  			return true;
  		},
  
  		teardown: function() {
  			var data = getData(this);
  
  			// If another swipe event is still setup, don't teardown.
  			if (--data.count > 0) { return; }
  
  			remove(this, 'moveend', moveend);
  
  			return true;
  		},
  
  		settings: settings
  	};
  });
  