1 /**
  2 * Tick by Grant Skinner. Dec 5, 2010
  3 * Visit www.gskinner.com/blog for documentation, updates and more free code.
  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 Tick class uses a static interface (ex. Tick.getPaused()) and should not be instantiated.
 35 	* @class Provides a centralized tick or heartbeat. Listeners can subscribe to the tick, and identify whether they are pausable or not.
 36 	**/
 37 	function Tick() {
 38 		throw "Tick cannot be instantiated.";
 39 	}
 40 	
 41 // private static properties:
 42 	/** @private **/
 43 	Tick._listeners = null;
 44 	/** @private **/
 45 	Tick._pauseable = null;
 46 	/** @private **/
 47 	Tick._paused = false;
 48 	/** @private **/
 49 	Tick._inited = false;
 50 	/** @private **/
 51 	Tick._startTime = 0;
 52 	/** @private **/
 53 	Tick._pausedTime=0;
 54 	/** @private **/
 55 	Tick._ticks = 0;
 56 	/** @private **/
 57 	Tick._pausedTicks = 0;
 58 	/** @private **/
 59 	Tick._interval = 50; // READ-ONLY
 60 	/** @private **/
 61 	Tick._intervalID = null;
 62 	/** @private **/
 63 	Tick._lastTime = 0;
 64 	
 65 // public static methods:
 66 	/**
 67 	* Adds a listener to the tick. The listener object must expose a .tick() method, which will be called once each tick. The exposed tick method can optionally accept a single parameter, which will include the elapsed time between the previous tick and the current one.
 68 	* @param o The object to add as a listener.
 69 	* @param pausable If false, the listener will continue to have tick called even when Tick is paused via Tick.pause(). Default is false.
 70 	* @static
 71 	**/
 72 	Tick.addListener = function(o, pauseable) {
 73 		if (!Tick._inited) {
 74 			Tick._inited = true;
 75 			Tick.removeAllListeners();
 76 			Tick.setInterval(Tick._interval);
 77 		}
 78 		
 79 		var index = Tick._listeners.indexOf(o);
 80 		if (index != -1) { return; }
 81 		
 82 		Tick._pauseable[Tick._listeners.length] = pauseable;
 83 		Tick._listeners.push(o);
 84 	}
 85 	
 86 	/**
 87 	* Removes the specified listener.
 88 	* @param o The listener to remove.
 89 	* @static
 90 	**/
 91 	Tick.removeListener = function(o) {
 92 		if (Tick._listeners == null) { return; }
 93 		var index = Tick._listeners.indexOf(o);
 94 		if (index != -1) {
 95 			Tick._listeners.splice(index,1);
 96 			Tick._pauseable.splice(index,1);
 97 		}
 98 	}
 99 	
100 	/**
101 	* Removes all listeners.
102 	* @static
103 	**/
104 	Tick.removeAllListeners = function() {
105 		Tick._listeners = [];
106 		Tick._pauseable = [];
107 	}
108 	
109 	/**
110 	* Sets the time (in milliseconds) between ticks. Default is 50 (20 FPS).
111 	* @param interval Time in milliseconds between ticks.
112 	* @static
113 	**/
114 	Tick.setInterval = function(interval) {
115 		if (Tick._intervalID != null) { clearInterval(Tick._intervalID); }
116 		Tick._lastTime = Tick._getTime();
117 		Tick._interval = interval;
118 		Tick._intervalID = setInterval(Tick._tick, interval);
119 	}
120 	
121 	/**
122 	* Returns the current time between ticks, as set with setInterval.
123 	* @static
124 	**/
125 	Tick.getInterval = function() {
126 		return Tick._interval;
127 	}
128 	
129 	/**
130 	* Returns the frame rate in frames per second (FPS). For example, with an interval of 40, getFPS() will return 25 (1000ms per second divided by 40 ms per tick = 25fps).
131 	* @static
132 	**/
133 	Tick.getFPS = function() {
134 		return 1000/Tick._interval;
135 	}
136 	
137 	/**
138 	* While Tick is paused, pausable listeners are not ticked. See addListener for more information.
139 	* @param value Indicates whether to pause (true) or unpause (false) Tick.
140 	* @static
141 	**/
142 	Tick.setPaused = function(value) {
143 		Tick._paused = value;
144 	}
145 	
146 	/**
147 	* Returns a boolean indicating whether Tick is currently paused, as set with setPaused.
148 	* @static
149 	**/
150 	Tick.getPaused = function() {
151 		return Tick._paused;
152 	}
153 	
154 	/**
155 	* Returns the number of milliseconds that have elapsed since Tick was loaded. For example, you could use this in a time synchronized animation to determine the exact amount of time that has elapsed.
156 	* @param pauseable Indicates whether to return the elapsed time for pauseable, or unpauseable listeners. If true, time that elapsed while Tick was paused is not included.
157 	* @static
158 	**/
159 	Tick.getTime = function(pauseable) {
160 		return Tick._getTime() - Tick._startTime - (pauseable ? Tick._pausedTime : 0);
161 	}
162 	
163 	/**
164 	* Returns the number of ticks that have elapsed while Tick was active.
165 	* @param pauseable Indicates whether to return the elapsed ticks for pauseable, or unpauseable listeners.
166 	* @static
167 	**/
168 	Tick.getTicks = function(pauseable) {
169 		return  Tick._ticks - (pauseable ?Tick._pausedTicks : 0);
170 	}
171 	
172 // private static methods:
173 	/** @private **/
174 	Tick._tick = function() {
175 		Tick._ticks++;
176 		
177 		var time = Tick.getTime(false);
178 		var elapsedTime = time-Tick._lastTime;
179 		var paused = Tick._paused;
180 		
181 		if (paused) {
182 			Tick._pausedTicks++;
183 			Tick._pausedTime += elapsedTime;
184 		}
185 		Tick._lastTime = time;
186 		
187 		var pauseable = Tick._pauseable;
188 		var listeners = Tick._listeners;
189 		
190 		var l = listeners.length;
191 		for (var i=0; i<l; i++) {
192 			var p = pauseable[i];
193 			var listener = listeners[i];
194 			if (listener == null || (paused && p) || listener.tick == null) { continue; }
195 			listener.tick(elapsedTime);
196 		}
197 	}
198 	
199 	/** @private **/
200 	Tick._getTime = function() {
201 		new Date().getTime();
202 	}
203 	Tick._startTime = Tick._getTime();
204