diff --git a/gulpfile.js b/gulpfile.js index 7836f78c5..85054fac5 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -10,10 +10,8 @@ var gulp = require('gulp'); var rollup = require('gulp-rollup'); var rename = require("gulp-rename"); -var rename = require('gulp-rename'); var uglify = require('gulp-uglify'); var pump = require('pump'); -var uglify = require('gulp-uglify'); var path = require('path'); var spawn = require('child_process').spawn; diff --git a/lib/common/utils.ts b/lib/common/utils.ts index 5b2e82fbb..496d0bbff 100644 --- a/lib/common/utils.ts +++ b/lib/common/utils.ts @@ -118,11 +118,16 @@ const EVENT_TASKS = zoneSymbol('eventTasks'); const ADD_EVENT_LISTENER = 'addEventListener'; const REMOVE_EVENT_LISTENER = 'removeEventListener'; +interface NestedEventListener { + listener?: EventListenerOrEventListenerObject; +} + +declare type NestedEventListenerOrEventListenerObject = NestedEventListener | EventListener | EventListenerObject; interface ListenerTaskMeta extends TaskData { useCapturing: boolean; eventName: string; - handler: EventListenerOrEventListenerObject; + handler: NestedEventListenerOrEventListenerObject; target: any; name: string; } @@ -134,7 +139,8 @@ function findExistingRegisteredTask( for (let i = 0; i < eventTasks.length; i++) { const eventTask = eventTasks[i]; const data = eventTask.data; - if (data.handler === handler && data.useCapturing === capture && data.eventName === name) { + const listener = data.handler; + if ((data.handler === handler || listener.listener === handler) && data.useCapturing === capture && data.eventName === name) { if (remove) { eventTasks.splice(i, 1); } @@ -145,6 +151,24 @@ function findExistingRegisteredTask( return null; } +function findAllExistingRegisteredTasks(target: any, name: string, capture: boolean, remove: boolean): Task[] { + const eventTasks: Task[] = target[EVENT_TASKS]; + if (eventTasks) { + const result = []; + for (var i = eventTasks.length - 1; i >= 0; i --) { + const eventTask = eventTasks[i]; + const data = eventTask.data; + if (data.eventName === name && data.useCapturing === capture) { + result.push(eventTask); + if (remove) { + eventTasks.splice(i, 1); + } + } + } + return result; + } + return null; +} function attachRegisteredEvent(target: any, eventTask: Task): void { let eventTasks: Task[] = target[EVENT_TASKS]; @@ -247,6 +271,25 @@ export function makeZoneAwareRemoveListener(fnName: string, useCapturingParam: b }; } +export function makeZoneAwareRemoveAllListeners(fnName: string, useCapturingParam: boolean = true) { + const symbol = zoneSymbol(fnName); + const defaultUseCapturing = useCapturingParam ? false : undefined; + + return function zoneAwareRemoveAllListener(self: any, args: any[]) { + var eventName = args[0]; + var useCapturing = args[1] || defaultUseCapturing; + var target = self || _global; + var eventTasks = findAllExistingRegisteredTasks(target, eventName, useCapturing, true); + if (eventTasks) { + for (var i = 0; i < eventTasks.length; i ++) { + var eventTask = eventTasks[i]; + eventTask.zone.cancelTask(eventTask); + } + } + target[symbol](eventName, useCapturing); + } +} + export function makeZoneAwareListeners(fnName: string) { const symbol = zoneSymbol(fnName); @@ -371,4 +414,4 @@ export function patchMethod( proto[name] = createNamedFn(name, patchFn(delegate, delegateName, name)); } return delegate; -} +} \ No newline at end of file diff --git a/lib/node/events.ts b/lib/node/events.ts index a798e9e10..3e6b19499 100644 --- a/lib/node/events.ts +++ b/lib/node/events.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {makeZoneAwareAddListener, makeZoneAwareListeners, makeZoneAwareRemoveListener, patchMethod} from '../common/utils'; +import {makeZoneAwareAddListener, makeZoneAwareListeners, makeZoneAwareRemoveListener, makeZoneAwareRemoveAllListeners, patchMethod} from '../common/utils'; const callAndReturnFirstParam = (fn: (self: any, args: any[]) => any) => { return (self: any, args: any[]) => { @@ -19,12 +19,14 @@ const callAndReturnFirstParam = (fn: (self: any, args: any[]) => any) => { const EE_ADD_LISTENER = 'addListener'; const EE_PREPEND_LISTENER = 'prependListener'; const EE_REMOVE_LISTENER = 'removeListener'; +const EE_REMOVE_ALL_LISTENER = 'removeAllListeners'; const EE_LISTENERS = 'listeners'; const EE_ON = 'on'; const zoneAwareAddListener = callAndReturnFirstParam(makeZoneAwareAddListener(EE_ADD_LISTENER, EE_REMOVE_LISTENER, false, true)); const zoneAwarePrependListener = callAndReturnFirstParam(makeZoneAwareAddListener(EE_PREPEND_LISTENER, EE_REMOVE_LISTENER, false, true)); const zoneAwareRemoveListener = callAndReturnFirstParam(makeZoneAwareRemoveListener(EE_REMOVE_LISTENER, false)); +const zoneAwareRemoveAllListeners = callAndReturnFirstParam(makeZoneAwareRemoveAllListeners(EE_REMOVE_ALL_LISTENER, false)); const zoneAwareListeners = makeZoneAwareListeners(EE_LISTENERS); export function patchEventEmitterMethods(obj: any): boolean { @@ -32,6 +34,7 @@ export function patchEventEmitterMethods(obj: any): boolean { patchMethod(obj, EE_ADD_LISTENER, () => zoneAwareAddListener); patchMethod(obj, EE_PREPEND_LISTENER, () => zoneAwarePrependListener); patchMethod(obj, EE_REMOVE_LISTENER, () => zoneAwareRemoveListener); + patchMethod(obj, EE_REMOVE_ALL_LISTENER, () => zoneAwareRemoveAllListeners); patchMethod(obj, EE_LISTENERS, () => zoneAwareListeners); obj[EE_ON] = obj[EE_ADD_LISTENER]; return true; diff --git a/test/node/events.spec.ts b/test/node/events.spec.ts index 5a3fa9906..f010a9da2 100644 --- a/test/node/events.spec.ts +++ b/test/node/events.spec.ts @@ -68,5 +68,13 @@ describe('nodejs EventEmitter', () => { zoneA.run(() => { expect(emitter.listeners('test')).toEqual([]); }); - }) + }); + it ('should remove All listeners properly', () => { + zoneA.run(() => { + emitter.on('test', expectZoneA); + emitter.on('test', expectZoneA); + emitter.removeAllListeners('test'); + expect(emitter.listeners('test').length).toEqual(0); + }); + }); }); \ No newline at end of file