/*
 * L.Mixin.Events is used to add custom events functionality to Leaflet classes.
 */

var key = '_leaflet_events';

L.Mixin = {};

L.Mixin.Events = {

	addEventListener: function (types, fn, context) { // (String, Function[, Object]) or (Object[, Object])

		var events = this[key] = this[key] || {},
		    contextId = context && L.stamp(context),
		    type, i, len, evt,
		    objKey, objLenKey, eventsObj;

		// types can be a map of types/handlers
		if (typeof types === 'object') {
			for (type in types) {
				if (types.hasOwnProperty(type)) {
					this.addEventListener(type, types[type], fn);
				}
			}

			return this;
		}

		// types can be a string of space-separated words
		types = L.Util.splitWords(types);

		for (i = 0, len = types.length; i < len; i++) {
			evt = {
				action: fn,
				context: context || this
			};

			if (contextId) {
				// store listeners of a particular context in a separate hash (if it has an id)
				// gives a major performance boost when removing thousands of map layers

				objKey = types[i] + '_idx',
				objLenKey = objKey + '_len',
				eventsObj = events[objKey] = events[objKey] || {};

				if (eventsObj[contextId]) {
					eventsObj[contextId].push(evt);
				} else {
					eventsObj[contextId] = [evt];
					events[objLenKey] = (events[objLenKey] || 0) + 1;
				}

			} else {
				events[types[i]] = events[types[i]] || [];
				events[types[i]].push(evt);
			}
		}

		return this;
	},

	hasEventListeners: function (type) { // (String) -> Boolean
		return (key in this) &&
		       (((type in this[key]) && this[key][type].length > 0) ||
		        (this[key][type + '_idx_len'] > 0));
	},

	removeEventListener: function (types, fn, context) { // (String[, Function, Object]) or (Object[, Object])
		var events = this[key],
		    contextId = context && context._leaflet_id, // if the context has an id, use it to find the listeners
		    type, i, len, listeners, j,
		    objKey, objLenKey;

		if (typeof types === 'object') {
			for (type in types) {
				if (types.hasOwnProperty(type)) {
					this.removeEventListener(type, types[type], fn);
				}
			}
			return this;
		}

		types = L.Util.splitWords(types);

		for (i = 0, len = types.length; i < len; i++) {
			if (this.hasEventListeners(types[i])) {

				objKey = types[i] + '_idx';

				if (contextId && events[objKey]) {
					listeners =  events[objKey][contextId] || [];
				} else {
					listeners = events[types[i]] || [];
				}

				for (j = listeners.length - 1; j >= 0; j--) {
					if ((!fn || listeners[j].action === fn) && (!context || (listeners[j].context === context))) {
						listeners.splice(j, 1);
					}
				}

				if (contextId && listeners.length === 0 && events[objKey] && events[objKey][contextId]) {
					objLenKey = objKey + '_len';
					delete events[objKey][contextId];
					events[objLenKey] = (events[objLenKey] || 1) - 1;
				}
			}
		}

		return this;
	},

	fireEvent: function (type, data) { // (String[, Object])
		if (!this.hasEventListeners(type)) {
			return this;
		}

		var event = L.Util.extend({}, data, {
			type: type,
			target: this
		});

		var listeners, i, len, eventsObj, contextId;

		if (this[key][type]) {
			listeners = this[key][type].slice();

			for (i = 0, len = listeners.length; i < len; i++) {
				listeners[i].action.call(listeners[i].context || this, event);
			}
		}

		// fire event for the context-indexed listeners as well

		eventsObj = this[key][type + '_idx'];

		if (eventsObj) {
			for (contextId in eventsObj) {
				if (eventsObj.hasOwnProperty(contextId)) {
					listeners = eventsObj[contextId];
					if (listeners) {
						for (i = 0, len = listeners.length; i < len; i++) {
							listeners[i].action.call(listeners[i].context || this, event);
						}
					}
				}
			}
		}

		return this;
	}
};

L.Mixin.Events.on = L.Mixin.Events.addEventListener;
L.Mixin.Events.off = L.Mixin.Events.removeEventListener;
L.Mixin.Events.fire = L.Mixin.Events.fireEvent;

@@ -1875,86 +1875,25 @@
  && 
-context._leaflet_id, // if the context has an id, use it to find the listeners
+L.stamp(context),
 %0D%0A%09%09

Use L.stamp to always have a stamped context on removal too.