@@ -181,8 +181,18 @@ export interface NestedEventListener { listener?: EventListenerOrEventListenerOb
181181export declare type NestedEventListenerOrEventListenerObject =
182182 NestedEventListener | EventListener | EventListenerObject ;
183183
184+ export interface EventListenerOptions { capture ?: boolean ; }
185+
186+ export interface AddEventListenerOptions extends EventListenerOptions {
187+ passive ?: boolean ;
188+ once ?: boolean ;
189+ }
190+
191+ export declare type EventListenerOptionsOrCapture =
192+ EventListenerOptions | AddEventListenerOptions | boolean ;
193+
184194export interface ListenerTaskMeta extends TaskData {
185- useCapturing : boolean ;
195+ options : EventListenerOptionsOrCapture ;
186196 eventName : string ;
187197 handler : NestedEventListenerOrEventListenerObject ;
188198 target : any ;
@@ -193,16 +203,31 @@ export interface ListenerTaskMeta extends TaskData {
193203 ( removeFnSymbol : any , delegate : Task | NestedEventListenerOrEventListenerObject ) => any ;
194204}
195205
206+ // compare the EventListenerOptionsOrCapture
207+ // 1. if the options is usCapture: boolean, compare the useCpature values directly
208+ // 2. if the options is EventListerOptions, only compare the capture
209+ function compareEventListenerOptions (
210+ left : EventListenerOptionsOrCapture , right : EventListenerOptionsOrCapture ) : boolean {
211+ const leftCapture : any = ( typeof left === 'boolean' ) ?
212+ left :
213+ ( ( typeof left === 'object' ) ? ( left && left . capture ) : false ) ;
214+ const rightCapture : any = ( typeof right === 'boolean' ) ?
215+ right :
216+ ( ( typeof right === 'object' ) ? ( right && right . capture ) : false ) ;
217+ return ! ! leftCapture === ! ! rightCapture ;
218+ }
219+
196220function findExistingRegisteredTask (
197- target : any , handler : any , name : string , capture : boolean , remove : boolean ) : Task {
221+ target : any , handler : any , name : string , options : EventListenerOptionsOrCapture ,
222+ remove : boolean ) : Task {
198223 const eventTasks : Task [ ] = target [ EVENT_TASKS ] ;
199224 if ( eventTasks ) {
200225 for ( let i = 0 ; i < eventTasks . length ; i ++ ) {
201226 const eventTask = eventTasks [ i ] ;
202227 const data = < ListenerTaskMeta > eventTask . data ;
203228 const listener = < NestedEventListener > data . handler ;
204229 if ( ( data . handler === handler || listener . listener === handler ) &&
205- data . useCapturing === capture && data . eventName === name ) {
230+ compareEventListenerOptions ( data . options , options ) && data . eventName === name ) {
206231 if ( remove ) {
207232 eventTasks . splice ( i , 1 ) ;
208233 }
@@ -213,15 +238,14 @@ function findExistingRegisteredTask(
213238 return null ;
214239}
215240
216- function findAllExistingRegisteredTasks (
217- target : any , name : string , capture : boolean , remove : boolean ) : Task [ ] {
241+ function findAllExistingRegisteredTasks ( target : any , name : string , remove : boolean ) : Task [ ] {
218242 const eventTasks : Task [ ] = target [ EVENT_TASKS ] ;
219243 if ( eventTasks ) {
220244 const result = [ ] ;
221245 for ( let i = eventTasks . length - 1 ; i >= 0 ; i -- ) {
222246 const eventTask = eventTasks [ i ] ;
223247 const data = < ListenerTaskMeta > eventTask . data ;
224- if ( data . eventName === name && data . useCapturing === capture ) {
248+ if ( data . eventName === name ) {
225249 result . push ( eventTask ) ;
226250 if ( remove ) {
227251 eventTasks . splice ( i , 1 ) ;
@@ -247,7 +271,7 @@ function attachRegisteredEvent(target: any, eventTask: Task, isPrepend: boolean)
247271
248272const defaultListenerMetaCreator = ( self : any , args : any [ ] ) => {
249273 return {
250- useCapturing : args [ 2 ] ,
274+ options : args [ 2 ] ,
251275 eventName : args [ 0 ] ,
252276 handler : args [ 1 ] ,
253277 target : self || _global ,
@@ -259,16 +283,15 @@ const defaultListenerMetaCreator = (self: any, args: any[]) => {
259283 // remove the delegate directly and try catch error
260284 if ( ! this . crossContext ) {
261285 if ( delegate && ( < Task > delegate ) . invoke ) {
262- return this . target [ addFnSymbol ] (
263- this . eventName , ( < Task > delegate ) . invoke , this . useCapturing ) ;
286+ return this . target [ addFnSymbol ] ( this . eventName , ( < Task > delegate ) . invoke , this . options ) ;
264287 } else {
265- return this . target [ addFnSymbol ] ( this . eventName , delegate , this . useCapturing ) ;
288+ return this . target [ addFnSymbol ] ( this . eventName , delegate , this . options ) ;
266289 }
267290 } else {
268291 // add a if/else branch here for performance concern, for most times
269292 // cross site context is false, so we don't need to try/catch
270293 try {
271- return this . target [ addFnSymbol ] ( this . eventName , delegate , this . useCapturing ) ;
294+ return this . target [ addFnSymbol ] ( this . eventName , delegate , this . options ) ;
272295 } catch ( err ) {
273296 // do nothing here is fine, because objects in a cross-site context are unusable
274297 }
@@ -280,16 +303,15 @@ const defaultListenerMetaCreator = (self: any, args: any[]) => {
280303 // remove the delegate directly and try catch error
281304 if ( ! this . crossContext ) {
282305 if ( delegate && ( < Task > delegate ) . invoke ) {
283- return this . target [ removeFnSymbol ] (
284- this . eventName , ( < Task > delegate ) . invoke , this . useCapturing ) ;
306+ return this . target [ removeFnSymbol ] ( this . eventName , ( < Task > delegate ) . invoke , this . options ) ;
285307 } else {
286- return this . target [ removeFnSymbol ] ( this . eventName , delegate , this . useCapturing ) ;
308+ return this . target [ removeFnSymbol ] ( this . eventName , delegate , this . options ) ;
287309 }
288310 } else {
289311 // add a if/else branch here for performance concern, for most times
290312 // cross site context is false, so we don't need to try/catch
291313 try {
292- return this . target [ removeFnSymbol ] ( this . eventName , delegate , this . useCapturing ) ;
314+ return this . target [ removeFnSymbol ] ( this . eventName , delegate , this . options ) ;
293315 } catch ( err ) {
294316 // do nothing here is fine, because objects in a cross-site context are unusable
295317 }
@@ -314,15 +336,14 @@ export function makeZoneAwareAddListener(
314336
315337 function cancelEventListener ( eventTask : Task ) : void {
316338 const meta = < ListenerTaskMeta > eventTask . data ;
317- findExistingRegisteredTask (
318- meta . target , eventTask . invoke , meta . eventName , meta . useCapturing , true ) ;
339+ findExistingRegisteredTask ( meta . target , eventTask . invoke , meta . eventName , meta . options , true ) ;
319340 return meta . invokeRemoveFunc ( removeFnSymbol , eventTask ) ;
320341 }
321342
322343 return function zoneAwareAddListener ( self : any , args : any [ ] ) {
323344 const data : ListenerTaskMeta = metaCreator ( self , args ) ;
324345
325- data . useCapturing = data . useCapturing || defaultUseCapturing ;
346+ data . options = data . options || defaultUseCapturing ;
326347 // - Inside a Web Worker, `this` is undefined, the context is `global`
327348 // - When `addEventListener` is called on the global context in strict mode, `this` is undefined
328349 // see https://github.com/angular/zone.js/issues/190
@@ -351,7 +372,7 @@ export function makeZoneAwareAddListener(
351372
352373 if ( ! allowDuplicates ) {
353374 const eventTask : Task = findExistingRegisteredTask (
354- data . target , data . handler , data . eventName , data . useCapturing , false ) ;
375+ data . target , data . handler , data . eventName , data . options , false ) ;
355376 if ( eventTask ) {
356377 // we already registered, so this will have noop.
357378 return data . invokeAddFunc ( addFnSymbol , eventTask ) ;
@@ -374,7 +395,7 @@ export function makeZoneAwareRemoveListener(
374395 return function zoneAwareRemoveListener ( self : any , args : any [ ] ) {
375396 const data = metaCreator ( self , args ) ;
376397
377- data . useCapturing = data . useCapturing || defaultUseCapturing ;
398+ data . options = data . options || defaultUseCapturing ;
378399 // - Inside a Web Worker, `this` is undefined, the context is `global`
379400 // - When `addEventListener` is called on the global context in strict mode, `this` is undefined
380401 // see https://github.com/angular/zone.js/issues/190
@@ -399,8 +420,8 @@ export function makeZoneAwareRemoveListener(
399420 if ( ! delegate || validZoneHandler ) {
400421 return data . invokeRemoveFunc ( symbol , data . handler ) ;
401422 }
402- const eventTask = findExistingRegisteredTask (
403- data . target , data . handler , data . eventName , data . useCapturing , true ) ;
423+ const eventTask =
424+ findExistingRegisteredTask ( data . target , data . handler , data . eventName , data . options , true ) ;
404425 if ( eventTask ) {
405426 eventTask . zone . cancelTask ( eventTask ) ;
406427 } else {
@@ -409,9 +430,8 @@ export function makeZoneAwareRemoveListener(
409430 } ;
410431}
411432
412- export function makeZoneAwareRemoveAllListeners ( fnName : string , useCapturingParam : boolean = true ) {
433+ export function makeZoneAwareRemoveAllListeners ( fnName : string ) {
413434 const symbol = zoneSymbol ( fnName ) ;
414- const defaultUseCapturing = useCapturingParam ? false : undefined ;
415435
416436 return function zoneAwareRemoveAllListener ( self : any , args : any [ ] ) {
417437 const target = self || _global ;
@@ -424,13 +444,12 @@ export function makeZoneAwareRemoveAllListeners(fnName: string, useCapturingPara
424444 return ;
425445 }
426446 const eventName = args [ 0 ] ;
427- const useCapturing = args [ 1 ] || defaultUseCapturing ;
428447 // call this function just remove the related eventTask from target[EVENT_TASKS]
429- findAllExistingRegisteredTasks ( target , eventName , useCapturing , true ) ;
430448 // we don't need useCapturing here because useCapturing is just for DOM, and
431449 // removeAllListeners should only be called by node eventEmitter
432450 // and we don't cancel Task either, because call native eventEmitter.removeAllListeners will
433451 // will do remove listener(cancelTask) for us
452+ findAllExistingRegisteredTasks ( target , eventName , true ) ;
434453 target [ symbol ] ( eventName ) ;
435454 } ;
436455}
0 commit comments