| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524 | ;(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);
 |