1 /**
  2 * Ticker by Grant Skinner. Dec 5, 2010
  3 * Visit http://easeljs.com/ for documentation, updates and examples.
  4 *
  5 *
  6 * Copyright (c) 2010 Grant Skinner
  7 * 
  8 * Permission is hereby granted, free of charge, to any person
  9 * obtaining a copy of this software and associated documentation
 10 * files (the "Software"), to deal in the Software without
 11 * restriction, including without limitation the rights to use,
 12 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 13 * copies of the Software, and to permit persons to whom the
 14 * Software is furnished to do so, subject to the following
 15 * conditions:
 16 * 
 17 * The above copyright notice and this permission notice shall be
 18 * included in all copies or substantial portions of the Software.
 19 * 
 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 23 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 27 * OTHER DEALINGS IN THE SOFTWARE.
 28 */
 29 
 30 
 31 
 32 // constructor:
 33 	/**
 34 	* The Ticker class uses a static interface (ex. Ticker.getPaused()) and should not be instantiated.
 35 	* @class Provides a centralized tick or heartbeat broadcast at a set interval. Listeners can subscribe 
 36 	* to the tick event to be notified when a set time interval has elapsed.
 37 	* Note that the interval that the tick event is called is a target interval, and may be broadcast 
 38 	* at a slower interval during times of high CPU load.
 39 	*/
 40 	function Ticker() {
 41 		throw "Ticker cannot be instantiated.";
 42 	}
 43 	
 44 // private static properties:
 45 	/** @private */
 46 	Ticker._listeners = [];
 47 	/** @private */
 48 	Ticker._pauseable = [];
 49 	/** @private */
 50 	Ticker._paused = false;
 51 	/** @private */
 52 	Ticker._inited = false;
 53 	/** @private */
 54 	Ticker._startTime = 0;
 55 	/** @private */
 56 	Ticker._pausedTime=0;
 57 	/** @private : number of ticks that have passed */
 58 	Ticker._ticks = 0;
 59 	/** @private : number of ticks that have passed while Ticker has been paused */
 60 	Ticker._pausedTickers = 0;
 61 	/** @private */
 62 	Ticker._interval = 50; // READ-ONLY
 63 	/** @private */
 64 	Ticker._intervalID = null;
 65 	/** @private */
 66 	Ticker._lastTime = 0;
 67 	/** @private */
 68 	Ticker._times = [];
 69 	
 70 // public static methods:
 71 	/**
 72 	* Adds a listener for the tick event. The listener object must expose a .tick() method, which will be called once 
 73 	* each tick / interval. The interval is specified via the .setInterval(ms) method.
 74 	* The exposed tick method is passed a single parameter, which include the elapsed time between the 
 75 	* previous tick and the current one.
 76 	* @param o The object to add as a listener.
 77 	* @param pauseable If false, the listener will continue to have tick called even when Ticker is paused via
 78 	* Ticker.pause(). Default is false.
 79 	* @static
 80 	*/
 81 	Ticker.addListener = function(o, pauseable) {
 82 		if (!Ticker._inited) {
 83 			Ticker._inited = true;
 84 			Ticker.setInterval(Ticker._interval);
 85 		}
 86 		this.removeListener(o);
 87 		Ticker._pauseable[Ticker._listeners.length] = (pauseable == null) ? true : pauseable;
 88 		Ticker._listeners.push(o);
 89 	}
 90 	
 91 	/**
 92 	* Removes the specified listener.
 93 	* @param o The object to remove from listening from the tick event.
 94 	* @static
 95 	*/
 96 	Ticker.removeListener = function(o) {
 97 		if (Ticker._listeners == null) { return; }
 98 		var index = Ticker._listeners.indexOf(o);
 99 		if (index != -1) {
100 			Ticker._listeners.splice(index,1);
101 			Ticker._pauseable.splice(index,1);
102 		}
103 	}
104 	
105 	/**
106 	* Removes all listeners.
107 	* @static
108 	*/
109 	Ticker.removeAllListeners = function() {
110 		Ticker._listeners = [];
111 		Ticker._pauseable = [];
112 	}
113 	
114 	/**
115 	* Sets the target time (in milliseconds) between ticks. Default is 50 (20 FPS).
116 	* Note actual time between ticks may be more than requested depending on CPU load.
117 	* @param interval Time in milliseconds between ticks.
118 	* @static
119 	*/
120 	Ticker.setInterval = function(interval) {
121 		if (Ticker._intervalID != null) { clearInterval(Ticker._intervalID); }
122 		Ticker._lastTime = Ticker._getTime();
123 		Ticker._interval = interval;
124 		Ticker._intervalID = setInterval(Ticker._tick, interval);
125 	}
126 	
127 	/**
128 	* Returns the current target time between ticks, as set with setInterval.
129 	* @static
130 	*/
131 	Ticker.getInterval = function() {
132 		return Ticker._interval;
133 	}
134 	
135 	/**
136 	* Returns the target frame rate in frames per second (FPS). For example, with an interval of 40, getFPS() will 
137 	* return 25 (1000ms per second divided by 40 ms per tick = 25fps).
138 	* @static
139 	*/
140 	Ticker.getFPS = function() {
141 		return 1000/Ticker._interval;
142 	}
143 	
144 	/**
145 	* Sets the target frame rate in frames per second (FPS). For example, with an interval of 40, getFPS() will 
146 	* return 25 (1000ms per second divided by 40 ms per tick = 25fps).
147 	* @param value Target number of ticks broadcast per second.
148 	* @static
149 	*/	
150 	Ticker.setFPS = function(value) {
151 		Ticker.setInterval(1000/value);
152 	}
153 	
154 	/**
155 	* Returns the actual frames / ticks per second.
156 	* @param ticks Optional. The number of previous ticks over which to measure the actual frames / ticks per second. 
157 	* @static
158 	*/
159 	Ticker.getMeasuredFPS = function(ticks) {
160 		if (Ticker._times.length < 2) { return -1; }
161 		
162 		// x >> 1 : use bitwise to divide by two (int math)
163 		if (ticks == null) { ticks = Ticker.getFPS()>>1; }
164 		ticks = Math.min(Ticker._times.length-1,ticks);
165 		return 1000/((Ticker._times[0]-Ticker._times[ticks])/ticks);
166 	}
167 	
168 	/**
169 	* While Ticker is paused, pausable listeners are not ticked. See addListener for more information.
170 	* @param value Indicates whether to pause (true) or unpause (false) Ticker.
171 	* @static
172 	*/
173 	Ticker.setPaused = function(value) {
174 		Ticker._paused = value;
175 	}
176 	
177 	/**
178 	* Returns a boolean indicating whether Ticker is currently paused, as set with setPaused.
179 	* @static
180 	*/
181 	Ticker.getPaused = function() {
182 		return Ticker._paused;
183 	}
184 	
185 	/**
186 	* Returns the number of milliseconds that have elapsed since Ticker was loaded. For example, you could use this in 
187 	* a time synchronized animation to determine the exact amount of time that has elapsed.
188 	* @param pauseable Indicates whether to return the elapsed time for pauseable, or unpauseable listeners. If true, 
189 	* time that elapsed while Ticker was paused is not included.
190 	* @static
191 	*/
192 	Ticker.getTime = function(pauseable) {
193 		return Ticker._getTime() - Ticker._startTime - (pauseable ? Ticker._pausedTime : 0);
194 	}
195 	
196 	/**
197 	* Returns the number of ticks that have elapsed while Ticker was active.
198 	* @param pauseable Indicates whether to return the elapsed ticks for pauseable, or unpauseable listeners.
199 	* @static
200 	*/
201 	Ticker.getTicks = function(pauseable) {
202 		return  Ticker._ticks - (pauseable ?Ticker._pausedTickers : 0);
203 	}
204 	
205 // private static methods:
206 	/** @private */
207 	Ticker._tick = function() {
208 		Ticker._ticks++;
209 		
210 		var time = Ticker.getTime(false);
211 		var elapsedTime = time-Ticker._lastTime;
212 		var paused = Ticker._paused;
213 		
214 		if (paused) {
215 			Ticker._pausedTickers++;
216 			Ticker._pausedTime += elapsedTime;
217 		}
218 		Ticker._lastTime = time;
219 		
220 		var pauseable = Ticker._pauseable;
221 		var listeners = Ticker._listeners;
222 		
223 		var l = listeners ? listeners.length : 0;
224 		for (var i=0; i<l; i++) {
225 			var p = pauseable[i];
226 			var listener = listeners[i];
227 			if (listener == null || (paused && p) || listener.tick == null) { continue; }
228 			listener.tick(elapsedTime);
229 		}
230 		
231 		Ticker._times.unshift(time);
232 		if (Ticker._times.length > 100) { Ticker._times.pop(); }
233 	}
234 	
235 	/** @private */
236 	Ticker._getTime = function() {
237 		return new Date().getTime();
238 	}
239 	Ticker._startTime = Ticker._getTime();
240