1 var jaws = (function(jaws) {
  2 
  3 // requestAnim shim layer by Paul Irish
  4 window.requestAnimFrame = (function(){
  5   return  window.requestAnimationFrame       ||
  6           window.webkitRequestAnimationFrame ||
  7           window.mozRequestAnimationFrame    ||
  8           window.oRequestAnimationFrame      ||
  9           window.msRequestAnimationFrame     ||
 10           function(/* function */ callback, /* DOMElement */ element){
 11             window.setTimeout(callback, 16.666);
 12           };
 13 })();
 14 
 15 /**
 16  * @class A classic game loop forever looping calls to update() / draw() with given framerate. "Field Summary" contains options for the GameLoop()-constructor.
 17  *
 18  * @property {int} tick_duration  duration in ms between the last 2 ticks (often called dt)
 19  * @property {int} fps  the real fps (as opposed to the target fps), smoothed out with a moving average
 20  * @property {int} ticks  total amount of ticks since game loops start
 21  *
 22  * @example
 23  *
 24  * game = {}
 25  *  draw: function() { ... your stuff executed every 30 FPS ... }
 26  * }
 27  *
 28  * game_loop = new jaws.GameLoop(game, {fps: 30})
 29  * game_loop.start()
 30  *
 31  * // You can also use the shortcut jaws.start(), it will:
 32  * // 1) Load all assets with jaws.assets.loadAll()
 33  * // 2) Create a GameLoop() and start it
 34  * jaws.start(MyGameState, {fps: 30})
 35  *
 36  */
 37 jaws.GameLoop = function GameLoop(game_object, options, game_state_setup_options) {
 38   if( !(this instanceof arguments.callee) ) return new arguments.callee( game_object, options );
 39 
 40   this.tick_duration = 0
 41   this.fps = 0
 42   this.ticks = 0
 43   
 44   var update_id
 45   var paused = false
 46   var stopped = false
 47   var that = this
 48   var mean_value = new MeanValue(20) // let's have a smooth, non-jittery FPS-value
 49 
 50   /** 
 51    * returns how game_loop has been active in milliseconds 
 52    * does currently not factor in pause-time
 53    */
 54   this.runtime = function() {
 55     return (this.last_tick - this.first_tick)
 56   }
 57 
 58   /** Start the game loop by calling setup() once and then loop update()/draw() forever with given FPS */
 59   this.start = function() {
 60     jaws.log.info("Game loop start", true)
 61   
 62     this.first_tick = (new Date()).getTime();
 63     this.current_tick = (new Date()).getTime();
 64     this.last_tick = (new Date()).getTime(); 
 65 
 66     if(options.setup !== false && game_object.setup) { game_object.setup(game_state_setup_options) }
 67     step_delay = 1000 / options.fps;
 68    
 69     if(options.fps == 60) {
 70       requestAnimFrame(this.loop)
 71     }
 72     else {
 73       update_id = setInterval(this.loop, step_delay);
 74     }
 75   }
 76   
 77   /** The core of the game loop. Calculate a mean FPS and call update()/draw() if game loop is not paused */
 78   this.loop = function() {
 79     that.current_tick = (new Date()).getTime();
 80     that.tick_duration = that.current_tick - that.last_tick
 81     that.fps = mean_value.add(1000/that.tick_duration).get()
 82 
 83     if(!stopped && !paused) {
 84       if(game_object.update) { game_object.update() }
 85       if(game_object.draw)   { game_object.draw() }
 86       that.ticks++
 87     }
 88     if(options.fps == 60 && !stopped) requestAnimFrame(that.loop);
 89     that.last_tick = that.current_tick;
 90   }
 91   
 92   /** Pause the game loop. loop() will still get called but not update() / draw() */
 93   this.pause = function()   { paused = true }
 94   
 95   /** unpause the game loop */
 96   this.unpause = function() { paused = false }
 97 
 98   /** Stop the game loop */
 99   this.stop = function() { 
100     if(update_id) clearInterval(update_id); 
101     stopped = true;
102   }
103 }
104 
105 /** @ignore */
106 function MeanValue(size) {
107   this.size = size
108   this.values = new Array(this.size)
109   this.value
110   
111   this.add = function(value) {
112     if(this.values.length > this.size) {  // is values filled?
113       this.values.splice(0,1)
114       this.value = 0
115       for(var i=0; this.values[i]; i++) {
116         this.value += this.values[i]
117       }
118       this.value = this.value / this.size
119     }
120     this.values.push(value)
121     
122     return this
123   }
124 
125   this.get = function() {
126     return parseInt(this.value)
127   }
128 
129 }
130 
131 return jaws;
132 })(jaws || {});
133 
134