@@ -108,10 +108,11 @@ export function patchOnProperties(obj: any, properties: string[]) {
108
108
} ;
109
109
110
110
const EVENT_TASKS = zoneSymbol ( 'eventTasks' ) ;
111
+
112
+ // For EventTarget
111
113
const ADD_EVENT_LISTENER = 'addEventListener' ;
112
114
const 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
+
115
116
116
117
interface ListenerTaskMeta extends TaskData {
117
118
useCapturing : boolean ;
@@ -151,83 +152,113 @@ function attachRegisteredEvent(target: any, eventTask: Task): void {
151
152
eventTasks . push ( eventTask ) ;
152
153
}
153
154
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 ;
160
159
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 ) ;
192
165
}
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 ) ;
202
173
}
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 ) ;
211
223
} ;
212
- zone . scheduleEventTask ( source , delegate , data , scheduleEventListener , cancelEventListener ) ;
213
224
}
214
225
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 ) ;
228
256
}
229
257
}
230
258
259
+ const zoneAwareAddEventListener = makeZoneAwareAddListener ( ADD_EVENT_LISTENER , REMOVE_EVENT_LISTENER ) ;
260
+ const zoneAwareRemoveEventListener = makeZoneAwareRemoveListener ( REMOVE_EVENT_LISTENER ) ;
261
+
231
262
export function patchEventTargetMethods ( obj : any ) : boolean {
232
263
if ( obj && obj . addEventListener ) {
233
264
patchMethod ( obj , ADD_EVENT_LISTENER , ( ) => zoneAwareAddEventListener ) ;
@@ -236,7 +267,8 @@ export function patchEventTargetMethods(obj: any): boolean {
236
267
} else {
237
268
return false ;
238
269
}
239
- } ;
270
+ }
271
+
240
272
241
273
const originalInstanceKey = zoneSymbol ( 'originalInstance' ) ;
242
274
0 commit comments