1 var jaws = (function(jaws) {
  2 
  3 /**
  4  * @class Manages an animation with a given list of frames. "Field Summary" contains options for the Animation()-constructor.
  5  *
  6  * @property {bool} loop        Restart animation when end is reached
  7  * @property {bool} bounce      Rewind the animation frame by frame when end is reached
  8  * @property {int} index          Start on this frame
  9  * @property {array} frames       Images/canvaselements
 10  * @property {milliseconds} frame_duration  How long should each frame be displayed
 11  * @property {int} frame_direction  -1 for backwards animation. 1 is default
 12  * @property {array} frame_size     Containing width/height, eg. [32, 32]
 13  * @property {int} offset           When cutting out frames from a sprite sheet, start at this frame
 14  * @property {string} orientation   How to cut out frames frmo sprite sheet, possible values are "down" or "right"
 15  * @property {function} on_end      Function to call when animation ends. triggers only on non-looping, non-bouncing animations
 16  * @property {object} subsets       Name specific frames-intervals for easy access later, i.e. {move: [2,4], fire: [4,6]}. Access with animation.subset[name]
 17  *
 18  * @example
 19  * // in setup()
 20  * anim = new jaws.Animation({sprite_sheet: "droid_11x15.png", frame_size: [11,15], frame_duration: 100})
 21  * player = new jaws.Sprite({y:300, anchor: "center_bottom"})
 22  *
 23  * // in update()
 24  * player.setImage( anim.next() )
 25  *
 26  * // in draw()
 27  * player.draw()
 28  *
 29  */
 30 jaws.Animation = function Animation(options) {
 31   if( !(this instanceof arguments.callee) ) return new arguments.callee( options );
 32 
 33   jaws.parseOptions(this, options, this.default_options);
 34 
 35   if(options.sprite_sheet) {
 36     var sprite_sheet = new jaws.SpriteSheet({image: options.sprite_sheet, scale_image: this.scale_image, frame_size: this.frame_size, orientation: this.orientation, offset: this.offset})
 37     this.frames = sprite_sheet.frames
 38     this.frame_size = sprite_sheet.frame_size
 39   }
 40 
 41   if(options.scale_image) {
 42     var image = (jaws.isDrawable(options.sprite_sheet) ? options.sprite_sheet : jaws.assets.get(options.sprite_sheet))
 43     this.frame_size[0] *= options.scale_image
 44     this.frame_size[1] *= options.scale_image
 45     options.sprite_sheet = jaws.retroScaleImage(image, options.scale_image)
 46   }
 47 
 48   /* Initializing timer-stuff */
 49   this.current_tick = (new Date()).getTime();
 50   this.last_tick = (new Date()).getTime();
 51   this.sum_tick = 0
 52 
 53   if(options.subsets) {
 54     this.subsets = {}
 55     for(subset in options.subsets) {
 56       start_stop = options.subsets[subset]
 57       this.subsets[subset] = this.slice(start_stop[0], start_stop[1])
 58     }
 59   }
 60 }
 61 
 62 jaws.Animation.prototype.default_options = {
 63   frames: [],
 64   subsets: [],
 65   frame_duration: 100,  // default: 100ms between each frameswitch
 66   index: 0,             // default: start with the very first frame
 67   loop: 1,
 68   bounce: 0,
 69   frame_direction: 1,
 70   frame_size: null,
 71   orientation: "down",
 72   on_end: null,
 73   offset: 0,
 74   scale_image: null,
 75   sprite_sheet: null
 76 }
 77 
 78 /**
 79  * Return a special animationsubset created with "subset"-parameter when initializing
 80  *
 81  */
 82 jaws.Animation.prototype.subset = function(subset) {
 83   return this.subsets[subset]
 84 }
 85 
 86 /**
 87  Propells the animation forward by counting milliseconds and changing this.index accordingly
 88  Supports looping and bouncing animations
 89 */
 90 jaws.Animation.prototype.update = function() {
 91   this.current_tick = (new Date()).getTime();
 92   this.sum_tick += (this.current_tick - this.last_tick);
 93   this.last_tick = this.current_tick;
 94 
 95   if(this.sum_tick > this.frame_duration) {
 96     this.index += this.frame_direction
 97     this.sum_tick = 0
 98   }
 99   if( (this.index >= this.frames.length) || (this.index < 0) ) {
100     if(this.bounce) {
101       this.frame_direction = -this.frame_direction
102       this.index += this.frame_direction * 2
103     }
104     else if(this.loop) {
105       if(this.frame_direction < 0) { 
106         this.index = this.frames.length -1; 
107       } else { 
108         this.index = 0; 
109       }
110     }
111     else {
112       this.index -= this.frame_direction
113       if (this.on_end) {
114         this.on_end()
115         this.on_end = null
116       }
117     }
118   }
119   return this
120 }
121 
122 /**
123   works like Array.slice but returns a new Animation-object with a subset of the frames
124 */
125 jaws.Animation.prototype.slice = function(start, stop) {
126   var o = {}
127   o.frame_duration = this.frame_duration
128   o.loop = this.loop
129   o.bounce = this.bounce
130   o.on_end = this.on_end
131   o.frame_direction = this.frame_direction
132   o.frames = this.frames.slice().slice(start, stop)
133   return new jaws.Animation(o)
134 };
135 
136 /**
137   Moves animation forward by calling update() and then return the current frame
138 */
139 jaws.Animation.prototype.next = function() {
140   this.update()
141   return this.frames[this.index]
142 };
143 
144 /** returns true if animation is at the very last frame */
145 jaws.Animation.prototype.atLastFrame = function() { return (this.index == this.frames.length-1) }
146 
147 /** returns true if animation is at the very first frame */
148 jaws.Animation.prototype.atFirstFrame = function() { return (this.index == 0) }
149 
150 
151 /**
152   returns the current frame
153 */
154 jaws.Animation.prototype.currentFrame = function() {
155   return this.frames[this.index]
156 };
157 
158 /**
159  * Debugstring for Animation()-constructor
160  * @example
161  * var anim = new Animation(...)
162  * console.log(anim.toString())
163  */
164 jaws.Animation.prototype.toString = function() { return "[Animation, " + this.frames.length + " frames]" }
165 
166 return jaws;
167 })(jaws || {});
168 
169