136 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			136 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
/**
 | 
						|
 * @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;
 | 
						|
    function timer(this: unknown) {
 | 
						|
      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);
 | 
						|
        }
 | 
						|
      });
 | 
						|
}
 |