| 
									
										
										
										
											2019-06-01 00:56:07 +09:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @license | 
					
						
							|  |  |  |  * Copyright Google Inc. All Rights Reserved. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Use of this source code is governed by an MIT-style license that can be | 
					
						
							|  |  |  |  * found in the LICENSE file at https://angular.io/license
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * @fileoverview | 
					
						
							|  |  |  |  * @suppress {missingRequire} | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import {patchMethod, scheduleMacroTaskWithCurrentZone, zoneSymbol} from './utils'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const taskSymbol = zoneSymbol('zoneTask'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | interface TimerOptions extends TaskData { | 
					
						
							|  |  |  |   handleId?: number; | 
					
						
							|  |  |  |   args: any[]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export function patchTimer(window: any, setName: string, cancelName: string, nameSuffix: string) { | 
					
						
							|  |  |  |   let setNative: Function|null = null; | 
					
						
							|  |  |  |   let clearNative: Function|null = null; | 
					
						
							|  |  |  |   setName += nameSuffix; | 
					
						
							|  |  |  |   cancelName += nameSuffix; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const tasksByHandleId: {[id: number]: Task} = {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function scheduleTask(task: Task) { | 
					
						
							|  |  |  |     const data = <TimerOptions>task.data; | 
					
						
							| 
									
										
										
										
											2019-06-26 10:33:02 +02:00
										 |  |  |     function timer(this: unknown) { | 
					
						
							| 
									
										
										
										
											2019-06-01 00:56:07 +09:00
										 |  |  |       try { | 
					
						
							|  |  |  |         task.invoke.apply(this, arguments); | 
					
						
							|  |  |  |       } finally { | 
					
						
							|  |  |  |         // issue-934, task will be cancelled
 | 
					
						
							|  |  |  |         // even it is a periodic task such as
 | 
					
						
							|  |  |  |         // setInterval
 | 
					
						
							|  |  |  |         if (!(task.data && task.data.isPeriodic)) { | 
					
						
							|  |  |  |           if (typeof data.handleId === 'number') { | 
					
						
							|  |  |  |             // in non-nodejs env, we remove timerId
 | 
					
						
							|  |  |  |             // from local cache
 | 
					
						
							|  |  |  |             delete tasksByHandleId[data.handleId]; | 
					
						
							|  |  |  |           } else if (data.handleId) { | 
					
						
							|  |  |  |             // Node returns complex objects as handleIds
 | 
					
						
							|  |  |  |             // we remove task reference from timer object
 | 
					
						
							|  |  |  |             (data.handleId as any)[taskSymbol] = null; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     data.args[0] = timer; | 
					
						
							|  |  |  |     data.handleId = setNative !.apply(window, data.args); | 
					
						
							|  |  |  |     return task; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function clearTask(task: Task) { return clearNative !((<TimerOptions>task.data).handleId); } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   setNative = | 
					
						
							|  |  |  |       patchMethod(window, setName, (delegate: Function) => function(self: any, args: any[]) { | 
					
						
							|  |  |  |         if (typeof args[0] === 'function') { | 
					
						
							|  |  |  |           const options: TimerOptions = { | 
					
						
							|  |  |  |             isPeriodic: nameSuffix === 'Interval', | 
					
						
							|  |  |  |             delay: (nameSuffix === 'Timeout' || nameSuffix === 'Interval') ? args[1] || 0 : | 
					
						
							|  |  |  |                                                                              undefined, | 
					
						
							|  |  |  |             args: args | 
					
						
							|  |  |  |           }; | 
					
						
							|  |  |  |           const task = | 
					
						
							|  |  |  |               scheduleMacroTaskWithCurrentZone(setName, args[0], options, scheduleTask, clearTask); | 
					
						
							|  |  |  |           if (!task) { | 
					
						
							|  |  |  |             return task; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           // Node.js must additionally support the ref and unref functions.
 | 
					
						
							|  |  |  |           const handle: any = (<TimerOptions>task.data).handleId; | 
					
						
							|  |  |  |           if (typeof handle === 'number') { | 
					
						
							|  |  |  |             // for non nodejs env, we save handleId: task
 | 
					
						
							|  |  |  |             // mapping in local cache for clearTimeout
 | 
					
						
							|  |  |  |             tasksByHandleId[handle] = task; | 
					
						
							|  |  |  |           } else if (handle) { | 
					
						
							|  |  |  |             // for nodejs env, we save task
 | 
					
						
							|  |  |  |             // reference in timerId Object for clearTimeout
 | 
					
						
							|  |  |  |             handle[taskSymbol] = task; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           // check whether handle is null, because some polyfill or browser
 | 
					
						
							|  |  |  |           // may return undefined from setTimeout/setInterval/setImmediate/requestAnimationFrame
 | 
					
						
							|  |  |  |           if (handle && handle.ref && handle.unref && typeof handle.ref === 'function' && | 
					
						
							|  |  |  |               typeof handle.unref === 'function') { | 
					
						
							|  |  |  |             (<any>task).ref = (<any>handle).ref.bind(handle); | 
					
						
							|  |  |  |             (<any>task).unref = (<any>handle).unref.bind(handle); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           if (typeof handle === 'number' || handle) { | 
					
						
							|  |  |  |             return handle; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           return task; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           // cause an error by calling it directly.
 | 
					
						
							|  |  |  |           return delegate.apply(window, args); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   clearNative = | 
					
						
							|  |  |  |       patchMethod(window, cancelName, (delegate: Function) => function(self: any, args: any[]) { | 
					
						
							|  |  |  |         const id = args[0]; | 
					
						
							|  |  |  |         let task: Task; | 
					
						
							|  |  |  |         if (typeof id === 'number') { | 
					
						
							|  |  |  |           // non nodejs env.
 | 
					
						
							|  |  |  |           task = tasksByHandleId[id]; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           // nodejs env.
 | 
					
						
							|  |  |  |           task = id && id[taskSymbol]; | 
					
						
							|  |  |  |           // other environments.
 | 
					
						
							|  |  |  |           if (!task) { | 
					
						
							|  |  |  |             task = id; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (task && typeof task.type === 'string') { | 
					
						
							|  |  |  |           if (task.state !== 'notScheduled' && | 
					
						
							|  |  |  |               (task.cancelFn && task.data !.isPeriodic || task.runCount === 0)) { | 
					
						
							|  |  |  |             if (typeof id === 'number') { | 
					
						
							|  |  |  |               delete tasksByHandleId[id]; | 
					
						
							|  |  |  |             } else if (id) { | 
					
						
							|  |  |  |               id[taskSymbol] = null; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             // Do not cancel already canceled functions
 | 
					
						
							|  |  |  |             task.zone.cancelTask(task); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           // cause an error by calling it directly.
 | 
					
						
							|  |  |  |           delegate.apply(window, args); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | } |