From af88ff8a01c1d5b445eeb212569dde5a88c8e509 Mon Sep 17 00:00:00 2001 From: Matthew Hill Date: Tue, 7 Jul 2015 14:18:16 +0100 Subject: [PATCH] fix(utils): event listener patches break when passed an object implementing EventListener --- lib/utils.js | 30 ++++++++++++++++++------ test/patch/element.spec.js | 48 ++++++++++++++++++++++++++++++++++---- 2 files changed, 67 insertions(+), 11 deletions(-) diff --git a/lib/utils.js b/lib/utils.js index b3c92e721..e44e014c3 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -86,21 +86,37 @@ function patchProperties(obj, properties) { function patchEventTargetMethods(obj) { var addDelegate = obj.addEventListener; - obj.addEventListener = function (eventName, fn) { - fn._bound = fn._bound || {}; - arguments[1] = fn._bound[eventName] = zone.bind(fn); + obj.addEventListener = function (eventName, handler) { + var fn; + + if (handler.handleEvent) { + // Have to pass in 'handler' reference as an argument here, otherwise it gets clobbered in + // IE9 by the arguments[1] assignment at end of this function. + fn = (function(handler) { + return function() { + handler.handleEvent.apply(handler, arguments); + }; + })(handler); + } else { + fn = handler; + } + + handler._fn = fn; + handler._bound = handler._bound || {}; + arguments[1] = handler._bound[eventName] = zone.bind(fn); return addDelegate.apply(this, arguments); }; var removeDelegate = obj.removeEventListener; - obj.removeEventListener = function (eventName, fn) { - if(arguments[1]._bound && arguments[1]._bound[eventName]) { - var _bound = arguments[1]._bound; + obj.removeEventListener = function (eventName, handler) { + if(handler._bound && handler._bound[eventName]) { + var _bound = handler._bound; + arguments[1] = _bound[eventName]; delete _bound[eventName]; } var result = removeDelegate.apply(this, arguments); - global.zone.dequeueTask(fn); + global.zone.dequeueTask(handler._fn); return result; }; }; diff --git a/test/patch/element.spec.js b/test/patch/element.spec.js index 7eec69703..19becceb6 100644 --- a/test/patch/element.spec.js +++ b/test/patch/element.spec.js @@ -14,17 +14,43 @@ describe('element', function () { document.body.removeChild(button); }); - it('should work with addEventListener', function () { + it('should work with addEventListener when called with a function listener', function () { + var clickEvent = document.createEvent('Event'); + clickEvent.initEvent('click', true, true); + testZone.run(function() { - button.addEventListener('click', function () { + button.addEventListener('click', function (event) { expect(zone).toBeDirectChildOf(testZone); + expect(event).toBe(clickEvent) }); }); - button.click(); + button.dispatchEvent(clickEvent); }); - it('should respect removeEventListener', function () { + it('should work with addEventListener when called with an EventListener-implementing listener', function () { + var eventListener = { + x: 5, + handleEvent: function(event) { + // Test that context is preserved + expect(this.x).toBe(5); + + expect(event).toBe(clickEvent); + expect(zone).toBeDirectChildOf(testZone); + } + }; + + var clickEvent = document.createEvent('Event'); + clickEvent.initEvent('click', true, true); + + testZone.run(function() { + button.addEventListener('click', eventListener); + }); + + button.dispatchEvent(clickEvent); + }); + + it('should respect removeEventListener when called with a function listener', function () { var log = ''; var logFunction = function logFunction () { log += 'a'; @@ -44,6 +70,20 @@ describe('element', function () { expect(log).toEqual('aa'); }); + it('should respect removeEventListener with an EventListener-implementing listener', function () { + var eventListener = { + x: 5, + handleEvent: jasmine.createSpy('handleEvent') + }; + + button.addEventListener('click', eventListener); + button.removeEventListener('click', eventListener); + + button.click(); + + expect(eventListener.handleEvent).not.toHaveBeenCalled(); + }); + describe('onclick', function() {