;(function() {

	"use strict";

	var Sniff = {
		android:navigator.userAgent.toLowerCase().indexOf("android") > -1
	};

	var matchesSelector = function(el, selector, ctx) {
			ctx = ctx || el.parentNode;
			var possibles = ctx.querySelectorAll(selector);
			for (var i = 0; i < possibles.length; i++) {
				if (possibles[i] === el) {
					return true;
				}
			}
			return false;
		},
		_gel = function(el) { return typeof el == "string" ? document.getElementById(el) : el; },
		_t = function(e) { return e.srcElement || e.target; },
		_d = function(l, fn) {
			for (var i = 0, j = l.length; i < j; i++) {
				if (l[i] == fn) break;
			}
			if (i < l.length) l.splice(i, 1);
		},
		guid = 1,
		//
		// this function generates a guid for every handler, sets it on the handler, then adds
		// it to the associated object's map of handlers for the given event. this is what enables us 
		// to unbind all events of some type, or all events (the second of which can be requested by the user, 
		// but it also used by Mottle when an element is removed.)
		_store = function(obj, event, fn) {
			var g = guid++;
			obj.__ta = obj.__ta || {};
			obj.__ta[event] = obj.__ta[event] || {};
			// store each handler with a unique guid.
			obj.__ta[event][g] = fn;
			// set the guid on the handler.
			fn.__tauid = g;
			return g;
		},
		_unstore = function(obj, event, fn) {
			obj.__ta && obj.__ta[event] && delete obj.__ta[event][fn.__tauid];
			// a handler might have attached extra functions, so we unbind those too.
			if (fn.__taExtra) {
				for (var i = 0; i < fn.__taExtra.length; i++) {
					_unbind(obj, fn.__taExtra[i][0], fn.__taExtra[i][1]);
				}
				fn.__taExtra.length = 0;
			}
			// a handler might have attached an unstore callback
			fn.__taUnstore && fn.__taUnstore();
		},
		_curryChildFilter = function(children, obj, fn, evt) {
			if (children == null) return fn;
			else {
				var c = children.split(","),
					_fn = function(e) {
						_fn.__tauid = fn.__tauid;
						var t = _t(e);
						for (var i = 0; i < c.length; i++) {
							if (matchesSelector(t, c[i], obj)) {
								fn.apply(t, arguments);
							}
						}
					};
				registerExtraFunction(fn, evt, _fn);
				return _fn;
			}
		},
		//
		// registers an 'extra' function on some event listener function we were given - a function that we
		// created and bound to the element as part of our housekeeping, and which we want to unbind and remove
		// whenever the given function is unbound.
		registerExtraFunction = function(fn, evt, newFn) {
			fn.__taExtra = fn.__taExtra || [];
			fn.__taExtra.push([evt, newFn]);
		},
		DefaultHandler = function(obj, evt, fn, children) {
			// TODO: this was here originally because i wanted to handle devices that are both touch AND mouse. however this can cause certain of the helper
			// functions to be bound twice, as - for example - on a nexus 4, both a mouse event and a touch event are fired.  the use case i had in mind
			// was a device such as an Asus touch pad thing, which has a touch pad but can also be controlled with a mouse.
			//if (isMouseDevice)
			//	_bind(obj, evt, _curryChildFilter(children, obj, fn, evt), fn);
			
			if (isTouchDevice && touchMap[evt]) {
				_bind(obj, touchMap[evt], _curryChildFilter(children, obj, fn, touchMap[evt]), fn);
			}
			else
				_bind(obj, evt, _curryChildFilter(children, obj, fn, evt), fn);
		},
		SmartClickHandler = function(obj, evt, fn, children) {
			if (obj.__taSmartClicks == null) {
				var down = function(e) { obj.__tad = _pageLocation(e); },
					up = function(e) { obj.__tau = _pageLocation(e); },
					click = function(e) {
						if (obj.__tad && obj.__tau && obj.__tad[0] === obj.__tau[0] && obj.__tad[1] === obj.__tau[1]) {
							for (var i = 0; i < obj.__taSmartClicks.length; i++)
								obj.__taSmartClicks[i].apply(_t(e), [ e ]);
						}
					};
				DefaultHandler(obj, "mousedown", down, children);
				DefaultHandler(obj, "mouseup", up, children);
				DefaultHandler(obj, "click", click, children);
				obj.__taSmartClicks = [];
			}
			
			// store in the list of callbacks
			obj.__taSmartClicks.push(fn);
			// the unstore function removes this function from the object's listener list for this type.
			fn.__taUnstore = function() {
				_d(obj.__taSmartClicks, fn);
			};
		},
		_tapProfiles = {
			"tap":{touches:1, taps:1},
			"dbltap":{touches:1, taps:2},
			"contextmenu":{touches:2, taps:1}
		},
		TapHandler = function(clickThreshold, dblClickThreshold) {
			return function(obj, evt, fn, children) {
				// if event is contextmenu, for devices which are mouse only, we want to
				// use the default bind. 
				if (evt == "contextmenu" && isMouseDevice)
					DefaultHandler(obj, evt, fn, children);
				else {
                    // the issue here is that this down handler gets registered only for the
                    // child nodes in the first registration. in fact it should be registered with
                    // no child selector and then on down we should cycle through the regustered
                    // functions to see if one of them matches. on mouseup we should execute ALL of
                    // the functions whose children are either null or match the element.
					if (obj.__taTapHandler == null) {
						var tt = obj.__taTapHandler = {
							tap:[],
							dbltap:[],
							contextmenu:[],
							down:false,
							taps:0,
                            downSelectors:[]
						};
						var down = function(e) {
                                var target = e.srcElement || e.target;
                                for (var i = 0; i < tt.downSelectors.length; i++) {
                                    if (tt.downSelectors[i] == null || matchesSelector(target, tt.downSelectors[i], obj)) {
                                        tt.down = true;
                                        setTimeout(clearSingle, clickThreshold);
                                        setTimeout(clearDouble, dblClickThreshold);
                                        break; // we only need one match on mousedown
                                    }
                                }
							},
							up = function(e) {
								if (tt.down) {
                                    var target = e.srcElement || e.target;
									tt.taps++;
									var tc = _touchCount(e);
									for (var eventId in _tapProfiles) {
										var p = _tapProfiles[eventId];
										if (p.touches === tc && (p.taps === 1 || p.taps === tt.taps)) {
											for (var i = 0; i < tt[eventId].length; i++) {
                                                if (tt[eventId][i][1] == null || matchesSelector(target, tt[eventId][i][1], obj))
												    tt[eventId][i][0].apply(_t(e), [ e ]);
											}
										}
									}
								}
							},
							clearSingle = function() {
								tt.down = false;
							},
							clearDouble = function() {
								tt.taps = 0;
							};
						
						DefaultHandler(obj, "mousedown", down/*, children*/);
						DefaultHandler(obj, "mouseup", up/*, children*/);
					}
                    // add this child selector (it can be null, that's fine).
                    obj.__taTapHandler.downSelectors.push(children);

					obj.__taTapHandler[evt].push([fn, children]);
					// the unstore function removes this function from the object's listener list for this type.
					fn.__taUnstore = function() {
						_d(obj.__taTapHandler[evt], fn);
					};
				}
			};
		},
		meeHelper = function(type, evt, obj, target) {
			for (var i in obj.__tamee[type]) {
				obj.__tamee[type][i].apply(target, [ evt ]);
			}
		},
		MouseEnterExitHandler = function() {
			var activeElements = [];
			return function(obj, evt, fn, children) {
				if (!obj.__tamee) {
					// __tamee holds a flag saying whether the mouse is currently "in" the element, and a list of
					// both mouseenter and mouseexit functions.
					obj.__tamee = { over:false, mouseenter:[], mouseexit:[] };
					// register over and out functions
					var over = function(e) {
							var t = _t(e);
							if ( (children== null && (t == obj && !obj.__tamee.over)) || (matchesSelector(t, children, obj) && (t.__tamee == null || !t.__tamee.over)) ) {
								meeHelper("mouseenter", e, obj, t);
								t.__tamee = t.__tamee || {};
								t.__tamee.over = true;
								activeElements.push(t);
							}
						},
						out = function(e) {
							var t = _t(e);
							// is the current target one of the activeElements? and is the 
							// related target NOT a descendant of it?
							for (var i = 0; i < activeElements.length; i++) {
								if (t == activeElements[i] && !matchesSelector((e.relatedTarget || e.toElement), "*", t)) {
									t.__tamee.over = false;
									activeElements.splice(i, 1);
									meeHelper("mouseexit", e, obj, t);
								}
							}
						};
						
					_bind(obj, "mouseover", _curryChildFilter(children, obj, over, "mouseover"), over);
					_bind(obj, "mouseout", _curryChildFilter(children, obj, out, "mouseout"), out);
				}

				fn.__taUnstore = function() {
					delete obj.__tamee[evt][fn.__tauid];
				};

				_store(obj, evt, fn);
				obj.__tamee[evt][fn.__tauid] = fn;
			};
		},
		isTouchDevice = "ontouchstart" in document.documentElement,
		isMouseDevice = "onmousedown" in document.documentElement,
		touchMap = { "mousedown":"touchstart", "mouseup":"touchend", "mousemove":"touchmove" },
		touchstart="touchstart",touchend="touchend",touchmove="touchmove",
		ta_down = "__MottleDown", ta_up = "__MottleUp", 
		ta_context_down = "__MottleContextDown", ta_context_up = "__MottleContextUp",
		iev = (function() {
			var rv = -1; 
			if (navigator.appName == 'Microsoft Internet Explorer') {
				var ua = navigator.userAgent,
					re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
				if (re.exec(ua) != null)
					rv = parseFloat(RegExp.$1);
			}
			return rv;
		})(),
		isIELT9 = iev > -1 && iev < 9, 
		_genLoc = function(e, prefix) {
			if (e == null) return [ 0, 0 ];
			var ts = _touches(e), t = _getTouch(ts, 0);
			return [t[prefix + "X"], t[prefix + "Y"]];
		},
		_pageLocation = function(e) {
			if (e == null) return [ 0, 0 ];
			if (isIELT9) {
				return [ e.clientX + document.documentElement.scrollLeft, e.clientY + document.documentElement.scrollTop ];
			}
			else {
				return _genLoc(e, "page");
			}
		},
		_screenLocation = function(e) {
			return _genLoc(e, "screen");
		},
		_clientLocation = function(e) {
			return _genLoc(e, "client");
		},
		_getTouch = function(touches, idx) { return touches.item ? touches.item(idx) : touches[idx]; },
		_touches = function(e) {
			return e.touches && e.touches.length > 0 ? e.touches : 
				   e.changedTouches && e.changedTouches.length > 0 ? e.changedTouches :
				   e.targetTouches && e.targetTouches.length > 0 ? e.targetTouches :
				   [ e ];
		},
		_touchCount = function(e) { return _touches(e).length; },
		//http://www.quirksmode.org/blog/archives/2005/10/_and_the_winner_1.html
		_bind = function( obj, type, fn, originalFn) {
			_store(obj, type, fn);
			originalFn.__tauid = fn.__tauid;
			if (obj.addEventListener)
				obj.addEventListener( type, fn, false );
			else if (obj.attachEvent) {
				var key = type + fn.__tauid;
				obj["e" + key] = fn;
				// TODO look at replacing with .call(..)
				obj[key] = function() { 
					obj["e"+key] && obj["e"+key]( window.event ); 
				};
				obj.attachEvent( "on"+type, obj[key] );
			}
		},
		_unbind = function( obj, type, fn) {
			if (fn == null) return;
			_each(obj, function() {
				var _el = _gel(this);
				_unstore(_el, type, fn);
				// it has been bound if there is a tauid. otherwise it was not bound and we can ignore it.
				if (fn.__tauid != null) {
					if (_el.removeEventListener)
						_el.removeEventListener( type, fn, false );
					else if (this.detachEvent) {
						var key = type + fn.__tauid;
						_el[key] && _el.detachEvent( "on"+type, _el[key] );
						_el[key] = null;
						_el["e"+key] = null;
					}
				}
			});
		},
		_devNull = function() {},
		_each = function(obj, fn) {
			if (obj == null) return;
			// if a list (or list-like), use it. if a string, get a list 
			// by running the string through querySelectorAll. else, assume 
			// it's an Element.
			obj = (typeof obj !== "string") && (obj.tagName == null && obj.length != null) ? obj : typeof obj === "string" ? document.querySelectorAll(obj) : [ obj ];
			for (var i = 0; i < obj.length; i++)
				fn.apply(obj[i]);
		};

	/**
	* Event handler.  Offers support for abstracting out the differences
	* between touch and mouse devices, plus "smart click" functionality
	* (don't fire click if the mouse has moved betweeb mousedown and mouseup),
	* and synthesized click/tap events.
	* @class Mottle
	* @constructor
	* @param {Object} params Constructor params
	* @param {Integer} [params.clickThreshold=150] Threshold, in milliseconds beyond which a touchstart followed by a touchend is not considered to be a click.
	* @param {Integer} [params.dblClickThreshold=350] Threshold, in milliseconds beyond which two successive tap events are not considered to be a click.
	* @param {Boolean} [params.smartClicks=false] If true, won't fire click events if the mouse has moved between mousedown and mouseup. Note that this functionality
	* requires that Mottle consume the mousedown event, and so may not be viable in all use cases.
	*/
	this.Mottle = function(params) {
		params = params || {};
		var self = this, 
			clickThreshold = params.clickThreshold || 150,
			dblClickThreshold = params.dblClickThreshold || 350,
			mouseEnterExitHandler = new MouseEnterExitHandler(),
			tapHandler = new TapHandler(clickThreshold, dblClickThreshold),
			_smartClicks = params.smartClicks,
			_doBind = function(obj, evt, fn, children) {
				if (fn == null) return;
				_each(obj, function() {
					var _el = _gel(this);
					if (_smartClicks && evt === "click")
						SmartClickHandler(_el, evt, fn, children);
					else if (evt === "tap" || evt === "dbltap" || evt === "contextmenu") {
						tapHandler(_el, evt, fn, children);
					}
					else if (evt === "mouseenter" || evt == "mouseexit")
						mouseEnterExitHandler(_el, evt, fn, children);
					else 
						DefaultHandler(_el, evt, fn, children);
				});
			};

		/**
		* Removes an element from the DOM, and unregisters all event handlers for it. You should use this
		* to ensure you don't leak memory.
		* @method remove
		* @param {String|Element} el Element, or id of the element, to remove.
		* @return {Mottle} The current Mottle instance; you can chain this method.
		*/
		this.remove = function(el) {
			_each(el, function() {
				var _el = _gel(this);
				if (_el.__ta) {
					for (var evt in _el.__ta) {
						for (var h in _el.__ta[evt]) {
							_unbind(_el, evt, _el.__ta[evt][h]);
						}
					}
				}
				_el.parentNode && _el.parentNode.removeChild(_el);
			});
			return this;
		};

		/**
		* Register an event handler, optionally as a delegate for some set of descendant elements. Note
		* that this method takes either 3 or 4 arguments - if you supply 3 arguments it is assumed you have 
		* omitted the `children` parameter, and that the event handler should be bound directly to the given element.
		* @method on
		* @param {Element[]|Element|String} el Either an Element, or a CSS spec for a list of elements, or an array of Elements.
		* @param {String} [children] Comma-delimited list of selectors identifying allowed children.
		* @param {String} event Event ID.
		* @param {Function} fn Event handler function.
		* @return {Mottle} The current Mottle instance; you can chain this method.
		*/
		this.on = function(el, event, children, fn) {
			var _el = arguments[0],
				_c = arguments.length == 4 ? arguments[2] : null,
				_e = arguments[1],
				_f = arguments[arguments.length - 1];

			_doBind(_el, _e, _f, _c);
			return this;
		};	

		/**
		* Cancel delegate event handling for the given function. Note that unlike with 'on' you do not supply
		* a list of child selectors here: it removes event delegation from all of the child selectors for which the
		* given function was registered (if any).
		* @method off
		* @param {Element[]|Element|String} el Element - or ID of element - from which to remove event listener.
		* @param {String} event Event ID.
		* @param {Function} fn Event handler function.
		* @return {Mottle} The current Mottle instance; you can chain this method.
		*/
		this.off = function(el, evt, fn) {
			_unbind(el, evt, fn);
			return this;
		};

		/**
		* Triggers some event for a given element.
		* @method trigger
		* @param {Element} el Element for which to trigger the event.
		* @param {String} event Event ID.
		* @param {Event} originalEvent The original event. Should be optional of course, but currently is not, due
		* to the jsPlumb use case that caused this method to be added.
		* @param {Object} [payload] Optional object to set as `payload` on the generated event; useful for message passing.
		* @return {Mottle} The current Mottle instance; you can chain this method.
		*/
		this.trigger = function(el, event, originalEvent, payload) {
			var eventToBind = (isTouchDevice && touchMap[event]) ? touchMap[event] : event;
			var pl = _pageLocation(originalEvent), sl = _screenLocation(originalEvent), cl = _clientLocation(originalEvent);
			_each(el, function() {
				var _el = _gel(this), evt;
				originalEvent = originalEvent || {
					screenX:sl[0],
					screenY:sl[1],
					clientX:cl[0],
					clientY:cl[1]
				};

				var _decorate = function(_evt) {
					if (payload) _evt.payload = payload;
				};

				var eventGenerators = {
					"TouchEvent":function(evt) {
						var t = document.createTouch(window, _el, 0, pl[0], pl[1], 
									sl[0], sl[1],
									cl[0], cl[1],
									0,0,0,0);

						evt.initTouchEvent(eventToBind, true, true, window, 0, 
							sl[0], sl[1],
							cl[0], cl[1],
							false, false, false, false, document.createTouchList(t));
					},
					"MouseEvents":function(evt) {
						evt.initMouseEvent(eventToBind, true, true, window, 0,
							sl[0], sl[1],
							cl[0], cl[1],
							false, false, false, false, 1, _el);
						
						if (Sniff.android) {
							// Android's touch events are not standard.
							var t = document.createTouch(window, _el, 0, pl[0], pl[1], 
										sl[0], sl[1],
										cl[0], cl[1],
										0,0,0,0);

							evt.touches = evt.targetTouches = evt.changedTouches = document.createTouchList(t);
						}
					}
				};

				if (document.createEvent) {
					var ite = (isTouchDevice && touchMap[event] && !Sniff.android), evtName = ite ? "TouchEvent" : "MouseEvents";
					evt = document.createEvent(evtName);
					eventGenerators[evtName](evt);
					_decorate(evt);
					_el.dispatchEvent(evt);
				}
				else if (document.createEventObject) {
					evt = document.createEventObject();
					evt.eventType = evt.eventName = eventToBind;
					evt.screenX = sl[0];
					evt.screenY = sl[1];
					evt.clientX = cl[0];
					evt.clientY = cl[1];
					_decorate(evt);
					_el.fireEvent('on' + eventToBind, evt);
				}
			});
			return this;
		}
	};

	/**
	* Static method to assist in 'consuming' an element: uses `stopPropagation` where available, or sets `e.returnValue=false` where it is not.
	* @method Mottle.consume
	* @param {Event} e Event to consume
	* @param {Boolean} [doNotPreventDefault=false] If true, does not call `preventDefault()` on the event.
	*/
	Mottle.consume = function(e, doNotPreventDefault) {
		if (e.stopPropagation)
			e.stopPropagation();
		else 
			e.returnValue = false;

		if (!doNotPreventDefault && e.preventDefault)
			 e.preventDefault();
	};

	/**
	* Gets the page location corresponding to the given event. For touch events this means get the page location of the first touch.
	* @method Mottle.pageLocation
	* @param {Event} e Event to get page location for.
	* @return {Integer[]} [left, top] for the given event.
	*/
	Mottle.pageLocation = _pageLocation;

}).call(this);
