From 95ed9cdda2679356f1266f89b1e422c8e32630f6 Mon Sep 17 00:00:00 2001 From: Aaron King Date: Fri, 29 Jun 2012 16:14:41 -0700 Subject: [PATCH] Allow L.Mixin.Events.addEventListener() to accept a map in which the string keys represent one or more space-separated event types and the values represent a handler function to be called for the event(s). Allow L.Mixin.Events.removeEventListener() to accept one or more space-separated event types. Omitting the fn parameter will remove all event handlers for the supplied event type(s). Also allow L.Mixin.Events.removeEventListener() to accept a map where the string keys represent one or more space-separated event types and the values represent handler functions previously attached for the event(s). Add unit tests for the above changes. --- spec/suites/core/EventsSpec.js | 74 ++++++++++++++++++++++++++++--- src/core/Events.js | 80 +++++++++++++++++++++++++++------- 2 files changed, 132 insertions(+), 22 deletions(-) diff --git a/spec/suites/core/EventsSpec.js b/spec/suites/core/EventsSpec.js index be143866..ac61d90f 100644 --- a/spec/suites/core/EventsSpec.js +++ b/spec/suites/core/EventsSpec.js @@ -13,49 +13,89 @@ describe('Events', function() { var obj = new Klass(), spy = jasmine.createSpy(), spy2 = jasmine.createSpy(), - spy3 = jasmine.createSpy(); + spy3 = jasmine.createSpy(), + spy4 = jasmine.createSpy(), + spy5 = jasmine.createSpy(); + spy6 = jasmine.createSpy(); obj.addEventListener('test', spy); obj.addEventListener('test', spy2); obj.addEventListener('other', spy3); + obj.addEventListener({ test: spy4, other: spy5 }); + obj.addEventListener({'test other': spy6 }) expect(spy).not.toHaveBeenCalled(); expect(spy2).not.toHaveBeenCalled(); expect(spy3).not.toHaveBeenCalled(); + expect(spy4).not.toHaveBeenCalled(); + expect(spy5).not.toHaveBeenCalled(); + expect(spy6).not.toHaveBeenCalled(); obj.fireEvent('test'); expect(spy).toHaveBeenCalled(); expect(spy2).toHaveBeenCalled(); expect(spy3).not.toHaveBeenCalled(); + expect(spy4).toHaveBeenCalled(); + expect(spy5).not.toHaveBeenCalled(); + expect(spy6).toHaveBeenCalled(); + expect(spy6.calls.length).toEqual(1); }); + it('should provide event object to listeners and execute them in the right context', function() { var obj = new Klass(), obj2 = new Klass(), + obj3 = new Klass(), + obj4 = new Klass(), foo = {}; function listener1(e) { expect(e.type).toEqual('test'); expect(e.target).toEqual(obj); expect(this).toEqual(obj); - expect(e.bar).toEqual(3); - }; + expect(e.baz).toEqual(1); + } function listener2(e) { + expect(e.type).toEqual('test'); expect(e.target).toEqual(obj2); expect(this).toEqual(foo); - }; + expect(e.baz).toEqual(2); + } + + function listener3(e) { + expect(e.type).toEqual('test'); + expect(e.target).toEqual(obj3); + expect(this).toEqual(obj3); + expect(e.baz).toEqual(3); + } + + function listener4(e) { + expect(e.type).toEqual('test'); + expect(e.target).toEqual(obj4); + expect(this).toEqual(foo); + expect(e.baz).toEqual(4); + } obj.addEventListener('test', listener1); obj2.addEventListener('test', listener2, foo); + obj3.addEventListener({ test: listener3 }); + obj4.addEventListener({ test: listener4 }, foo); - obj.fireEvent('test', {bar: 3}); + obj.fireEvent('test', {baz: 1}); + obj2.fireEvent('test', {baz: 2}); + obj3.fireEvent('test', {baz: 3}); + obj4.fireEvent('test', {baz: 4}); }); it('should not call listeners removed through #removeEventListener', function() { var obj = new Klass(), - spy = jasmine.createSpy(); + spy = jasmine.createSpy(), + spy2 = jasmine.createSpy(), + spy3 = jasmine.createSpy(), + spy4 = jasmine.createSpy(), + spy5 = jasmine.createSpy(); obj.addEventListener('test', spy); obj.removeEventListener('test', spy); @@ -63,6 +103,28 @@ describe('Events', function() { obj.fireEvent('test'); expect(spy).not.toHaveBeenCalled(); + + obj.addEventListener('test2', spy2); + obj.addEventListener('test2', spy3); + obj.removeEventListener('test2'); + + obj.fireEvent('test2'); + + expect(spy2).not.toHaveBeenCalled(); + expect(spy3).not.toHaveBeenCalled(); + + obj.addEventListener('test3', spy4); + obj.addEventListener('test4', spy5); + obj.removeEventListener({ + test3: spy4, + test4: spy5 + }); + + obj.fireEvent('test3'); + obj.fireEvent('test4'); + + expect(spy4).not.toHaveBeenCalled(); + expect(spy5).not.toHaveBeenCalled(); }); }); diff --git a/src/core/Events.js b/src/core/Events.js index f010b395..6974be96 100644 --- a/src/core/Events.js +++ b/src/core/Events.js @@ -5,13 +5,37 @@ L.Mixin = {}; L.Mixin.Events = { - addEventListener: function (/*String*/ type, /*Function*/ fn, /*(optional) Object*/ context) { + addEventListener: function (/*String or Object*/ types, /*(optional) Function or Object*/ fn, /*(optional) Object*/ context) { var events = this._leaflet_events = this._leaflet_events || {}; - events[type] = events[type] || []; - events[type].push({ - action: fn, - context: context || this - }); + + // Types can be a map of types/handlers + if (typeof types === 'object') { + context = context || fn; + fn = undefined; + + for (var type in types) { + if (types.hasOwnProperty(type)) { + this.addEventListener(type, types[type], context); + } + } + + return this; + } + + if (!fn) { + return false; + } + + types = (types || '').replace(/^\s+/, '').replace(/\s+$/, '').split(' '); + + for (var i = 0, ilen = types.length; i < ilen; i++) { + events[types[i]] = events[types[i]] || []; + events[types[i]].push({ + action: fn, + context: context || this + }); + } + return this; }, @@ -20,20 +44,44 @@ L.Mixin.Events = { return (k in this) && (type in this[k]) && (this[k][type].length > 0); }, - removeEventListener: function (/*String*/ type, /*Function*/ fn, /*(optional) Object*/ context) { - if (!this.hasEventListeners(type)) { + removeEventListener: function (/*String or Object*/ types, /*(optional) Function*/ fn, /*(optional) Object*/ context) { + var events = this._leaflet_events; + + if (typeof types === 'object') { + context = context || fn; + fn = undefined; + + for (var type in types) { + if (types.hasOwnProperty(type)) { + this.off(type, types[type], context); + } + } + return this; } - - for (var i = 0, events = this._leaflet_events, len = events[type].length; i < len; i++) { - if ( - (events[type][i].action === fn) && - (!context || (events[type][i].context === context)) - ) { - events[type].splice(i, 1); - return this; + + types = (types || '').replace(/^\s+/, '').replace(/\s+$/, '').split(' '); + + for (var i = 0, ilen = types.length; i < ilen; i++) { + var eventType = events[types[i]] || []; + + if (!this.hasEventListeners(types[i])) { + continue; + } + + // Remove matching events + var j = eventType.length; + + while (j--) { + if ( + (!fn || eventType[j].action === fn) && + (!context || (eventType[j].context === context)) + ) { + eventType.splice(j, 1); + } } } + return this; },