1 // Mock setTimeout, clearTimeout
  2 // Contributed by Pivotal Computer Systems, www.pivotalsf.com
  3 
  4 jasmine.FakeTimer = function() {
  5   this.reset();
  6 
  7   var self = this;
  8   self.setTimeout = function(funcToCall, millis) {
  9     self.timeoutsMade++;
 10     self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false);
 11     return self.timeoutsMade;
 12   };
 13 
 14   self.setInterval = function(funcToCall, millis) {
 15     self.timeoutsMade++;
 16     self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true);
 17     return self.timeoutsMade;
 18   };
 19 
 20   self.clearTimeout = function(timeoutKey) {
 21     self.scheduledFunctions[timeoutKey] = jasmine.undefined;
 22   };
 23 
 24   self.clearInterval = function(timeoutKey) {
 25     self.scheduledFunctions[timeoutKey] = jasmine.undefined;
 26   };
 27 
 28 };
 29 
 30 jasmine.FakeTimer.prototype.reset = function() {
 31   this.timeoutsMade = 0;
 32   this.scheduledFunctions = {};
 33   this.nowMillis = 0;
 34 };
 35 
 36 jasmine.FakeTimer.prototype.tick = function(millis) {
 37   var oldMillis = this.nowMillis;
 38   var newMillis = oldMillis + millis;
 39   this.runFunctionsWithinRange(oldMillis, newMillis);
 40   this.nowMillis = newMillis;
 41 };
 42 
 43 jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) {
 44   var scheduledFunc;
 45   var funcsToRun = [];
 46   for (var timeoutKey in this.scheduledFunctions) {
 47     scheduledFunc = this.scheduledFunctions[timeoutKey];
 48     if (scheduledFunc != jasmine.undefined &&
 49         scheduledFunc.runAtMillis >= oldMillis &&
 50         scheduledFunc.runAtMillis <= nowMillis) {
 51       funcsToRun.push(scheduledFunc);
 52       this.scheduledFunctions[timeoutKey] = jasmine.undefined;
 53     }
 54   }
 55 
 56   if (funcsToRun.length > 0) {
 57     funcsToRun.sort(function(a, b) {
 58       return a.runAtMillis - b.runAtMillis;
 59     });
 60     for (var i = 0; i < funcsToRun.length; ++i) {
 61       try {
 62         var funcToRun = funcsToRun[i];
 63         this.nowMillis = funcToRun.runAtMillis;
 64         funcToRun.funcToCall();
 65         if (funcToRun.recurring) {
 66           this.scheduleFunction(funcToRun.timeoutKey,
 67               funcToRun.funcToCall,
 68               funcToRun.millis,
 69               true);
 70         }
 71       } catch(e) {
 72       }
 73     }
 74     this.runFunctionsWithinRange(oldMillis, nowMillis);
 75   }
 76 };
 77 
 78 jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) {
 79   this.scheduledFunctions[timeoutKey] = {
 80     runAtMillis: this.nowMillis + millis,
 81     funcToCall: funcToCall,
 82     recurring: recurring,
 83     timeoutKey: timeoutKey,
 84     millis: millis
 85   };
 86 };
 87 
 88 /**
 89  * @namespace
 90  */
 91 jasmine.Clock = {
 92   defaultFakeTimer: new jasmine.FakeTimer(),
 93 
 94   reset: function() {
 95     jasmine.Clock.assertInstalled();
 96     jasmine.Clock.defaultFakeTimer.reset();
 97   },
 98 
 99   tick: function(millis) {
100     jasmine.Clock.assertInstalled();
101     jasmine.Clock.defaultFakeTimer.tick(millis);
102   },
103 
104   runFunctionsWithinRange: function(oldMillis, nowMillis) {
105     jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis);
106   },
107 
108   scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) {
109     jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring);
110   },
111 
112   useMock: function() {
113     if (!jasmine.Clock.isInstalled()) {
114       var spec = jasmine.getEnv().currentSpec;
115       spec.after(jasmine.Clock.uninstallMock);
116 
117       jasmine.Clock.installMock();
118     }
119   },
120 
121   installMock: function() {
122     jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer;
123   },
124 
125   uninstallMock: function() {
126     jasmine.Clock.assertInstalled();
127     jasmine.Clock.installed = jasmine.Clock.real;
128   },
129 
130   real: {
131     setTimeout: jasmine.getGlobal().setTimeout,
132     clearTimeout: jasmine.getGlobal().clearTimeout,
133     setInterval: jasmine.getGlobal().setInterval,
134     clearInterval: jasmine.getGlobal().clearInterval
135   },
136 
137   assertInstalled: function() {
138     if (!jasmine.Clock.isInstalled()) {
139       throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()");
140     }
141   },
142 
143   isInstalled: function() {
144     return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer;
145   },
146 
147   installed: null
148 };
149 jasmine.Clock.installed = jasmine.Clock.real;
150 
151 //else for IE support
152 jasmine.getGlobal().setTimeout = function(funcToCall, millis) {
153   if (jasmine.Clock.installed.setTimeout.apply) {
154     return jasmine.Clock.installed.setTimeout.apply(this, arguments);
155   } else {
156     return jasmine.Clock.installed.setTimeout(funcToCall, millis);
157   }
158 };
159 
160 jasmine.getGlobal().setInterval = function(funcToCall, millis) {
161   if (jasmine.Clock.installed.setInterval.apply) {
162     return jasmine.Clock.installed.setInterval.apply(this, arguments);
163   } else {
164     return jasmine.Clock.installed.setInterval(funcToCall, millis);
165   }
166 };
167 
168 jasmine.getGlobal().clearTimeout = function(timeoutKey) {
169   if (jasmine.Clock.installed.clearTimeout.apply) {
170     return jasmine.Clock.installed.clearTimeout.apply(this, arguments);
171   } else {
172     return jasmine.Clock.installed.clearTimeout(timeoutKey);
173   }
174 };
175 
176 jasmine.getGlobal().clearInterval = function(timeoutKey) {
177   if (jasmine.Clock.installed.clearTimeout.apply) {
178     return jasmine.Clock.installed.clearInterval.apply(this, arguments);
179   } else {
180     return jasmine.Clock.installed.clearInterval(timeoutKey);
181   }
182 };
183 
184