Skip to content
This repository was archived by the owner on Feb 26, 2024. It is now read-only.

Commit 0fd8c03

Browse files
JiaLiPassionmhevery
authored andcommitted
refactor: add generic patch eventtarget method
* add generic patch eventtarget method * export types * unify api name
1 parent ba7858c commit 0fd8c03

File tree

1 file changed

+62
-38
lines changed

1 file changed

+62
-38
lines changed

Diff for: lib/common/utils.ts

+62-38
Original file line numberDiff line numberDiff line change
@@ -118,19 +118,22 @@ const EVENT_TASKS = zoneSymbol('eventTasks');
118118
const ADD_EVENT_LISTENER = 'addEventListener';
119119
const REMOVE_EVENT_LISTENER = 'removeEventListener';
120120

121-
interface NestedEventListener {
121+
export interface NestedEventListener {
122122
listener?: EventListenerOrEventListenerObject;
123123
}
124124

125-
declare type NestedEventListenerOrEventListenerObject =
125+
export declare type NestedEventListenerOrEventListenerObject =
126126
NestedEventListener | EventListener | EventListenerObject;
127127

128-
interface ListenerTaskMeta extends TaskData {
128+
export interface ListenerTaskMeta extends TaskData {
129129
useCapturing: boolean;
130130
eventName: string;
131131
handler: NestedEventListenerOrEventListenerObject;
132132
target: any;
133133
name: string;
134+
invokeAddFunc: (addFnSymbol: any, delegate: Task |
135+
NestedEventListenerOrEventListenerObject) => any,
136+
invokeRemoveFunc: (removeFnSymbol: any, delegate: Task | NestedEventListenerOrEventListenerObject) => any
134137
}
135138

136139
function findExistingRegisteredTask(
@@ -185,95 +188,113 @@ function attachRegisteredEvent(target: any, eventTask: Task, isPrepend: boolean)
185188
}
186189
}
187190

191+
const defaultListenerMetaCreator = (self: any, args: any[]) => {
192+
return {
193+
useCapturing: args[2],
194+
eventName: args[0],
195+
handler: args[1],
196+
target: self || _global,
197+
name: args[0],
198+
invokeAddFunc: function (addFnSymbol: any, delegate: Task | NestedEventListenerOrEventListenerObject) {
199+
if (delegate && (<Task>delegate).invoke) {
200+
return this.target[addFnSymbol](this.eventName, (<Task>delegate).invoke, this.useCapturing);
201+
} else {
202+
return this.target[addFnSymbol](this.eventName, delegate, this.useCapturing);
203+
}
204+
},
205+
invokeRemoveFunc: function (removeFnSymbol:any, delegate: Task | NestedEventListenerOrEventListenerObject) {
206+
if (delegate && (<Task>delegate).invoke) {
207+
return this.target[removeFnSymbol](this.eventName, (<Task>delegate).invoke, this.useCapturing);
208+
} else {
209+
return this.target[removeFnSymbol](this.eventName, delegate, this.useCapturing);
210+
}
211+
}
212+
};
213+
}
214+
188215
export function makeZoneAwareAddListener(
189216
addFnName: string, removeFnName: string, useCapturingParam: boolean = true,
190-
allowDuplicates: boolean = false, isPrepend: boolean = false) {
217+
allowDuplicates: boolean = false, isPrepend: boolean = false,
218+
metaCreator: (self: any, args: any[]) => ListenerTaskMeta = defaultListenerMetaCreator) {
191219
const addFnSymbol = zoneSymbol(addFnName);
192220
const removeFnSymbol = zoneSymbol(removeFnName);
193221
const defaultUseCapturing = useCapturingParam ? false : undefined;
194222

195223
function scheduleEventListener(eventTask: Task): any {
196224
const meta = <ListenerTaskMeta>eventTask.data;
197225
attachRegisteredEvent(meta.target, eventTask, isPrepend);
198-
return meta.target[addFnSymbol](meta.eventName, eventTask.invoke, meta.useCapturing);
226+
return meta.invokeAddFunc(addFnSymbol, eventTask);
199227
}
200228

201229
function cancelEventListener(eventTask: Task): void {
202230
const meta = <ListenerTaskMeta>eventTask.data;
203231
findExistingRegisteredTask(
204232
meta.target, eventTask.invoke, meta.eventName, meta.useCapturing, true);
205-
meta.target[removeFnSymbol](meta.eventName, eventTask.invoke, meta.useCapturing);
233+
return meta.invokeRemoveFunc(removeFnSymbol, eventTask);
206234
}
207235

208236
return function zoneAwareAddListener(self: any, args: any[]) {
209-
const eventName: string = args[0];
210-
const handler: EventListenerOrEventListenerObject = args[1];
211-
const useCapturing: boolean = args[2] || defaultUseCapturing;
237+
const data: ListenerTaskMeta = metaCreator(self, args);
238+
239+
data.useCapturing = data.useCapturing || defaultUseCapturing;
212240
// - Inside a Web Worker, `this` is undefined, the context is `global`
213241
// - When `addEventListener` is called on the global context in strict mode, `this` is undefined
214242
// see https://github.com/angular/zone.js/issues/190
215-
const target = self || _global;
216243
let delegate: EventListener = null;
217-
if (typeof handler == 'function') {
218-
delegate = <EventListener>handler;
219-
} else if (handler && (<EventListenerObject>handler).handleEvent) {
220-
delegate = (event) => (<EventListenerObject>handler).handleEvent(event);
244+
if (typeof data.handler == 'function') {
245+
delegate = <EventListener>data.handler;
246+
} else if (data.handler && (<EventListenerObject>data.handler).handleEvent) {
247+
delegate = (event) => (<EventListenerObject>data.handler).handleEvent(event);
221248
}
222249
var validZoneHandler = false;
223250
try {
224251
// In cross site contexts (such as WebDriver frameworks like Selenium),
225252
// accessing the handler object here will cause an exception to be thrown which
226253
// will fail tests prematurely.
227-
validZoneHandler = handler && handler.toString() === '[object FunctionWrapper]';
254+
validZoneHandler = data.handler && data.handler.toString() === '[object FunctionWrapper]';
228255
} catch (e) {
229256
// Returning nothing here is fine, because objects in a cross-site context are unusable
230257
return;
231258
}
232259
// Ignore special listeners of IE11 & Edge dev tools, see
233260
// https://github.com/angular/zone.js/issues/150
234261
if (!delegate || validZoneHandler) {
235-
return target[addFnSymbol](eventName, handler, useCapturing);
262+
return data.invokeAddFunc(addFnSymbol, data.handler);
236263
}
237264

238265
if (!allowDuplicates) {
239266
const eventTask: Task =
240-
findExistingRegisteredTask(target, handler, eventName, useCapturing, false);
267+
findExistingRegisteredTask(data.target, data.handler, data.eventName, data.useCapturing, false);
241268
if (eventTask) {
242269
// we already registered, so this will have noop.
243-
return target[addFnSymbol](eventName, eventTask.invoke, useCapturing);
270+
return data.invokeAddFunc(addFnSymbol, eventTask);
244271
}
245272
}
246273

247274
const zone: Zone = Zone.current;
248-
const source = target.constructor['name'] + '.' + addFnName + ':' + eventName;
249-
const data: ListenerTaskMeta = {
250-
target: target,
251-
eventName: eventName,
252-
name: eventName,
253-
useCapturing: useCapturing,
254-
handler: handler
255-
};
275+
const source = data.target.constructor['name'] + '.' + addFnName + ':' + data.eventName;
276+
256277
zone.scheduleEventTask(source, delegate, data, scheduleEventListener, cancelEventListener);
257278
};
258279
}
259280

260-
export function makeZoneAwareRemoveListener(fnName: string, useCapturingParam: boolean = true) {
281+
export function makeZoneAwareRemoveListener(fnName: string, useCapturingParam: boolean = true,
282+
metaCreator: (self: any, args: any[]) => ListenerTaskMeta = defaultListenerMetaCreator) {
261283
const symbol = zoneSymbol(fnName);
262284
const defaultUseCapturing = useCapturingParam ? false : undefined;
263285

264286
return function zoneAwareRemoveListener(self: any, args: any[]) {
265-
const eventName: string = args[0];
266-
const handler: EventListenerOrEventListenerObject = args[1];
267-
const useCapturing: boolean = args[2] || defaultUseCapturing;
287+
const data = metaCreator(self, args);
288+
data.useCapturing = data.useCapturing || defaultUseCapturing;
268289
// - Inside a Web Worker, `this` is undefined, the context is `global`
269290
// - When `addEventListener` is called on the global context in strict mode, `this` is undefined
270291
// see https://github.com/angular/zone.js/issues/190
271-
const target = self || _global;
272-
const eventTask = findExistingRegisteredTask(target, handler, eventName, useCapturing, true);
292+
const eventTask = findExistingRegisteredTask(data.target, data.handler, data.eventName,
293+
data.useCapturing, true);
273294
if (eventTask) {
274295
eventTask.zone.cancelTask(eventTask);
275296
} else {
276-
target[symbol](eventName, handler, useCapturing);
297+
data.invokeRemoveFunc(symbol, data.handler);
277298
}
278299
};
279300
}
@@ -323,17 +344,20 @@ const zoneAwareAddEventListener =
323344
makeZoneAwareAddListener(ADD_EVENT_LISTENER, REMOVE_EVENT_LISTENER);
324345
const zoneAwareRemoveEventListener = makeZoneAwareRemoveListener(REMOVE_EVENT_LISTENER);
325346

326-
export function patchEventTargetMethods(obj: any): boolean {
327-
if (obj && obj.addEventListener) {
328-
patchMethod(obj, ADD_EVENT_LISTENER, () => zoneAwareAddEventListener);
329-
patchMethod(obj, REMOVE_EVENT_LISTENER, () => zoneAwareRemoveEventListener);
347+
export function patchEventTargetMethods(obj: any, addFnName: string = ADD_EVENT_LISTENER,
348+
removeFnName: string = REMOVE_EVENT_LISTENER,
349+
metaCreator: (self: any, args: any[]) => ListenerTaskMeta = defaultListenerMetaCreator): boolean {
350+
if (obj && obj[addFnName]) {
351+
patchMethod(obj, addFnName,
352+
() => makeZoneAwareAddListener(addFnName, removeFnName, true, false, false, metaCreator));
353+
patchMethod(obj, removeFnName,
354+
() => makeZoneAwareRemoveListener(removeFnName, true, metaCreator));
330355
return true;
331356
} else {
332357
return false;
333358
}
334359
}
335360

336-
337361
const originalInstanceKey = zoneSymbol('originalInstance');
338362

339363
// wrap some native API on `window`

0 commit comments

Comments
 (0)