/* * L.Mixin.Events is used to add custom events functionality to Leaflet classes. */ var eventsKey = '_leaflet_events'; L.Mixin = {}; L.Mixin.Events = { addEventListener: function (types, fn, context) { // (String, Function[, Object]) or (Object[, Object]) // types can be a map of types/handlers if (L.Util.invokeEach(types, this.addEventListener, this, fn, context)) { return this; } var events = this[eventsKey] = this[eventsKey] || {}, contextId = context && context !== this && L.stamp(context), i, len, event, type, indexKey, indexLenKey, typeIndex; // types can be a string of space-separated words types = L.Util.splitWords(types); for (i = 0, len = types.length; i < len; i++) { event = { action: fn, context: context || this }; type = types[i]; 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 indexKey = type + '_idx'; indexLenKey = indexKey + '_len'; typeIndex = events[indexKey] = events[indexKey] || {}; if (!typeIndex[contextId]) { typeIndex[contextId] = []; // keep track of the number of keys in the index to quickly check if it's empty events[indexLenKey] = (events[indexLenKey] || 0) + 1; } typeIndex[contextId].push(event); } else { events[type] = events[type] || []; events[type].push(event); } } return this; }, hasEventListeners: function (type) { // (String) -> Boolean var events = this[eventsKey]; return !!events && ((type in events && events[type].length > 0) || (type + '_idx' in events && events[type + '_idx_len'] > 0)); }, removeEventListener: function (types, fn, context) { // ([String, Function, Object]) or (Object[, Object]) if (!this[eventsKey]) { return this; } if (!types) { return this.clearAllEventListeners(); } if (L.Util.invokeEach(types, this.removeEventListener, this, fn, context)) { return this; } var events = this[eventsKey], contextId = context && context !== this && L.stamp(context), i, len, type, listeners, j, indexKey, indexLenKey, typeIndex, removed; types = L.Util.splitWords(types); for (i = 0, len = types.length; i < len; i++) { type = types[i]; indexKey = type + '_idx'; indexLenKey = indexKey + '_len'; typeIndex = events[indexKey]; if (!fn) { // clear all listeners for a type if function isn't specified delete events[type]; delete events[indexKey]; delete events[indexLenKey]; } else { listeners = contextId && typeIndex ? typeIndex[contextId] : events[type]; if (listeners) { for (j = listeners.length - 1; j >= 0; j--) { if ((listeners[j].action === fn) && (!context || (listeners[j].context === context))) { removed = listeners.splice(j, 1); // set the old action to a no-op, because it is possible // that the listener is being iterated over as part of a dispatch removed[0].action = L.Util.falseFn; } } if (context && typeIndex && (listeners.length === 0)) { delete typeIndex[contextId]; events[indexLenKey]--; } } } } return this; }, clearAllEventListeners: function () { delete this[eventsKey]; 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 events = this[eventsKey], listeners, i, len, typeIndex, contextId; if (events[type]) { // make sure adding/removing listeners inside other listeners won't cause infinite loop listeners = events[type].slice(); for (i = 0, len = listeners.length; i < len; i++) { listeners[i].action.call(listeners[i].context, event); } } // fire event for the context-indexed listeners as well typeIndex = events[type + '_idx']; for (contextId in typeIndex) { listeners = typeIndex[contextId].slice(); if (listeners) { for (i = 0, len = listeners.length; i < len; i++) { listeners[i].action.call(listeners[i].context, event); } } } return this; }, addOneTimeEventListener: function (types, fn, context) { if (L.Util.invokeEach(types, this.addOneTimeEventListener, this, fn, context)) { return this; } var handler = L.bind(function () { this .removeEventListener(types, fn, context) .removeEventListener(types, handler, context); }, this); return this .addEventListener(types, fn, context) .addEventListener(types, handler, context); } }; L.Mixin.Events.on = L.Mixin.Events.addEventListener; L.Mixin.Events.off = L.Mixin.Events.removeEventListener; L.Mixin.Events.once = L.Mixin.Events.addOneTimeEventListener; L.Mixin.Events.fire = L.Mixin.Events.fireEvent;