@@ -108,10 +108,11 @@ export function patchOnProperties(obj: any, properties: string[]) {
108108} ;
109109
110110const EVENT_TASKS = zoneSymbol ( 'eventTasks' ) ;
111+
112+ // For EventTarget
111113const ADD_EVENT_LISTENER = 'addEventListener' ;
112114const REMOVE_EVENT_LISTENER = 'removeEventListener' ;
113- const SYMBOL_ADD_EVENT_LISTENER = zoneSymbol ( ADD_EVENT_LISTENER ) ;
114- const SYMBOL_REMOVE_EVENT_LISTENER = zoneSymbol ( REMOVE_EVENT_LISTENER ) ;
115+
115116
116117interface ListenerTaskMeta extends TaskData {
117118 useCapturing : boolean ;
@@ -151,83 +152,113 @@ function attachRegisteredEvent(target: any, eventTask: Task): void {
151152 eventTasks . push ( eventTask ) ;
152153}
153154
154- function scheduleEventListener ( eventTask : Task ) : any {
155- const meta = < ListenerTaskMeta > eventTask . data ;
156- attachRegisteredEvent ( meta . target , eventTask ) ;
157- return meta . target [ SYMBOL_ADD_EVENT_LISTENER ] ( meta . eventName , eventTask . invoke ,
158- meta . useCapturing ) ;
159- }
155+ export function makeZoneAwareAddListener ( addFnName : string , removeFnName : string , useCapturingParam : boolean = true , allowDuplicates : boolean = false ) {
156+ const addFnSymbol = zoneSymbol ( addFnName ) ;
157+ const removeFnSymbol = zoneSymbol ( removeFnName ) ;
158+ const defaultUseCapturing = useCapturingParam ? false : undefined ;
160159
161- function cancelEventListener ( eventTask : Task ) : void {
162- const meta = < ListenerTaskMeta > eventTask . data ;
163- findExistingRegisteredTask ( meta . target , eventTask . invoke , meta . eventName ,
164- meta . useCapturing , true ) ;
165- meta . target [ SYMBOL_REMOVE_EVENT_LISTENER ] ( meta . eventName , eventTask . invoke ,
166- meta . useCapturing ) ;
167- }
168-
169- function zoneAwareAddEventListener ( self : any , args : any [ ] ) {
170- const eventName : string = args [ 0 ] ;
171- const handler : EventListenerOrEventListenerObject = args [ 1 ] ;
172- const useCapturing : boolean = args [ 2 ] || false ;
173- // - Inside a Web Worker, `this` is undefined, the context is `global`
174- // - When `addEventListener` is called on the global context in strict mode, `this` is undefined
175- // see https://github.com/angular/zone.js/issues/190
176- const target = self || _global ;
177- let delegate : EventListener = null ;
178- if ( typeof handler == 'function' ) {
179- delegate = < EventListener > handler ;
180- } else if ( handler && ( < EventListenerObject > handler ) . handleEvent ) {
181- delegate = ( event ) => ( < EventListenerObject > handler ) . handleEvent ( event ) ;
182- }
183- var validZoneHandler = false ;
184- try {
185- // In cross site contexts (such as WebDriver frameworks like Selenium),
186- // accessing the handler object here will cause an exception to be thrown which
187- // will fail tests prematurely.
188- validZoneHandler = handler && handler . toString ( ) === "[object FunctionWrapper]" ;
189- } catch ( e ) {
190- // Returning nothing here is fine, because objects in a cross-site context are unusable
191- return ;
160+ function scheduleEventListener ( eventTask : Task ) : any {
161+ const meta = < ListenerTaskMeta > eventTask . data ;
162+ attachRegisteredEvent ( meta . target , eventTask ) ;
163+ return meta . target [ addFnSymbol ] ( meta . eventName , eventTask . invoke ,
164+ meta . useCapturing ) ;
192165 }
193- // Ignore special listeners of IE11 & Edge dev tools, see https://github.com/angular/zone.js/issues/150
194- if ( ! delegate || validZoneHandler ) {
195- return target [ SYMBOL_ADD_EVENT_LISTENER ] ( eventName , handler , useCapturing ) ;
196- }
197- const eventTask : Task
198- = findExistingRegisteredTask ( target , handler , eventName , useCapturing , false ) ;
199- if ( eventTask ) {
200- // we already registered, so this will have noop.
201- return target [ SYMBOL_ADD_EVENT_LISTENER ] ( eventName , eventTask . invoke , useCapturing ) ;
166+
167+ function cancelEventListener ( eventTask : Task ) : void {
168+ const meta = < ListenerTaskMeta > eventTask . data ;
169+ findExistingRegisteredTask ( meta . target , eventTask . invoke , meta . eventName ,
170+ meta . useCapturing , true ) ;
171+ meta . target [ removeFnSymbol ] ( meta . eventName , eventTask . invoke ,
172+ meta . useCapturing ) ;
202173 }
203- const zone : Zone = Zone . current ;
204- const source = target . constructor [ 'name' ] + '.addEventListener:' + eventName ;
205- const data : ListenerTaskMeta = {
206- target : target ,
207- eventName : eventName ,
208- name : eventName ,
209- useCapturing : useCapturing ,
210- handler : handler
174+
175+ return function zoneAwareAddListener ( self : any , args : any [ ] ) {
176+ const eventName : string = args [ 0 ] ;
177+ const handler : EventListenerOrEventListenerObject = args [ 1 ] ;
178+ const useCapturing : boolean = args [ 2 ] || defaultUseCapturing ;
179+ // - Inside a Web Worker, `this` is undefined, the context is `global`
180+ // - When `addEventListener` is called on the global context in strict mode, `this` is undefined
181+ // see https://github.com/angular/zone.js/issues/190
182+ const target = self || _global ;
183+ let delegate : EventListener = null ;
184+ if ( typeof handler == 'function' ) {
185+ delegate = < EventListener > handler ;
186+ } else if ( handler && ( < EventListenerObject > handler ) . handleEvent ) {
187+ delegate = ( event ) => ( < EventListenerObject > handler ) . handleEvent ( event ) ;
188+ }
189+ var validZoneHandler = false ;
190+ try {
191+ // In cross site contexts (such as WebDriver frameworks like Selenium),
192+ // accessing the handler object here will cause an exception to be thrown which
193+ // will fail tests prematurely.
194+ validZoneHandler = handler && handler . toString ( ) === "[object FunctionWrapper]" ;
195+ } catch ( e ) {
196+ // Returning nothing here is fine, because objects in a cross-site context are unusable
197+ return ;
198+ }
199+ // Ignore special listeners of IE11 & Edge dev tools, see https://github.com/angular/zone.js/issues/150
200+ if ( ! delegate || validZoneHandler ) {
201+ return target [ addFnSymbol ] ( eventName , handler , useCapturing ) ;
202+ }
203+
204+ if ( ! allowDuplicates ) {
205+ const eventTask : Task
206+ = findExistingRegisteredTask ( target , handler , eventName , useCapturing , false ) ;
207+ if ( eventTask ) {
208+ // we already registered, so this will have noop.
209+ return target [ addFnSymbol ] ( eventName , eventTask . invoke , useCapturing ) ;
210+ }
211+ }
212+
213+ const zone : Zone = Zone . current ;
214+ const source = target . constructor [ 'name' ] + '.' + addFnName + ':' + eventName ;
215+ const data : ListenerTaskMeta = {
216+ target : target ,
217+ eventName : eventName ,
218+ name : eventName ,
219+ useCapturing : useCapturing ,
220+ handler : handler
221+ } ;
222+ zone . scheduleEventTask ( source , delegate , data , scheduleEventListener , cancelEventListener ) ;
211223 } ;
212- zone . scheduleEventTask ( source , delegate , data , scheduleEventListener , cancelEventListener ) ;
213224}
214225
215- function zoneAwareRemoveEventListener ( self : any , args : any [ ] ) {
216- const eventName : string = args [ 0 ] ;
217- const handler : EventListenerOrEventListenerObject = args [ 1 ] ;
218- const useCapturing : boolean = args [ 2 ] || false ;
219- // - Inside a Web Worker, `this` is undefined, the context is `global`
220- // - When `addEventListener` is called on the global context in strict mode, `this` is undefined
221- // see https://github.com/angular/zone.js/issues/190
222- const target = self || _global ;
223- const eventTask = findExistingRegisteredTask ( target , handler , eventName , useCapturing , true ) ;
224- if ( eventTask ) {
225- eventTask . zone . cancelTask ( eventTask ) ;
226- } else {
227- target [ SYMBOL_REMOVE_EVENT_LISTENER ] ( eventName , handler , useCapturing ) ;
226+ export function makeZoneAwareRemoveListener ( fnName : string , useCapturingParam : boolean = true ) {
227+ const symbol = zoneSymbol ( fnName ) ;
228+ const defaultUseCapturing = useCapturingParam ? false : undefined ;
229+
230+ return function zoneAwareRemoveListener ( self : any , args : any [ ] ) {
231+ const eventName : string = args [ 0 ] ;
232+ const handler : EventListenerOrEventListenerObject = args [ 1 ] ;
233+ const useCapturing : boolean = args [ 2 ] || defaultUseCapturing ;
234+ // - Inside a Web Worker, `this` is undefined, the context is `global`
235+ // - When `addEventListener` is called on the global context in strict mode, `this` is undefined
236+ // see https://github.com/angular/zone.js/issues/190
237+ const target = self || _global ;
238+ const eventTask = findExistingRegisteredTask ( target , handler , eventName , useCapturing , true ) ;
239+ if ( eventTask ) {
240+ eventTask . zone . cancelTask ( eventTask ) ;
241+ } else {
242+ target [ symbol ] ( eventName , handler , useCapturing ) ;
243+ }
244+ } ;
245+ }
246+
247+ export function makeZoneAwareListeners ( fnName : string ) {
248+ const symbol = zoneSymbol ( fnName ) ;
249+
250+ return function zoneAwareEventListeners ( self : any , args : any [ ] ) {
251+ const eventName : string = args [ 0 ] ;
252+ const target = self || _global ;
253+ return target [ EVENT_TASKS ]
254+ . filter ( task => task . data . eventName === eventName )
255+ . map ( task => task . data . handler ) ;
228256 }
229257}
230258
259+ const zoneAwareAddEventListener = makeZoneAwareAddListener ( ADD_EVENT_LISTENER , REMOVE_EVENT_LISTENER ) ;
260+ const zoneAwareRemoveEventListener = makeZoneAwareRemoveListener ( REMOVE_EVENT_LISTENER ) ;
261+
231262export function patchEventTargetMethods ( obj : any ) : boolean {
232263 if ( obj && obj . addEventListener ) {
233264 patchMethod ( obj , ADD_EVENT_LISTENER , ( ) => zoneAwareAddEventListener ) ;
@@ -236,7 +267,8 @@ export function patchEventTargetMethods(obj: any): boolean {
236267 } else {
237268 return false ;
238269 }
239- } ;
270+ }
271+
240272
241273const originalInstanceKey = zoneSymbol ( 'originalInstance' ) ;
242274
0 commit comments