text: /*
   * L.DomEvent contains functions for working with DOM events.
   */
  
  L.DomEvent = {
  	/* inspired by John Resig, Dean Edwards and YUI addEvent implementations */
  	addListener: function (obj, type, fn, context) { // (HTMLElement, String, Function[, Object])
  
  		var id = L.stamp(fn),
  		    key = '_leaflet_' + type + id,
  		    handler, originalHandler, newType;
  
  		if (obj[key]) { return this; }
  
  		handler = function (e) {
  			return fn.call(context || obj, e || L.DomEvent._getEvent());
  		};
  
  		if (L.Browser.msTouch && type.indexOf('touch') === 0) {
  			return this.addMsTouchListener(obj, type, handler, id);
  		}
  		if (L.Browser.touch && (type === 'dblclick') && this.addDoubleTapListener) {
  			this.addDoubleTapListener(obj, handler, id);
  		}
  
  		if ('addEventListener' in obj) {
  
  			if (type === 'mousewheel') {
  				obj.addEventListener('DOMMouseScroll', handler, false);
  				obj.addEventListener(type, handler, false);
  
  			} else if ((type === 'mouseenter') || (type === 'mouseleave')) {
  
  				originalHandler = handler;
  				newType = (type === 'mouseenter' ? 'mouseover' : 'mouseout');
  
  				handler = function (e) {
  					if (!L.DomEvent._checkMouse(obj, e)) { return; }
  					return originalHandler(e);
  				};
  
  				obj.addEventListener(newType, handler, false);
  
  			} else if (type === 'click' && L.Browser.android) {
  				originalHandler = handler;
  				handler = function (e) {
  					return L.DomEvent._filterClick(e, originalHandler);
  				};
  
  				obj.addEventListener(type, handler, false);
  			} else {
  				obj.addEventListener(type, handler, false);
  			}
  
  		} else if ('attachEvent' in obj) {
  			obj.attachEvent('on' + type, handler);
  		}
  
  		obj[key] = handler;
  
  		return this;
  	},
  
  	removeListener: function (obj, type, fn) {  // (HTMLElement, String, Function)
  
  		var id = L.stamp(fn),
  		    key = '_leaflet_' + type + id,
  		    handler = obj[key];
  
  		if (!handler) { return this; }
  
  		if (L.Browser.msTouch && type.indexOf('touch') === 0) {
  			this.removeMsTouchListener(obj, type, id);
  		} else if (L.Browser.touch && (type === 'dblclick') && this.removeDoubleTapListener) {
  			this.removeDoubleTapListener(obj, id);
  
  		} else if ('removeEventListener' in obj) {
  
  			if (type === 'mousewheel') {
  				obj.removeEventListener('DOMMouseScroll', handler, false);
  				obj.removeEventListener(type, handler, false);
  
  			} else if ((type === 'mouseenter') || (type === 'mouseleave')) {
  				obj.removeEventListener((type === 'mouseenter' ? 'mouseover' : 'mouseout'), handler, false);
  			} else {
  				obj.removeEventListener(type, handler, false);
  			}
  		} else if ('detachEvent' in obj) {
  			obj.detachEvent('on' + type, handler);
  		}
  
  		obj[key] = null;
  
  		return this;
  	},
  
  	stopPropagation: function (e) {
  
  		if (e.stopPropagation) {
  			e.stopPropagation();
  		} else {
  			e.cancelBubble = true;
  		}
  		return this;
  	},
  
  	disableClickPropagation: function (el) {
  
  		var stop = L.DomEvent.stopPropagation;
  
  		for (var i = L.Draggable.START.length - 1; i >= 0; i--) {
  			L.DomEvent.addListener(el, L.Draggable.START[i], stop);
  		}
  
  		return L.DomEvent
  			.addListener(el, 'click', stop)
  			.addListener(el, 'dblclick', stop);
  	},
  
  	preventDefault: function (e) {
  
  		if (e.preventDefault) {
  			e.preventDefault();
  		} else {
  			e.returnValue = false;
  		}
  		return this;
  	},
  
  	stop: function (e) {
  		return L.DomEvent.preventDefault(e).stopPropagation(e);
  	},
  
  	getMousePosition: function (e, container) {
  
  		var body = document.body,
  		    docEl = document.documentElement,
  		    x = e.pageX ? e.pageX : e.clientX + body.scrollLeft + docEl.scrollLeft,
  		    y = e.pageY ? e.pageY : e.clientY + body.scrollTop + docEl.scrollTop,
  		    pos = new L.Point(x, y);
  
  		return (container ? pos._subtract(L.DomUtil.getViewportOffset(container)) : pos);
  	},
  
  	getWheelDelta: function (e) {
  
  		var delta = 0;
  
  		if (e.wheelDelta) {
  			delta = e.wheelDelta / 120;
  		}
  		if (e.detail) {
  			delta = -e.detail / 3;
  		}
  		return delta;
  	},
  
  	// check if element really left/entered the event target (for mouseenter/mouseleave)
  	_checkMouse: function (el, e) {
  
  		var related = e.relatedTarget;
  
  		if (!related) { return true; }
  
  		try {
  			while (related && (related !== el)) {
  				related = related.parentNode;
  			}
  		} catch (err) {
  			return false;
  		}
  		return (related !== el);
  	},
  
  	_getEvent: function () { // evil magic for IE
  		/*jshint noarg:false */
  		var e = window.event;
  		if (!e) {
  			var caller = arguments.callee.caller;
  			while (caller) {
  				e = caller['arguments'][0];
  				if (e && window.Event === e.constructor) {
  					break;
  				}
  				caller = caller.caller;
  			}
  		}
  		return e;
  	},
  
  	// this solves a bug in Android WebView where a single touch triggers two click events.
  	_filterClick: function (e, handler) {
  		var timeStamp = (e.timeStamp || e.originalEvent.timeStamp);
  		var elapsed = L.DomEvent._lastClick && (timeStamp - L.DomEvent._lastClick);
  
  		// are they closer together than 400ms yet more than 100ms?
  		// Android typically triggers them ~300ms apart while multiple listeners
  		// on the same event should be triggered far faster.
  
  		if (elapsed && elapsed > 100 && elapsed < 400) {
  			L.DomEvent.stop(e);
  			return;
  		}
  		L.DomEvent._lastClick = timeStamp;
  
  		return handler(e);
  	}
  };
  
  L.DomEvent.on = L.DomEvent.addListener;
  L.DomEvent.off = L.DomEvent.removeListener;
  