From be2a587a8610c5105d08de69918df6a22aada5f9 Mon Sep 17 00:00:00 2001 From: straker Date: Sat, 19 Sep 2015 22:11:35 -0600 Subject: [PATCH] test(gameLoop): add tests for gameLoop.js refactor(all): change the set function to be called init since it's doing more than just setting properties, it's also giving defaults and initializing other properties docs(examples): update examples to reflect name change --- examples/galaxian/js/galaxian.js | 4 +- kontra.js | 133 +++++++++++----------- kontra.min.js | 2 +- kontra.min.js.map | 2 +- src/core.js | 2 +- src/gameLoop.js | 51 +++++---- src/pool.js | 18 +-- src/quadtree.js | 8 +- src/sprite.js | 26 ++--- src/spriteSheet.js | 16 +-- src/tileEngine.js | 12 +- test/gameLoop.spec.js | 190 +++++++++++++++++++++++++++++++ test/phantom.polyfill.js | 24 ++++ test/pool.spec.js | 43 ++++--- 14 files changed, 381 insertions(+), 150 deletions(-) create mode 100644 test/gameLoop.spec.js diff --git a/examples/galaxian/js/galaxian.js b/examples/galaxian/js/galaxian.js index 3190028d..3746aa4d 100644 --- a/examples/galaxian/js/galaxian.js +++ b/examples/galaxian/js/galaxian.js @@ -25,7 +25,7 @@ kontra.loadAssets( isAlive: function() { return !this.audio.ended; }, - set: function() { + init: function() { this.audio.play(); }, // set required properties @@ -341,7 +341,7 @@ kontra.loadAssets( kontra.audios.kick_shock.currentTime = 0; kontra.audios.kick_shock.play(); - player.position.set(280, 270); + player.position.init(280, 270); }; startGame(); diff --git a/kontra.js b/kontra.js index e5080452..fd7b92e7 100644 --- a/kontra.js +++ b/kontra.js @@ -840,7 +840,7 @@ var kontra = (function(kontra, document) { 'use strict'; /** - * Set up the canvas. + * Initialize the canvas. * @memberof kontra * * @param {object} properties - Properties for the game. @@ -955,7 +955,7 @@ var kontra = (function(kontra, window) { /** * Get the current time. Uses the User Timing API if it's available or defaults to using * Date().getTime() - * @private + * @memberof kontra * * @returns {number} */ @@ -976,18 +976,18 @@ var kontra = (function(kontra, window) { * Game loop that updates and renders the game every frame. * @memberof kontra * - * @see kontra.gameLoop.prototype.set for list of parameters. + * @see kontra.gameLoop.prototype.init for list of parameters. */ kontra.gameLoop = function(properties) { var gameLoop = Object.create(kontra.gameLoop.prototype); - gameLoop.set(properties); + gameLoop.init(properties); return gameLoop; }; kontra.gameLoop.prototype = { /** - * Set properties on the game loop. + * Initialize properties on the game loop. * @memberof kontra.gameLoop * * @param {object} properties - Configure the game loop. @@ -995,7 +995,7 @@ var kontra = (function(kontra, window) { * @param {function} properties.update - Function called to update the game. * @param {function} properties.render - Function called to render the game. */ - set: function set(properties) { + init: function init(properties) { properties = properties || {}; // check for required functions @@ -1015,14 +1015,33 @@ var kontra = (function(kontra, window) { this.render = properties.render; }, + /** + * Start the game loop. + * @memberof kontra.gameLoop + */ + start: function start() { + this._last = kontra.timestamp(); + this.isStopped = false; + requestAnimationFrame(this._frame.bind(this)); + }, + + /** + * Stop the game loop. + */ + stop: function stop() { + this.isStopped = true; + cancelAnimationFrame(this._rAF); + }, + /** * Called every frame of the game loop. * @memberof kontra.gameLoop + * @private */ - frame: function frame() { + _frame: function frame() { var _this = this; - _this._rAF = requestAnimationFrame(_this.frame.bind(_this)); + _this._rAF = requestAnimationFrame(_this._frame.bind(_this)); _this._now = kontra.timestamp(); _this._dt = _this._now - _this._last; @@ -1043,24 +1062,6 @@ var kontra = (function(kontra, window) { } _this.render(); - }, - - /** - * Start the game loop. - * @memberof kontra.gameLoop - */ - start: function start() { - this._last = kontra.timestamp(); - this.isStopped = false; - requestAnimationFrame(this.frame.bind(this)); - }, - - /** - * Stop the game loop. - */ - stop: function stop() { - this.isStopped = true; - cancelAnimationFrame(this._rAF); } }; @@ -1382,18 +1383,18 @@ var kontra = (function(kontra) { * Unused items are at the front of the pool and in use items are at the of the pool. * @memberof kontra * - * @see kontra.pool.prototype.set for list of parameters. + * @see kontra.pool.prototype.init for list of parameters. */ kontra.pool = function(properties) { var pool = Object.create(kontra.pool.prototype); - pool.set(properties); + pool.init(properties); return pool; }; kontra.pool.prototype = { /** - * Set properties on the pool. + * Initialize properties on the pool. * @memberof kontra.pool * * @param {object} properties - Properties of the pool. @@ -1403,9 +1404,9 @@ var kontra = (function(kontra) { * @param {boolean} properties.fill - Fill the pool to max size instead of slowly growing. * * Objects inside the pool must implement render(), update(), - * set(), and isAlive() functions. + * init(), and isAlive() functions. */ - set: function set(properties) { + init: function init(properties) { properties = properties || {}; var error, obj; @@ -1425,9 +1426,9 @@ var kontra = (function(kontra) { obj = this.create(); if (!obj || typeof obj.render !== 'function' || typeof obj.update !== 'function' || - typeof obj.set !== 'function' || typeof obj.isAlive !== 'function') { + typeof obj.init !== 'function' || typeof obj.isAlive !== 'function') { error = new SyntaxError('Create object required functions not found.'); - kontra.logError(error, 'Objects to be pooled must implement render(), update(), set() and isAlive() functions.'); + kontra.logError(error, 'Objects to be pooled must implement render(), update(), init() and isAlive() functions.'); return; } @@ -1460,7 +1461,7 @@ var kontra = (function(kontra) { * Get an object from the pool. * @memberof kontra.pool * - * @param {object} properties - Properties to pass to object.set(). + * @param {object} properties - Properties to pass to object.init(). */ get: function get(properties) { properties = properties || {}; @@ -1485,7 +1486,7 @@ var kontra = (function(kontra) { // save off first object in pool to reassign to last object after unshift var obj = _this.objects[0]; - obj.set(properties); + obj.init(properties); // unshift the array for (var i = 1; i < _this.size; i++) { @@ -1591,7 +1592,7 @@ var kontra = (function(kontra, undefined) { * collapses to avoid garbage collection. * @memberof kontra * - * @see kontra.quadtree.prototype.set for list of parameters. + * @see kontra.quadtree.prototype.init for list of parameters. *L * The quadrant indices are numbered as follows (following a z-order curve): * | @@ -1602,14 +1603,14 @@ var kontra = (function(kontra, undefined) { */ kontra.quadtree = function(properties) { var quadtree = Object.create(kontra.quadtree.prototype); - quadtree.set(properties); + quadtree.init(properties); return quadtree; }; kontra.quadtree.prototype = { /** - * Set properties on the quadtree. + * Initialize properties on the quadtree. * @memberof kontra.quadtree * * @param {number} [depth=0] - Current node depth. @@ -1618,7 +1619,7 @@ var kontra = (function(kontra, undefined) { * @param {object} [parentNode] - The node that contains this node. * @param {object} [bounds] - The 2D space this node occupies. */ - set: function set(properties) { + init: function init(properties) { properties = properties || {}; this.depth = properties.depth || 0; @@ -1855,7 +1856,7 @@ var kontra = (function(kontra, undefined) { var kontra = (function(kontra, Math, undefined) { 'use strict'; - // prevent these properties from being set at the end of kontra.sprite.set() + // prevent these properties from being set at the end of kontra.sprite.init() var excludedProperties = [ 'x', 'y', @@ -1876,18 +1877,18 @@ var kontra = (function(kontra, Math, undefined) { * A vector for 2D space. * @memberof kontra * - * @see kontra.vector.prototype.set for list of parameters. + * @see kontra.vector.prototype.init for list of parameters. */ kontra.vector = function(x, y) { var vector = Object.create(kontra.vector.prototype); - vector.set(x, y); + vector.init(x, y); return vector; }; kontra.vector.prototype = { /** - * Set the vectors x and y position. + * Initialize the vectors x and y position. * @memberof kontra.vector * * @param {number} x=0 - Center x coordinate. @@ -1895,7 +1896,7 @@ var kontra = (function(kontra, Math, undefined) { * * @returns {vector} */ - set: function set(x, y) { + init: function init(x, y) { this.x = x || 0; this.y = y || 0; @@ -1965,11 +1966,11 @@ var kontra = (function(kontra, Math, undefined) { * @memberof kontra * @requires kontra.vector * - * @see kontra.sprite._prot.set for list of parameters. + * @see kontra.sprite.prototype.init for list of parameters. */ kontra.sprite = function(properties) { var sprite = Object.create(kontra.sprite.prototype); - sprite.set(properties); + sprite.init(properties); return sprite; }; @@ -2050,10 +2051,10 @@ var kontra = (function(kontra, Math, undefined) { }, /** - * Set properties on the sprite. + * Initialize properties on the sprite. * @memberof kontra.sprite * - * @param {object} properties - Properties to set on the sprite. + * @param {object} properties - Properties of the sprite. * @param {number} properties.x - X coordinate of the sprite. * @param {number} properties.y - Y coordinate of the sprite. * @param {number} [properties.dx] - Change in X position. @@ -2079,14 +2080,14 @@ var kontra = (function(kontra, Math, undefined) { * decide when to kill it, you can set timeToLive to Infinity. * Just be sure to set timeToLive to 0 when you want the sprite to die. */ - set: function set(properties) { + init: function init(properties) { properties = properties || {}; var _this = this; - _this.position = (_this.position || kontra.vector()).set(properties.x, properties.y); - _this.velocity = (_this.velocity || kontra.vector()).set(properties.dx, properties.dy); - _this.acceleration = (_this.acceleration || kontra.vector()).set(properties.ddx, properties.ddy); + _this.position = (_this.position || kontra.vector()).init(properties.x, properties.y); + _this.velocity = (_this.velocity || kontra.vector()).init(properties.dx, properties.dy); + _this.acceleration = (_this.acceleration || kontra.vector()).init(properties.ddx, properties.ddy); _this.timeToLive = properties.timeToLive || 0; _this.context = properties.context || kontra.context; @@ -2296,18 +2297,18 @@ var kontra = (function(kontra, undefined) { * Single animation from a sprite sheet. * @memberof kontra * - * @see kontra.pool.prototype.set for list of parameters. + * @see kontra.pool.prototype.init for list of parameters. */ kontra.animation = function(properties) { var animation = Object.create(kontra.animation.prototype); - animation.set(properties); + animation.init(properties); return animation; }; kontra.animation.prototype = { /** - * Set properties on the animation. + * Initialize properties on the animation. * @memberof kontra.animation * * @param {object} properties - Properties of the animation. @@ -2315,7 +2316,7 @@ var kontra = (function(kontra, undefined) { * @param {number[]} properties.frames - List of frames of the animation. * @param {number} properties.frameSpeed - Time to wait before transitioning the animation to the next frame. */ - set: function set(properties) { + init: function init(properties) { properties = properties || {}; this.spriteSheet = properties.spriteSheet; @@ -2422,18 +2423,18 @@ var kontra = (function(kontra, undefined) { * Create a sprite sheet from an image. * @memberof kontra * - * @see kontra.spriteSheet.prototype.set for list of parameters. + * @see kontra.spriteSheet.prototype.init for list of parameters. */ kontra.spriteSheet = function(properties) { var spriteSheet = Object.create(kontra.spriteSheet.prototype); - spriteSheet.set(properties); + spriteSheet.init(properties); return spriteSheet; }; kontra.spriteSheet.prototype = { /** - * Set properties on the spriteSheet. + * Initialize properties on the spriteSheet. * @memberof kontra * @constructor * @@ -2443,7 +2444,7 @@ var kontra = (function(kontra, undefined) { * @param {number} properties.frameHeight - Height (in px) of each frame. * @param {object} properties.animations - Animations to create from the sprite sheet. */ - set: function set(properties) { + init: function init(properties) { properties = properties || {}; this.animations = {}; @@ -2689,18 +2690,18 @@ var kontra = (function(kontra, Math, undefined) { * A tile engine for rendering tilesets. Works well with the tile engine program Tiled. * @memberof kontra * - * @see kontra.tileEngine.prototype.set for list of parameters. + * @see kontra.tileEngine.prototype.init for list of parameters. */ kontra.tileEngine = function(properties) { var tileEngine = Object.create(kontra.tileEngine.prototype); - tileEngine.set(properties); + tileEngine.init(properties); return tileEngine; }; kontra.tileEngine.prototype = { /** - * Set properties on the tile engine. + * Initialize properties on the tile engine. * @memberof kontra.tileEngine * * @param {object} properties - Properties of the tile engine. @@ -2714,7 +2715,7 @@ var kontra = (function(kontra, Math, undefined) { * @param {number} [properties.sy=0] - Y position to clip the tileset. * @param {Context} [properties.context=kontra.context] - Provide a context for the tile engine to draw on. */ - set: function set(properties) { + init: function init(properties) { properties = properties || {}; var _this = this; @@ -3033,7 +3034,7 @@ var kontra = (function(kontra, Math, undefined) { /** * Modified binary search that will return the tileset associated with the tile - * @memberOf kontra.tileEngine + * @memberof kontra.tileEngine * @private * * @param {number} tile - Tile grid. @@ -3064,6 +3065,8 @@ var kontra = (function(kontra, Math, undefined) { /** * Pre-render the tiles to make drawing fast. + * @memberof kontra.tileEngine + * @private */ _preRenderImage: function preRenderImage() { var _this = this; diff --git a/kontra.min.js b/kontra.min.js index 206ecd49..64a416fd 100644 --- a/kontra.min.js +++ b/kontra.min.js @@ -1,2 +1,2 @@ -function qFactory(t,e){function i(t,e,n){var r;if(t)if(a(t))for(r in t)"prototype"==r||"length"==r||"name"==r||t.hasOwnProperty&&!t.hasOwnProperty(r)||e.call(n,t[r],r);else if(t.forEach&&t.forEach!==i)t.forEach(e,n);else if(h(t))for(r=0;re;e++)t=n[e],i.then(t[0],t[1],t[2])})}},reject:function(t){s.resolve(f(t))},notify:function(e){if(o){var i=o;o.length&&t(function(){for(var t,n=0,r=i.length;r>n;n++)t=i[n],t[2](e)})}},promise:{then:function(t,s,h){var u=c(),d=function(i){try{u.resolve((a(t)?t:n)(i))}catch(r){u.reject(r),e(r)}},f=function(t){try{u.resolve((a(s)?s:r)(t))}catch(i){u.reject(i),e(i)}},l=function(t){try{u.notify((a(h)?h:n)(t))}catch(i){e(i)}};return o?o.push([d,f,l]):i.then(d,f,l),u.promise},"catch":function(t){return this.then(null,t)},"finally":function(t){function e(t,e){var i=c();return e?i.resolve(t):i.reject(t),i.promise}function i(i,r){var s=null;try{s=(t||n)()}catch(o){return e(o,!1)}return s&&a(s.then)?s.then(function(){return e(i,r)},function(t){return e(t,!1)}):e(i,r)}return this.then(function(t){return i(t,!0)},function(t){return i(t,!1)})}}}},u=function(e){return e&&a(e.then)?e:{then:function(i){var n=c();return t(function(){n.resolve(i(e))}),n.promise}}},d=function(t){var e=c();return e.reject(t),e.promise},f=function(i){return{then:function(n,s){var o=c();return t(function(){try{o.resolve((a(s)?s:r)(i))}catch(t){o.reject(t),e(t)}}),o.promise}}},l=function(i,s,o,h){var f,l=c(),p=function(t){try{return(a(s)?s:n)(t)}catch(i){return e(i),d(i)}},m=function(t){try{return(a(o)?o:r)(t)}catch(i){return e(i),d(i)}},g=function(t){try{return(a(h)?h:n)(t)}catch(i){e(i)}};return t(function(){u(i).then(function(t){f||(f=!0,l.resolve(u(t).then(p,m,g)))},function(t){f||(f=!0,l.resolve(m(t)))},function(t){f||l.notify(g(t))})}),l.promise};return{defer:c,reject:d,when:l,all:s}}window.q=qFactory(function(t){setTimeout(function(){t()},0)},function(t){console.error("qLite: "+t.stack)});var kontra=function(t){var e=/(jpeg|jpg|gif|png)$/,i=/(wav|mp3|ogg|aac|m4a)$/;t.images={},t.audios={},t.data={},t.assetPaths={images:"",audios:"",data:""};var n=new Audio;return t.canUse=t.canUse||{},t.canUse.wav="",t.canUse.mp3=n.canPlayType("audio/mpeg;").replace(/^no$/,""),t.canUse.ogg=n.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),t.canUse.aac=n.canPlayType("audio/aac;").replace(/^no$/,""),t.canUse.m4a=(n.canPlayType("audio/x-m4a;")||t.canUse.aac).replace(/^no$/,""),t.getAssetExtension=function(t){return t.substr((~-t.lastIndexOf(".")>>>0)+2)},t.getAssetType=function(t){var n=this.getAssetExtension(t);return n.match(e)?"Image":n.match(i)?"Audio":"Data"},t.getAssetName=function(t){return t.replace(/\.[^/.]+$/,"")},t}(kontra||{}),kontra=function(t,e){return t.loadAssets=function(){var i,n,r=e.defer(),s=[],o=0,a=arguments.length;arguments.length||r.resolve();for(var h,c=0;h=arguments[c];c++)n=Array.isArray(h)?h[0]:h,i=this.getAssetType(n),function(e){s.push(e.promise),t["load"+i](n).then(function(){e.resolve(),r.notify({loaded:++o,total:a})},function(t){e.reject(t)})}(e.defer());return e.all(s).then(function(){r.resolve()},function(t){r.reject(t)}),r.promise},t.loadImage=function(i){var n=e.defer(),r=this.getAssetName(i),s=new Image;return i=this.assetPaths.images+i,s.onload=function(){t.images[r]=t.images[i]=this,n.resolve(this)},s.onerror=function(){n.reject("Unable to load image "+i)},s.src=i,n.promise},t.loadAudio=function(i){var n,r,s,o,a=e.defer();Array.isArray(i)||(i=[i]);for(var h=0;n=i[h];h++)if(this.canUse[this.getAssetExtension(n)]){s=n;break}return s?(r=this.getAssetName(s),o=new Audio,n=this.assetPaths.audios+s,o.addEventListener("canplay",function(){t.audios[r]=t.audios[n]=this,a.resolve(this)}),o.onerror=function(){a.reject("Unable to load audio "+n)},o.src=n,o.preload="auto",o.load()):a.reject("Browser cannot play any of the audio formats provided"),a.promise},t.loadData=function(i){var n=e.defer(),r=new XMLHttpRequest,s=this.getAssetName(i),o=this.assetPaths.data+i;return r.addEventListener("load",function(){if(200!==r.status)return void n.reject(r.responseText);try{var e=JSON.parse(r.responseText);t.data[s]=t.data[o]=e,n.resolve(e)}catch(i){var a=r.responseText;t.data[s]=t.data[o]=a,n.resolve(a)}}),r.open("GET",o,!0),r.send(),n.promise},t}(kontra||{},q),kontra=function(t,e){return t.bundles={},t.createBundle=function(t,e){this.bundles[t]||(this.bundles[t]=e||[])},t.loadBundles=function(){for(var t,i,n=e.defer(),r=[],s=0,o=0,a=0;i=arguments[a];a++)(t=this.bundles[i])?(o+=t.length,r.push(this.loadAssets.apply(this,t))):n.reject("Bundle '"+i+"' has not been created.");return e.all(r).then(function(){n.resolve()},function(t){n.reject(t)},function(){n.notify({loaded:++s,total:o})}),n.promise},t}(kontra||{},q),kontra=function(t,e){return t.loadManifest=function(i){var n,r=e.defer();return t.loadData(i).then(function(e){t.assetPaths.images=e.imagePath||"",t.assetPaths.audios=e.audioPath||"",t.assetPaths.data=e.dataPath||"";for(var i,s=0;i=e.bundles[s];s++)t.createBundle(i.name,i.assets);return e.loadBundles?(n="all"===e.loadBundles?Object.keys(t.bundles||{}):Array.isArray(e.loadBundles)?e.loadBundles:[e.loadBundles],void t.loadBundles.apply(t,n).then(function(){r.resolve()},function(t){r.reject(t)},function(t){r.notify(t)})):void r.resolve()},function(t){r.reject(t)}),r.promise},t}(kontra||{},q),kontra=function(t,e){"use strict";return t.init=function(i){if(i=i||{},t.isString(i.canvas))this.canvas=e.getElementById(i.canvas);else if(t.isCanvas(i.canvas))this.canvas=i.canvas;else if(this.canvas=e.getElementsByTagName("canvas")[0],!this.canvas){var n=new ReferenceError("No canvas element found.");return void t.logError(n,"You must provide a canvas element for the game.")}this.context=this.canvas.getContext("2d"),this.game={width:this.canvas.width,height:this.canvas.height}},t.logError=function(t,e){console.error("Kontra: "+e+"\n "+t.stack)},t.noop=function(){},t.isArray=Array.isArray,t.isString=function(t){return"string"==typeof t},t.isNumber=function(t){return"number"==typeof t},t.isImage=function(t){return t instanceof HTMLImageElement},t.isCanvas=function(t){return t instanceof HTMLCanvasElement},t}(kontra||{},document),kontra=function(t,e){"use strict";return t.timestamp=function(){return e.performance&&e.performance.now?function(){return e.performance.now()}:function(){return(new Date).getTime()}}(),t.gameLoop=function(e){var i=Object.create(t.gameLoop.prototype);return i.set(e),i},t.gameLoop.prototype={set:function(e){if(e=e||{},"function"!=typeof e.update||"function"!=typeof e.render){var i=new ReferenceError("Required functions not found");return void t.logError(i,"You must provide update() and render() functions to create a game loop.")}this.isStopped=!1,this._accumulator=0,this._delta=1e3/(e.fps||60),this.update=e.update,this.render=e.render},frame:function(){var e=this;if(e._rAF=requestAnimationFrame(e.frame.bind(e)),e._now=t.timestamp(),e._dt=e._now-e._last,e._last=e._now,!(e._dt>1e3)){for(e._accumulator+=e._dt;e._accumulator>=e._delta;)e.update(e._delta/1e3),e._accumulator-=e._delta;e.render()}},start:function(){this._last=t.timestamp(),this.isStopped=!1,requestAnimationFrame(this.frame.bind(this))},stop:function(){this.isStopped=!0,cancelAnimationFrame(this._rAF)}},t}(kontra||{},window),kontra=function(t,e){"use strict";function i(t){return"number"==typeof t.which?t.which:t.keyCode}function n(t){var e=[];t=t.trim().replace("++","+plus");for(var i,n=0;i=p[n];n++)-1!==t.indexOf(i)&&(e.push(i),t=t.replace(i,""));return t=t.replace(/\+/g,"").toLowerCase(),f[t]?e.push("shift+"+f[t]):t&&e.push(t),e.join("+")}function r(t){for(var e,n=[],r=0;e=p[r];r++)t[e+"Key"]&&n.push(e);var s=u[i(t)];return-1===n.indexOf(s)&&n.push(s),n.join("+")}function s(t){for(var e,i=r(t),n=0,s=i.split("+");e=s[n];n++)c[e]=!0;h[i]&&(h[i](t,i),t.preventDefault())}function o(t){var e=u[i(t)];c[e]=!1,l[e]&&(c[l[e]]=!1)}function a(t){c={}}for(var h={},c={},u={8:"backspace",9:"tab",13:"enter",16:"shift",17:"ctrl",18:"alt",20:"capslock",27:"esc",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"insert",46:"delete",91:"leftwindow",92:"rightwindow",93:"select",144:"numlock",145:"scrolllock",106:"*",107:"+",109:"-",110:".",111:"/",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"},d=0;26>d;d++)u[65+d]=String.fromCharCode(65+d).toLowerCase();for(d=0;10>d;d++)u[48+d]=""+d;for(d=1;20>d;d++)u[111+d]="f"+d;for(d=0;10>d;d++)u[96+d]="numpad"+d;var f={"~":"`","!":"1","@":"2","#":"3",$:"4","%":"5","^":"6","&":"7","*":"8","(":"9",")":"0",_:"-","+":"=",":":";",'"':"'","<":",",">":".","?":"/","|":"\\",plus:"="},l={leftwindow:"meta",select:"meta"},p=["meta","ctrl","alt","shift"];return e.addEventListener("keydown",s),e.addEventListener("keyup",o),e.addEventListener("blur",a),t.keys={},t.keys.bind=function(e,i){if("function"!=typeof i){var r=new SyntaxError("Invalid function.");return void t.logError(r,"You must provide a function as the second parameter.")}e=t.isArray(e)?e:[e];for(var s,o=0;s=e[o];o++){var a=n(s);h[a]=i}},t.keys.unbind=function(e){e=t.isArray(e)?e:[e];for(var i,r=0;i=e[r];r++){var s=n(i);h[s]=void 0}},t.keys.pressed=function(t){var e=n(t),i=!0;t=e.split("+");for(var r,s=0;r=t[s];s++)i=i&&!!c[r];return i},t}(kontra||{},window),kontra=function(t){"use strict";return t.pool=function(e){var i=Object.create(t.pool.prototype);return i.set(e),i},t.pool.prototype={set:function(e){e=e||{};var i,n;if("function"!=typeof e.create)return i=new SyntaxError("Required function not found."),void t.logError(i,"Parameter 'create' must be a function that returns an object.");if(this.create=e.create.bind(this,e.createProperties||{}),n=this.create(),!n||"function"!=typeof n.render||"function"!=typeof n.update||"function"!=typeof n.set||"function"!=typeof n.isAlive)return i=new SyntaxError("Create object required functions not found."),void t.logError(i,"Objects to be pooled must implement render(), update(), set() and isAlive() functions.");if(this.objects=[n],this.size=1,this.maxSize=e.maxSize||1/0,this.lastIndex=0,this.inUse=0,e.fill){if(!e.maxSize)return i=new SyntaxError("Required property not found."),void t.logError(i,"Parameter 'maxSize' must be set before you can fill a pool.");for(;this.objects.length=n;)if(e=this.objects[i],e.update(t),e.isAlive())i--;else{for(var r=i;r>0;r--)this.objects[r]=this.objects[r-1];this.objects[0]=e,this.inUse--,n++}},render:function(){for(var t=Math.max(this.objects.length-this.inUse,0),e=this.lastIndex;e>=t;e--)this.objects[e].render()}},t}(kontra||{}),kontra=function(t,e){"use strict";return t.quadtree=function(e){var i=Object.create(t.quadtree.prototype);return i.set(e),i},t.quadtree.prototype={set:function(e){e=e||{},this.depth=e.depth||0,this.maxDepth=e.maxDepth||3,this.maxObjects=e.maxObjects||25,this.isBranchNode=!1,this.parentNode=e.parentNode,this.bounds=e.bounds||{x:0,y:0,width:t.game.width,height:t.game.height},this.objects=[],this.subnodes=[]},clear:function(){if(this.isBranchNode)for(var t=0;4>t;t++)this.subnodes[t].clear();this.isBranchNode=!1,this.objects.length=0},get:function(t){for(var e,i,n=this,r=[];n.subnodes.length&&this.isBranchNode;){e=this._getIndex(t);for(var s=0,o=e.length;o>s;s++)i=e[s],r.push.apply(r,this.subnodes[i].get(t));return r}return n.objects},add:function(){for(var e,i,n,r=this,s=0,o=arguments.length;o>s;s++)if(i=arguments[s],t.isArray(i))r.add.apply(this,i);else if(r.subnodes.length&&r.isBranchNode)r._addToSubnode(i);else if(r.objects.push(i),r.objects.length>r.maxObjects&&r.depthi;i++)this.subnodes[e[i]].add(t)},_getIndex:function(t){var i=[],n=this.bounds.x+this.bounds.width/2,r=this.bounds.y+this.bounds.height/2,s=t.x!==e?t.x:t.position.x,o=t.y!==e?t.y:t.position.y,a=r>o&&o+t.height>=this.bounds.y,h=o+t.height>=r&&os&&s+t.width>=this.bounds.x&&(a&&i.push(0),h&&i.push(2)),s+t.width>=n&&ss;s++)this.subnodes[s]=t.quadtree({bounds:{x:n+(s%2===1?e:0),y:r+(s>=2?i:0),width:e,height:i},depth:this.depth+1,maxDepth:this.maxDepth,maxObjects:this.maxObjects,parentNode:this})},render:function(){if((this.objects.length||0===this.depth||this.parentNode&&this.parentNode.isBranchNode)&&(t.context.strokeStyle="red",t.context.strokeRect(this.bounds.x,this.bounds.y,this.bounds.width,this.bounds.height),this.subnodes.length))for(var e=0;4>e;e++)this.subnodes[e].render()}},t}(kontra||{}),kontra=function(t,e,i){"use strict";var n=["x","y","dx","dy","ddx","ddy","timeToLive","context","image","animations","color","width","height"];return t.vector=function(e,i){var n=Object.create(t.vector.prototype);return n.set(e,i),n},t.vector.prototype={set:function(t,e){return this.x=t||0,this.y=e||0,this},add:function(t,e){this.x+=(t.x||0)*(e||1),this.y+=(t.y||0)*(e||1)},clamp:function(t,n,r,s){this._xMin=t!==i?t:-(1/0),this._xMax=r!==i?r:1/0,this._yMin=n!==i?n:-(1/0),this._yMax=s!==i?s:1/0,this._x=this.x,this._y=this.y,Object.defineProperties(this,{x:{get:function(){return this._x},set:function(t){this._x=e.min(e.max(t,this._xMin),this._xMax)}},y:{get:function(){return this._y},set:function(t){this._y=e.min(e.max(t,this._yMin),this._yMax)}}})}},t.sprite=function(e){var i=Object.create(t.sprite.prototype);return i.set(e),i},t.sprite.prototype={advanceSprite:function(t){this.velocity.add(this.acceleration,t),this.position.add(this.velocity,t),this.timeToLive--},drawRect:function(){this.context.fillStyle=this.color,this.context.fillRect(this.position.x,this.position.y,this.width,this.height)},drawImage:function(){this.context.drawImage(this.image,this.position.x,this.position.y)},advanceAnimation:function(t){this.advanceSprite(t),this.currentAnimation.update(t)},drawAnimation:function(){this.currentAnimation.render({context:this.context,x:this.position.x,y:this.position.y})},playAnimation:function(t){this.currentAnimation=this.animations[t]},isAlive:function(){return this.timeToLive>0},set:function(e){e=e||{};var i=this;i.position=(i.position||t.vector()).set(e.x,e.y),i.velocity=(i.velocity||t.vector()).set(e.dx,e.dy),i.acceleration=(i.acceleration||t.vector()).set(e.ddx,e.ddy),i.timeToLive=e.timeToLive||0,i.context=e.context||t.context,t.isImage(e.image)||t.isCanvas(e.image)?(i.image=e.image,i.width=e.image.width,i.height=e.image.height,i.advance=i.advanceSprite,i.draw=i.drawImage):e.animations?(i.animations=e.animations,i.currentAnimation=e.animations[Object.keys(e.animations)[0]],i.width=i.currentAnimation.width,i.height=i.currentAnimation.height,i.advance=i.advanceAnimation,i.draw=i.drawAnimation):(i.color=e.color,i.width=e.width,i.height=e.height,i.advance=i.advanceSprite,i.draw=i.drawRect);for(var r in e)e.hasOwnProperty(r)&&-1===n.indexOf(r)&&(i[r]=e[r])},get x(){return this.position.x},get y(){return this.position.y},get dx(){return this.velocity.x},get dy(){return this.velocity.y},get ddx(){return this.acceleration.x},get ddy(){return this.acceleration.y},set x(t){this.position.x=t},set y(t){this.position.y=t},set dx(t){this.velocity.x=t},set dy(t){this.velocity.y=t},set ddx(t){this.acceleration.x=t},set ddy(t){this.acceleration.y=t},collidesWith:function(t){var e=t.x!==i?t.x:t.position.x,n=t.y!==i?t.y:t.position.y;return this.position.xe&&this.position.yn?!0:!1},update:function(t){this.advance(t)},render:function(){this.draw()}},t}(kontra||{},Math),kontra=function(t,e){"use strict";return t.animation=function(e){var i=Object.create(t.animation.prototype);return i.set(e),i},t.animation.prototype={set:function(t){t=t||{},this.spriteSheet=t.spriteSheet,this.frames=t.frames,this.frameSpeed=t.frameSpeed,this.width=t.spriteSheet.frame.width,this.height=t.spriteSheet.frame.height,this.currentFrame=0,this._accumulator=0,this.update=this.advance,this.render=this.draw},advance:function(t){for(t=(1>t?1e3*t:t)||1,this._accumulator+=t;this._accumulator>=this.frameSpeed;)this.currentFrame=++this.currentFrame%this.frames.length,this._accumulator-=this.frameSpeed},draw:function(e){e=e||{};var i=e.context||t.context,n=this.frames[this.currentFrame]/this.spriteSheet.framesPerRow|0,r=this.frames[this.currentFrame]%this.spriteSheet.framesPerRow|0;i.drawImage(this.spriteSheet.image,r*this.spriteSheet.frame.width,n*this.spriteSheet.frame.height,this.spriteSheet.frame.width,this.spriteSheet.frame.height,e.x,e.y,this.spriteSheet.frame.width,this.spriteSheet.frame.height)},play:function(){this.update=this.advance,this.render=this.draw},stop:function(){this.update=t.noop,this.render=t.noop},pause:function(){this.update=t.noop}},t.spriteSheet=function(e){var i=Object.create(t.spriteSheet.prototype);return i.set(e),i},t.spriteSheet.prototype={set:function(e){if(e=e||{},this.animations={},!t.isImage(e.image)&&!t.isCanvas(e.image)){var i=new SyntaxError("Invalid image.");return void t.logError(i,"You must provide an Image for the SpriteSheet.")}this.image=e.image,this.frame={width:e.frameWidth,height:e.frameHeight},this.framesPerRow=e.image.width/e.frameWidth|0,e.animations&&this.createAnimations(e.animations)},createAnimations:function(i){var n;if(!i||0===Object.keys(i).length)return n=new ReferenceError("No animations found."),void t.logError(n,"You must provide at least one named animation to create an Animation.");var r,s,o,a;for(var h in i)if(i.hasOwnProperty(h)){if(r=i[h],s=r.frames,o=r.frameSpeed,a=[],s===e)return n=new ReferenceError("No animation frames found."),void t.logError(n,"Animation "+h+" must provide a frames property.");if(t.isNumber(s))a.push(s);else if(t.isString(s))a=this._parseFrames(s);else if(t.isArray(s))for(var c,u=0;c=s[u];u++)t.isString(c)?a.push.apply(a,this._parseFrames(c)):a.push(c);this.animations[h]=t.animation({spriteSheet:this,frames:a,frameSpeed:o})}},_parseFrames:function(t){var e,i=[],n=t.split("..").map(Number),r=n[0]=n[1];e--)i.push(e);return i}},t}(kontra||{}),kontra=function(t,e,i,n){"use strict";return t.canUse=t.canUse||{},t.canUse.localStorage="localStorage"in e&&null!==e.localStorage,t.canUse.localStorage?(t.store={},t.store.set=function(t,e){e===n?this.remove(t):i.setItem(t,JSON.stringify(e))},t.store.get=function(t){var e=i.getItem(t);try{e=JSON.parse(e)}catch(n){}return e},t.store.remove=function(t){i.removeItem(t)},t.store.clear=function(){i.clear()},t):t}(kontra||{},window,window.localStorage),kontra=function(t,e,i){"use strict";return t.tileEngine=function(e){var i=Object.create(t.tileEngine.prototype);return i.set(e),i},t.tileEngine.prototype={set:function(e){e=e||{};var i=this;if(!e.width||!e.height){var n=new ReferenceError("Required parameters not found");return void t.logError(n,"You must provide width and height of the map to create a tile engine.")}i.width=e.width,i.height=e.height,i.tileWidth=e.tileWidth||32,i.tileHeight=e.tileHeight||32,i.context=e.context||t.context,i.canvasWidth=i.context.canvas.width,i.canvasHeight=i.context.canvas.height,i._offscreenCanvas=document.createElement("canvas"),i._offscreenContext=i._offscreenCanvas.getContext("2d"),i._offscreenCanvas.width=i.mapWidth=i.width*i.tileWidth,i._offscreenCanvas.height=i.mapHeight=i.height*i.tileHeight,i.sxMax=i.mapWidth-i.canvasWidth,i.syMax=i.mapHeight-i.canvasHeight,i.layers={},i._layerOrder=[],i.tilesets=[],i.x=e.x||0,i.y=e.y||0,i.sx=e.sx||0,i.sy=e.sy||0},addTileset:function(e){if(e=e||{},!t.isImage(e.image)&&!t.isCanvas(e.image)){var i=new SyntaxError("Invalid image.");return void t.logError(i,"You must provide an Image for the tile engine.")}var n=e.image,r=e.firstGrid,s=(n.width/this.tileWidth|0)*(n.height/this.tileHeight|0);if(!r)if(this.tilesets.length>0){var o=this.tilesets[this.tilesets.length-1],a=(o.image.width/this.tileWidth|0)*(o.image.height/this.tileHeight|0);r=o.firstGrid+a-1}else r=1;this.tilesets.push({firstGrid:r,lastGrid:r+s-1,image:n}),this.tilesets.sort(function(t,e){return t.firstGrid-e.firstGrid})},addLayer:function(e){e=e||{},e.render=e.render===i?!0:e.render;var n,r=this;if(t.isArray(e.data[0])){n=[];for(var s,o=0;s=e.data[o];o++)for(var a=0,h=s.length;h>a;a++)n.push(s[a])}else n=e.data;this.layers[e.name]=n,this.layers[e.name].zIndex=e.zIndex||0,this.layers[e.name].render=e.render,e.render&&(this._layerOrder.push(e.name),this._layerOrder.sort(function(t,e){return r.layers[t].zIndex-r.layers[e].zIndex}),this._preRenderImage())},layerCollidesWith:function(t,e){for(var n,r=e.x!==i?e.x:e.position.x,s=e.y!==i?e.y:e.position.y,o=this._getRow(s),a=this._getCol(r),h=this._getRow(s+e.height),c=this._getCol(r+e.width),u=o;h>=u;u++)for(var d=a;c>=d;d++)if(n=d+u*this.width,this.layers[t][n])return!0;return!1},tileAtLayer:function(t,e,i){var n=this._getRow(i),r=this._getCol(e),s=r+n*this.width;return this.layers[t][s]},render:function(){var t=this;t.sx=e.min(e.max(t.sx,0),t.sxMax),t.sy=e.min(e.max(t.sy,0),t.syMax),t.context.drawImage(t._offscreenCanvas,t.sx,t.sy,t.canvasWidth,t.canvasHeight,t.x,t.y,t.canvasWidth,t.canvasHeight)},renderLayer:function(t){for(var i,n,r,s,o,a,h,c,u,d=this,f=d.layers[t],l=d._getRow(),p=d._getCol(),m=p+l*d.width,g=p*d.tileWidth-d.sx,v=l*d.tileHeight-d.sy,y=e.ceil(d.canvasWidth/d.tileWidth)+1,x=e.ceil(d.canvasHeight/d.tileHeight)+1,b=y*x,w=0;b>w;)r=f[m],r&&(s=d._getTileset(r),o=s.image,i=g+w%y*d.tileWidth,n=v+(w/y|0)*d.tileHeight,a=r-s.firstGrid,h=o.width/d.tileWidth,c=a%h*d.tileWidth,u=(a/h|0)*d.tileHeight,d.context.drawImage(o,c,u,d.tileWidth,d.tileHeight,i,n,d.tileWidth,d.tileHeight)),++w%y===0?m=p+ ++l*d.width:m++},_getRow:function(t){return t=t||0,(this.sy+t)/this.tileHeight|0},_getCol:function(t){return t=t||0,(this.sx+t)/this.tileWidth|0},_getTileset:function(t){for(var e,i,n=0,r=this.tilesets.length-1;r>=n;){if(e=(n+r)/2|0,i=this.tilesets[e],t>=i.firstGrid&&t<=i.lastGrid)return i;t>i?n=e+1:r=e-1}},_preRenderImage:function(){for(var t,e,i,n,r,s,o,a,h,c,u=this,d=0;c=u.layers[u._layerOrder[d]];d++)for(var f=0,l=c.length;l>f;f++)t=c[f],t&&(e=u._getTileset(t),i=e.image,n=f%u.width*u.tileWidth,r=(f/u.width|0)*u.tileHeight,a=t-e.firstGrid,h=i.width/u.tileWidth,s=a%h*u.tileWidth,o=(a/h|0)*u.tileHeight,u._offscreenContext.drawImage(i,s,o,u.tileWidth,u.tileHeight,n,r,u.tileWidth,u.tileHeight))}},t}(kontra||{},Math); +function qFactory(t,e){function i(t,e,n){var r;if(t)if(a(t))for(r in t)"prototype"==r||"length"==r||"name"==r||t.hasOwnProperty&&!t.hasOwnProperty(r)||e.call(n,t[r],r);else if(t.forEach&&t.forEach!==i)t.forEach(e,n);else if(h(t))for(r=0;re;e++)t=n[e],i.then(t[0],t[1],t[2])})}},reject:function(t){s.resolve(f(t))},notify:function(e){if(o){var i=o;o.length&&t(function(){for(var t,n=0,r=i.length;r>n;n++)t=i[n],t[2](e)})}},promise:{then:function(t,s,h){var u=c(),d=function(i){try{u.resolve((a(t)?t:n)(i))}catch(r){u.reject(r),e(r)}},f=function(t){try{u.resolve((a(s)?s:r)(t))}catch(i){u.reject(i),e(i)}},l=function(t){try{u.notify((a(h)?h:n)(t))}catch(i){e(i)}};return o?o.push([d,f,l]):i.then(d,f,l),u.promise},"catch":function(t){return this.then(null,t)},"finally":function(t){function e(t,e){var i=c();return e?i.resolve(t):i.reject(t),i.promise}function i(i,r){var s=null;try{s=(t||n)()}catch(o){return e(o,!1)}return s&&a(s.then)?s.then(function(){return e(i,r)},function(t){return e(t,!1)}):e(i,r)}return this.then(function(t){return i(t,!0)},function(t){return i(t,!1)})}}}},u=function(e){return e&&a(e.then)?e:{then:function(i){var n=c();return t(function(){n.resolve(i(e))}),n.promise}}},d=function(t){var e=c();return e.reject(t),e.promise},f=function(i){return{then:function(n,s){var o=c();return t(function(){try{o.resolve((a(s)?s:r)(i))}catch(t){o.reject(t),e(t)}}),o.promise}}},l=function(i,s,o,h){var f,l=c(),p=function(t){try{return(a(s)?s:n)(t)}catch(i){return e(i),d(i)}},m=function(t){try{return(a(o)?o:r)(t)}catch(i){return e(i),d(i)}},g=function(t){try{return(a(h)?h:n)(t)}catch(i){e(i)}};return t(function(){u(i).then(function(t){f||(f=!0,l.resolve(u(t).then(p,m,g)))},function(t){f||(f=!0,l.resolve(m(t)))},function(t){f||l.notify(g(t))})}),l.promise};return{defer:c,reject:d,when:l,all:s}}window.q=qFactory(function(t){setTimeout(function(){t()},0)},function(t){console.error("qLite: "+t.stack)});var kontra=function(t){var e=/(jpeg|jpg|gif|png)$/,i=/(wav|mp3|ogg|aac|m4a)$/;t.images={},t.audios={},t.data={},t.assetPaths={images:"",audios:"",data:""};var n=new Audio;return t.canUse=t.canUse||{},t.canUse.wav="",t.canUse.mp3=n.canPlayType("audio/mpeg;").replace(/^no$/,""),t.canUse.ogg=n.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),t.canUse.aac=n.canPlayType("audio/aac;").replace(/^no$/,""),t.canUse.m4a=(n.canPlayType("audio/x-m4a;")||t.canUse.aac).replace(/^no$/,""),t.getAssetExtension=function(t){return t.substr((~-t.lastIndexOf(".")>>>0)+2)},t.getAssetType=function(t){var n=this.getAssetExtension(t);return n.match(e)?"Image":n.match(i)?"Audio":"Data"},t.getAssetName=function(t){return t.replace(/\.[^/.]+$/,"")},t}(kontra||{}),kontra=function(t,e){return t.loadAssets=function(){var i,n,r=e.defer(),s=[],o=0,a=arguments.length;arguments.length||r.resolve();for(var h,c=0;h=arguments[c];c++)n=Array.isArray(h)?h[0]:h,i=this.getAssetType(n),function(e){s.push(e.promise),t["load"+i](n).then(function(){e.resolve(),r.notify({loaded:++o,total:a})},function(t){e.reject(t)})}(e.defer());return e.all(s).then(function(){r.resolve()},function(t){r.reject(t)}),r.promise},t.loadImage=function(i){var n=e.defer(),r=this.getAssetName(i),s=new Image;return i=this.assetPaths.images+i,s.onload=function(){t.images[r]=t.images[i]=this,n.resolve(this)},s.onerror=function(){n.reject("Unable to load image "+i)},s.src=i,n.promise},t.loadAudio=function(i){var n,r,s,o,a=e.defer();Array.isArray(i)||(i=[i]);for(var h=0;n=i[h];h++)if(this.canUse[this.getAssetExtension(n)]){s=n;break}return s?(r=this.getAssetName(s),o=new Audio,n=this.assetPaths.audios+s,o.addEventListener("canplay",function(){t.audios[r]=t.audios[n]=this,a.resolve(this)}),o.onerror=function(){a.reject("Unable to load audio "+n)},o.src=n,o.preload="auto",o.load()):a.reject("Browser cannot play any of the audio formats provided"),a.promise},t.loadData=function(i){var n=e.defer(),r=new XMLHttpRequest,s=this.getAssetName(i),o=this.assetPaths.data+i;return r.addEventListener("load",function(){if(200!==r.status)return void n.reject(r.responseText);try{var e=JSON.parse(r.responseText);t.data[s]=t.data[o]=e,n.resolve(e)}catch(i){var a=r.responseText;t.data[s]=t.data[o]=a,n.resolve(a)}}),r.open("GET",o,!0),r.send(),n.promise},t}(kontra||{},q),kontra=function(t,e){return t.bundles={},t.createBundle=function(t,e){this.bundles[t]||(this.bundles[t]=e||[])},t.loadBundles=function(){for(var t,i,n=e.defer(),r=[],s=0,o=0,a=0;i=arguments[a];a++)(t=this.bundles[i])?(o+=t.length,r.push(this.loadAssets.apply(this,t))):n.reject("Bundle '"+i+"' has not been created.");return e.all(r).then(function(){n.resolve()},function(t){n.reject(t)},function(){n.notify({loaded:++s,total:o})}),n.promise},t}(kontra||{},q),kontra=function(t,e){return t.loadManifest=function(i){var n,r=e.defer();return t.loadData(i).then(function(e){t.assetPaths.images=e.imagePath||"",t.assetPaths.audios=e.audioPath||"",t.assetPaths.data=e.dataPath||"";for(var i,s=0;i=e.bundles[s];s++)t.createBundle(i.name,i.assets);return e.loadBundles?(n="all"===e.loadBundles?Object.keys(t.bundles||{}):Array.isArray(e.loadBundles)?e.loadBundles:[e.loadBundles],void t.loadBundles.apply(t,n).then(function(){r.resolve()},function(t){r.reject(t)},function(t){r.notify(t)})):void r.resolve()},function(t){r.reject(t)}),r.promise},t}(kontra||{},q),kontra=function(t,e){"use strict";return t.init=function(i){if(i=i||{},t.isString(i.canvas))this.canvas=e.getElementById(i.canvas);else if(t.isCanvas(i.canvas))this.canvas=i.canvas;else if(this.canvas=e.getElementsByTagName("canvas")[0],!this.canvas){var n=new ReferenceError("No canvas element found.");return void t.logError(n,"You must provide a canvas element for the game.")}this.context=this.canvas.getContext("2d"),this.game={width:this.canvas.width,height:this.canvas.height}},t.logError=function(t,e){console.error("Kontra: "+e+"\n "+t.stack)},t.noop=function(){},t.isArray=Array.isArray,t.isString=function(t){return"string"==typeof t},t.isNumber=function(t){return"number"==typeof t},t.isImage=function(t){return t instanceof HTMLImageElement},t.isCanvas=function(t){return t instanceof HTMLCanvasElement},t}(kontra||{},document),kontra=function(t,e){"use strict";return t.timestamp=function(){return e.performance&&e.performance.now?function(){return e.performance.now()}:function(){return(new Date).getTime()}}(),t.gameLoop=function(e){var i=Object.create(t.gameLoop.prototype);return i.init(e),i},t.gameLoop.prototype={init:function(e){if(e=e||{},"function"!=typeof e.update||"function"!=typeof e.render){var i=new ReferenceError("Required functions not found");return void t.logError(i,"You must provide update() and render() functions to create a game loop.")}this.isStopped=!1,this._accumulator=0,this._delta=1e3/(e.fps||60),this.update=e.update,this.render=e.render},start:function(){this._last=t.timestamp(),this.isStopped=!1,requestAnimationFrame(this._frame.bind(this))},stop:function(){this.isStopped=!0,cancelAnimationFrame(this._rAF)},_frame:function(){var e=this;if(e._rAF=requestAnimationFrame(e._frame.bind(e)),e._now=t.timestamp(),e._dt=e._now-e._last,e._last=e._now,!(e._dt>1e3)){for(e._accumulator+=e._dt;e._accumulator>=e._delta;)e.update(e._delta/1e3),e._accumulator-=e._delta;e.render()}}},t}(kontra||{},window),kontra=function(t,e){"use strict";function i(t){return"number"==typeof t.which?t.which:t.keyCode}function n(t){var e=[];t=t.trim().replace("++","+plus");for(var i,n=0;i=p[n];n++)-1!==t.indexOf(i)&&(e.push(i),t=t.replace(i,""));return t=t.replace(/\+/g,"").toLowerCase(),f[t]?e.push("shift+"+f[t]):t&&e.push(t),e.join("+")}function r(t){for(var e,n=[],r=0;e=p[r];r++)t[e+"Key"]&&n.push(e);var s=u[i(t)];return-1===n.indexOf(s)&&n.push(s),n.join("+")}function s(t){for(var e,i=r(t),n=0,s=i.split("+");e=s[n];n++)c[e]=!0;h[i]&&(h[i](t,i),t.preventDefault())}function o(t){var e=u[i(t)];c[e]=!1,l[e]&&(c[l[e]]=!1)}function a(t){c={}}for(var h={},c={},u={8:"backspace",9:"tab",13:"enter",16:"shift",17:"ctrl",18:"alt",20:"capslock",27:"esc",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"insert",46:"delete",91:"leftwindow",92:"rightwindow",93:"select",144:"numlock",145:"scrolllock",106:"*",107:"+",109:"-",110:".",111:"/",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"},d=0;26>d;d++)u[65+d]=String.fromCharCode(65+d).toLowerCase();for(d=0;10>d;d++)u[48+d]=""+d;for(d=1;20>d;d++)u[111+d]="f"+d;for(d=0;10>d;d++)u[96+d]="numpad"+d;var f={"~":"`","!":"1","@":"2","#":"3",$:"4","%":"5","^":"6","&":"7","*":"8","(":"9",")":"0",_:"-","+":"=",":":";",'"':"'","<":",",">":".","?":"/","|":"\\",plus:"="},l={leftwindow:"meta",select:"meta"},p=["meta","ctrl","alt","shift"];return e.addEventListener("keydown",s),e.addEventListener("keyup",o),e.addEventListener("blur",a),t.keys={},t.keys.bind=function(e,i){if("function"!=typeof i){var r=new SyntaxError("Invalid function.");return void t.logError(r,"You must provide a function as the second parameter.")}e=t.isArray(e)?e:[e];for(var s,o=0;s=e[o];o++){var a=n(s);h[a]=i}},t.keys.unbind=function(e){e=t.isArray(e)?e:[e];for(var i,r=0;i=e[r];r++){var s=n(i);h[s]=void 0}},t.keys.pressed=function(t){var e=n(t),i=!0;t=e.split("+");for(var r,s=0;r=t[s];s++)i=i&&!!c[r];return i},t}(kontra||{},window),kontra=function(t){"use strict";return t.pool=function(e){var i=Object.create(t.pool.prototype);return i.init(e),i},t.pool.prototype={init:function(e){e=e||{};var i,n;if("function"!=typeof e.create)return i=new SyntaxError("Required function not found."),void t.logError(i,"Parameter 'create' must be a function that returns an object.");if(this.create=e.create.bind(this,e.createProperties||{}),n=this.create(),!n||"function"!=typeof n.render||"function"!=typeof n.update||"function"!=typeof n.init||"function"!=typeof n.isAlive)return i=new SyntaxError("Create object required functions not found."),void t.logError(i,"Objects to be pooled must implement render(), update(), init() and isAlive() functions.");if(this.objects=[n],this.size=1,this.maxSize=e.maxSize||1/0,this.lastIndex=0,this.inUse=0,e.fill){if(!e.maxSize)return i=new SyntaxError("Required property not found."),void t.logError(i,"Parameter 'maxSize' must be set before you can fill a pool.");for(;this.objects.length=n;)if(e=this.objects[i],e.update(t),e.isAlive())i--;else{for(var r=i;r>0;r--)this.objects[r]=this.objects[r-1];this.objects[0]=e,this.inUse--,n++}},render:function(){for(var t=Math.max(this.objects.length-this.inUse,0),e=this.lastIndex;e>=t;e--)this.objects[e].render()}},t}(kontra||{}),kontra=function(t,e){"use strict";return t.quadtree=function(e){var i=Object.create(t.quadtree.prototype);return i.init(e),i},t.quadtree.prototype={init:function(e){e=e||{},this.depth=e.depth||0,this.maxDepth=e.maxDepth||3,this.maxObjects=e.maxObjects||25,this.isBranchNode=!1,this.parentNode=e.parentNode,this.bounds=e.bounds||{x:0,y:0,width:t.game.width,height:t.game.height},this.objects=[],this.subnodes=[]},clear:function(){if(this.isBranchNode)for(var t=0;4>t;t++)this.subnodes[t].clear();this.isBranchNode=!1,this.objects.length=0},get:function(t){for(var e,i,n=this,r=[];n.subnodes.length&&this.isBranchNode;){e=this._getIndex(t);for(var s=0,o=e.length;o>s;s++)i=e[s],r.push.apply(r,this.subnodes[i].get(t));return r}return n.objects},add:function(){for(var e,i,n,r=this,s=0,o=arguments.length;o>s;s++)if(i=arguments[s],t.isArray(i))r.add.apply(this,i);else if(r.subnodes.length&&r.isBranchNode)r._addToSubnode(i);else if(r.objects.push(i),r.objects.length>r.maxObjects&&r.depthi;i++)this.subnodes[e[i]].add(t)},_getIndex:function(t){var i=[],n=this.bounds.x+this.bounds.width/2,r=this.bounds.y+this.bounds.height/2,s=t.x!==e?t.x:t.position.x,o=t.y!==e?t.y:t.position.y,a=r>o&&o+t.height>=this.bounds.y,h=o+t.height>=r&&os&&s+t.width>=this.bounds.x&&(a&&i.push(0),h&&i.push(2)),s+t.width>=n&&ss;s++)this.subnodes[s]=t.quadtree({bounds:{x:n+(s%2===1?e:0),y:r+(s>=2?i:0),width:e,height:i},depth:this.depth+1,maxDepth:this.maxDepth,maxObjects:this.maxObjects,parentNode:this})},render:function(){if((this.objects.length||0===this.depth||this.parentNode&&this.parentNode.isBranchNode)&&(t.context.strokeStyle="red",t.context.strokeRect(this.bounds.x,this.bounds.y,this.bounds.width,this.bounds.height),this.subnodes.length))for(var e=0;4>e;e++)this.subnodes[e].render()}},t}(kontra||{}),kontra=function(t,e,i){"use strict";var n=["x","y","dx","dy","ddx","ddy","timeToLive","context","image","animations","color","width","height"];return t.vector=function(e,i){var n=Object.create(t.vector.prototype);return n.init(e,i),n},t.vector.prototype={init:function(t,e){return this.x=t||0,this.y=e||0,this},add:function(t,e){this.x+=(t.x||0)*(e||1),this.y+=(t.y||0)*(e||1)},clamp:function(t,n,r,s){this._xMin=t!==i?t:-(1/0),this._xMax=r!==i?r:1/0,this._yMin=n!==i?n:-(1/0),this._yMax=s!==i?s:1/0,this._x=this.x,this._y=this.y,Object.defineProperties(this,{x:{get:function(){return this._x},set:function(t){this._x=e.min(e.max(t,this._xMin),this._xMax)}},y:{get:function(){return this._y},set:function(t){this._y=e.min(e.max(t,this._yMin),this._yMax)}}})}},t.sprite=function(e){var i=Object.create(t.sprite.prototype);return i.init(e),i},t.sprite.prototype={advanceSprite:function(t){this.velocity.add(this.acceleration,t),this.position.add(this.velocity,t),this.timeToLive--},drawRect:function(){this.context.fillStyle=this.color,this.context.fillRect(this.position.x,this.position.y,this.width,this.height)},drawImage:function(){this.context.drawImage(this.image,this.position.x,this.position.y)},advanceAnimation:function(t){this.advanceSprite(t),this.currentAnimation.update(t)},drawAnimation:function(){this.currentAnimation.render({context:this.context,x:this.position.x,y:this.position.y})},playAnimation:function(t){this.currentAnimation=this.animations[t]},isAlive:function(){return this.timeToLive>0},init:function(e){e=e||{};var i=this;i.position=(i.position||t.vector()).init(e.x,e.y),i.velocity=(i.velocity||t.vector()).init(e.dx,e.dy),i.acceleration=(i.acceleration||t.vector()).init(e.ddx,e.ddy),i.timeToLive=e.timeToLive||0,i.context=e.context||t.context,t.isImage(e.image)||t.isCanvas(e.image)?(i.image=e.image,i.width=e.image.width,i.height=e.image.height,i.advance=i.advanceSprite,i.draw=i.drawImage):e.animations?(i.animations=e.animations,i.currentAnimation=e.animations[Object.keys(e.animations)[0]],i.width=i.currentAnimation.width,i.height=i.currentAnimation.height,i.advance=i.advanceAnimation,i.draw=i.drawAnimation):(i.color=e.color,i.width=e.width,i.height=e.height,i.advance=i.advanceSprite,i.draw=i.drawRect);for(var r in e)e.hasOwnProperty(r)&&-1===n.indexOf(r)&&(i[r]=e[r])},get x(){return this.position.x},get y(){return this.position.y},get dx(){return this.velocity.x},get dy(){return this.velocity.y},get ddx(){return this.acceleration.x},get ddy(){return this.acceleration.y},set x(t){this.position.x=t},set y(t){this.position.y=t},set dx(t){this.velocity.x=t},set dy(t){this.velocity.y=t},set ddx(t){this.acceleration.x=t},set ddy(t){this.acceleration.y=t},collidesWith:function(t){var e=t.x!==i?t.x:t.position.x,n=t.y!==i?t.y:t.position.y;return this.position.xe&&this.position.yn?!0:!1},update:function(t){this.advance(t)},render:function(){this.draw()}},t}(kontra||{},Math),kontra=function(t,e){"use strict";return t.animation=function(e){var i=Object.create(t.animation.prototype);return i.init(e),i},t.animation.prototype={init:function(t){t=t||{},this.spriteSheet=t.spriteSheet,this.frames=t.frames,this.frameSpeed=t.frameSpeed,this.width=t.spriteSheet.frame.width,this.height=t.spriteSheet.frame.height,this.currentFrame=0,this._accumulator=0,this.update=this.advance,this.render=this.draw},advance:function(t){for(t=(1>t?1e3*t:t)||1,this._accumulator+=t;this._accumulator>=this.frameSpeed;)this.currentFrame=++this.currentFrame%this.frames.length,this._accumulator-=this.frameSpeed},draw:function(e){e=e||{};var i=e.context||t.context,n=this.frames[this.currentFrame]/this.spriteSheet.framesPerRow|0,r=this.frames[this.currentFrame]%this.spriteSheet.framesPerRow|0;i.drawImage(this.spriteSheet.image,r*this.spriteSheet.frame.width,n*this.spriteSheet.frame.height,this.spriteSheet.frame.width,this.spriteSheet.frame.height,e.x,e.y,this.spriteSheet.frame.width,this.spriteSheet.frame.height)},play:function(){this.update=this.advance,this.render=this.draw},stop:function(){this.update=t.noop,this.render=t.noop},pause:function(){this.update=t.noop}},t.spriteSheet=function(e){var i=Object.create(t.spriteSheet.prototype);return i.init(e),i},t.spriteSheet.prototype={init:function(e){if(e=e||{},this.animations={},!t.isImage(e.image)&&!t.isCanvas(e.image)){var i=new SyntaxError("Invalid image.");return void t.logError(i,"You must provide an Image for the SpriteSheet.")}this.image=e.image,this.frame={width:e.frameWidth,height:e.frameHeight},this.framesPerRow=e.image.width/e.frameWidth|0,e.animations&&this.createAnimations(e.animations)},createAnimations:function(i){var n;if(!i||0===Object.keys(i).length)return n=new ReferenceError("No animations found."),void t.logError(n,"You must provide at least one named animation to create an Animation.");var r,s,o,a;for(var h in i)if(i.hasOwnProperty(h)){if(r=i[h],s=r.frames,o=r.frameSpeed,a=[],s===e)return n=new ReferenceError("No animation frames found."),void t.logError(n,"Animation "+h+" must provide a frames property.");if(t.isNumber(s))a.push(s);else if(t.isString(s))a=this._parseFrames(s);else if(t.isArray(s))for(var c,u=0;c=s[u];u++)t.isString(c)?a.push.apply(a,this._parseFrames(c)):a.push(c);this.animations[h]=t.animation({spriteSheet:this,frames:a,frameSpeed:o})}},_parseFrames:function(t){var e,i=[],n=t.split("..").map(Number),r=n[0]=n[1];e--)i.push(e);return i}},t}(kontra||{}),kontra=function(t,e,i,n){"use strict";return t.canUse=t.canUse||{},t.canUse.localStorage="localStorage"in e&&null!==e.localStorage,t.canUse.localStorage?(t.store={},t.store.set=function(t,e){e===n?this.remove(t):i.setItem(t,JSON.stringify(e))},t.store.get=function(t){var e=i.getItem(t);try{e=JSON.parse(e)}catch(n){}return e},t.store.remove=function(t){i.removeItem(t)},t.store.clear=function(){i.clear()},t):t}(kontra||{},window,window.localStorage),kontra=function(t,e,i){"use strict";return t.tileEngine=function(e){var i=Object.create(t.tileEngine.prototype);return i.init(e),i},t.tileEngine.prototype={init:function(e){e=e||{};var i=this;if(!e.width||!e.height){var n=new ReferenceError("Required parameters not found");return void t.logError(n,"You must provide width and height of the map to create a tile engine.")}i.width=e.width,i.height=e.height,i.tileWidth=e.tileWidth||32,i.tileHeight=e.tileHeight||32,i.context=e.context||t.context,i.canvasWidth=i.context.canvas.width,i.canvasHeight=i.context.canvas.height,i._offscreenCanvas=document.createElement("canvas"),i._offscreenContext=i._offscreenCanvas.getContext("2d"),i._offscreenCanvas.width=i.mapWidth=i.width*i.tileWidth,i._offscreenCanvas.height=i.mapHeight=i.height*i.tileHeight,i.sxMax=i.mapWidth-i.canvasWidth,i.syMax=i.mapHeight-i.canvasHeight,i.layers={},i._layerOrder=[],i.tilesets=[],i.x=e.x||0,i.y=e.y||0,i.sx=e.sx||0,i.sy=e.sy||0},addTileset:function(e){if(e=e||{},!t.isImage(e.image)&&!t.isCanvas(e.image)){var i=new SyntaxError("Invalid image.");return void t.logError(i,"You must provide an Image for the tile engine.")}var n=e.image,r=e.firstGrid,s=(n.width/this.tileWidth|0)*(n.height/this.tileHeight|0);if(!r)if(this.tilesets.length>0){var o=this.tilesets[this.tilesets.length-1],a=(o.image.width/this.tileWidth|0)*(o.image.height/this.tileHeight|0);r=o.firstGrid+a-1}else r=1;this.tilesets.push({firstGrid:r,lastGrid:r+s-1,image:n}),this.tilesets.sort(function(t,e){return t.firstGrid-e.firstGrid})},addLayer:function(e){e=e||{},e.render=e.render===i?!0:e.render;var n,r=this;if(t.isArray(e.data[0])){n=[];for(var s,o=0;s=e.data[o];o++)for(var a=0,h=s.length;h>a;a++)n.push(s[a])}else n=e.data;this.layers[e.name]=n,this.layers[e.name].zIndex=e.zIndex||0,this.layers[e.name].render=e.render,e.render&&(this._layerOrder.push(e.name),this._layerOrder.sort(function(t,e){return r.layers[t].zIndex-r.layers[e].zIndex}),this._preRenderImage())},layerCollidesWith:function(t,e){for(var n,r=e.x!==i?e.x:e.position.x,s=e.y!==i?e.y:e.position.y,o=this._getRow(s),a=this._getCol(r),h=this._getRow(s+e.height),c=this._getCol(r+e.width),u=o;h>=u;u++)for(var d=a;c>=d;d++)if(n=d+u*this.width,this.layers[t][n])return!0;return!1},tileAtLayer:function(t,e,i){var n=this._getRow(i),r=this._getCol(e),s=r+n*this.width;return this.layers[t][s]},render:function(){var t=this;t.sx=e.min(e.max(t.sx,0),t.sxMax),t.sy=e.min(e.max(t.sy,0),t.syMax),t.context.drawImage(t._offscreenCanvas,t.sx,t.sy,t.canvasWidth,t.canvasHeight,t.x,t.y,t.canvasWidth,t.canvasHeight)},renderLayer:function(t){for(var i,n,r,s,o,a,h,c,u,d=this,f=d.layers[t],l=d._getRow(),p=d._getCol(),m=p+l*d.width,g=p*d.tileWidth-d.sx,v=l*d.tileHeight-d.sy,y=e.ceil(d.canvasWidth/d.tileWidth)+1,x=e.ceil(d.canvasHeight/d.tileHeight)+1,b=y*x,w=0;b>w;)r=f[m],r&&(s=d._getTileset(r),o=s.image,i=g+w%y*d.tileWidth,n=v+(w/y|0)*d.tileHeight,a=r-s.firstGrid,h=o.width/d.tileWidth,c=a%h*d.tileWidth,u=(a/h|0)*d.tileHeight,d.context.drawImage(o,c,u,d.tileWidth,d.tileHeight,i,n,d.tileWidth,d.tileHeight)),++w%y===0?m=p+ ++l*d.width:m++},_getRow:function(t){return t=t||0,(this.sy+t)/this.tileHeight|0},_getCol:function(t){return t=t||0,(this.sx+t)/this.tileWidth|0},_getTileset:function(t){for(var e,i,n=0,r=this.tilesets.length-1;r>=n;){if(e=(n+r)/2|0,i=this.tilesets[e],t>=i.firstGrid&&t<=i.lastGrid)return i;t>i?n=e+1:r=e-1}},_preRenderImage:function(){for(var t,e,i,n,r,s,o,a,h,c,u=this,d=0;c=u.layers[u._layerOrder[d]];d++)for(var f=0,l=c.length;l>f;f++)t=c[f],t&&(e=u._getTileset(t),i=e.image,n=f%u.width*u.tileWidth,r=(f/u.width|0)*u.tileHeight,a=t-e.firstGrid,h=i.width/u.tileWidth,s=a%h*u.tileWidth,o=(a/h|0)*u.tileHeight,u._offscreenContext.drawImage(i,s,o,u.tileWidth,u.tileHeight,n,r,u.tileWidth,u.tileHeight))}},t}(kontra||{},Math); //# sourceMappingURL=kontra.min.js.map \ No newline at end of file diff --git a/kontra.min.js.map b/kontra.min.js.map index 9611a762..0d60e9ed 100644 --- a/kontra.min.js.map +++ b/kontra.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["kontraAssetLoader.js","core.js","gameLoop.js","keyboard.js","pool.js","quadtree.js","sprite.js","spriteSheet.js","store.js","tileEngine.js"],"names":["qFactory","nextTick","exceptionHandler","forEach","obj","iterator","context","key","isFunction","hasOwnProperty","call","isArray","length","defaultCallback","value","defaultErrback","reason","reject","all","promises","deferred","defer","counter","results","promise","ref","then","resolve","notify","toString","pending","val","callbacks","undefined","callback","i","ii","createInternalRejectedPromise","progress","errback","progressback","result","wrappedCallback","e","wrappedErrback","wrappedProgressback","push","catch","this","finally","makePromise","resolved","handleCallback","isResolved","callbackOutput","error","when","done","window","q","setTimeout","console","stack","kontra","isImage","isAudio","images","audios","data","assetPaths","audio","Audio","canUse","wav","mp3","canPlayType","replace","ogg","aac","m4a","getAssetExtension","url","substr","lastIndexOf","getAssetType","extension","match","getAssetName","loadAssets","type","numLoaded","numAssets","arguments","asset","Array","assetDeferred","loaded","total","loadImage","name","image","Image","onload","onerror","src","loadAudio","source","playableSource","addEventListener","preload","load","loadData","req","XMLHttpRequest","dataUrl","status","responseText","json","JSON","parse","open","send","bundles","createBundle","bundle","assets","loadBundles","apply","loadManifest","manifest","imagePath","audioPath","dataPath","Object","keys","document","init","properties","isString","canvas","getElementById","isCanvas","getElementsByTagName","ReferenceError","logError","getContext","game","width","height","message","noop","isNumber","HTMLImageElement","HTMLCanvasElement","timestamp","performance","now","Date","getTime","gameLoop","create","prototype","set","update","render","isStopped","_accumulator","_delta","fps","frame","_this","_rAF","requestAnimationFrame","bind","_now","_dt","_last","start","stop","cancelAnimationFrame","normalizeKeyCode","which","keyCode","normalizeKeys","combination","trim","modifier","modifierOrder","indexOf","toLowerCase","shiftKeys","join","getKeyCombination","keyMap","keydownEventHandler","split","pressedKeys","preventDefault","keyupEventHandler","aliases","blurEventHandler",8,9,13,16,17,18,20,27,32,33,34,35,36,37,38,39,40,45,46,91,92,93,144,145,106,107,109,110,111,186,187,188,189,190,191,192,219,220,221,222,"String","fromCharCode","~","!","@","#","$","%","^","&","*","(",")","_","+",":","\"","<",">","?","|","plus","leftwindow","select","SyntaxError","unbind","pressed","pool","createProperties","isAlive","objects","size","maxSize","Infinity","lastIndex","inUse","fill","unshift","get","x","getAliveObjects","slice","clear","dt","index","Math","max","j","quadtree","depth","maxDepth","maxObjects","isBranchNode","parentNode","bounds","y","subnodes","object","indices","node","_getIndex","add","_addToSubnode","_split","verticalMidpoint","horizontalMidpoint","position","intersectsTopQuadrants","intersectsBottomQuadrants","subWidth","subHeight","strokeStyle","strokeRect","excludedProperties","vector","clamp","xMin","yMin","xMax","yMax","_xMin","_xMax","_yMin","_yMax","_x","_y","defineProperties","min","sprite","advanceSprite","velocity","acceleration","timeToLive","drawRect","fillStyle","color","fillRect","drawImage","advanceAnimation","currentAnimation","drawAnimation","playAnimation","animations","dx","dy","ddx","ddy","advance","draw","prop",{"end":{"file":"?","comments_before":[],"nlb":false,"endpos":60046,"endcol":9,"endline":2145,"pos":60045,"col":8,"line":2145,"value":"x","type":"name"},"start":{"file":"?","comments_before":[],"nlb":false,"endpos":60046,"endcol":9,"endline":2145,"pos":60045,"col":8,"line":2145,"value":"x","type":"name"},"name":"x"},"collidesWith","animation","spriteSheet","frames","frameSpeed","currentFrame","row","framesPerRow","col","play","pause","frameWidth","frameHeight","createAnimations","sequence","_parseFrames","consecutiveFrames","map","Number","direction","localStorage","store","remove","setItem","stringify","getItem","removeItem","tileEngine","tileWidth","tileHeight","canvasWidth","canvasHeight","_offscreenCanvas","createElement","_offscreenContext","mapWidth","mapHeight","sxMax","syMax","layers","_layerOrder","tilesets","sx","sy","addTileset","firstGrid","numTiles","lastTileset","tiles","lastGrid","sort","a","b","addLayer","r","c","zIndex","_preRenderImage","layerCollidesWith","_getRow","_getCol","endRow","endCol","tileAtLayer","renderLayer","tile","tileset","tileOffset","layer","startX","startY","viewWidth","ceil","viewHeight","count","_getTileset","currTile","len"],"mappings":"AAuCA,QAAAA,UAAAC,EAAAC,GAKA,QAAAC,GAAAC,EAAAC,EAAAC,GACA,GAAAC,EACA,IAAAH,EACA,GAAAI,EAAAJ,GACA,IAAAG,IAAAH,GAGA,aAAAG,GAAA,UAAAA,GAAA,QAAAA,GAAAH,EAAAK,iBAAAL,EAAAK,eAAAF,IACAF,EAAAK,KAAAJ,EAAAF,EAAAG,GAAAA,OAGA,IAAAH,EAAAD,SAAAC,EAAAD,UAAAA,EACAC,EAAAD,QAAAE,EAAAC,OACA,IAAAK,EAAAP,GACA,IAAAG,EAAA,EAAAA,EAAAH,EAAAQ,OAAAL,IACAF,EAAAK,KAAAJ,EAAAF,EAAAG,GAAAA,OAEA,KAAAA,IAAAH,GACAA,EAAAK,eAAAF,IACAF,EAAAK,KAAAJ,EAAAF,EAAAG,GAAAA,EAKA,OAAAH,GA0RA,QAAAS,GAAAC,GACA,MAAAA,GAIA,QAAAC,GAAAC,GACA,MAAAC,GAAAD,GAmBA,QAAAE,GAAAC,GACA,GAAAC,GAAAC,IACAC,EAAA,EACAC,EAAAZ,EAAAQ,QAqBA,OAnBAhB,GAAAgB,EAAA,SAAAK,EAAAjB,GACAe,IACAG,EAAAD,GAAAE,KAAA,SAAAZ,GACAS,EAAAd,eAAAF,KACAgB,EAAAhB,GAAAO,IACAQ,GAAAF,EAAAO,QAAAJ,KACA,SAAAP,GACAO,EAAAd,eAAAF,IACAa,EAAAH,OAAAD,IACA,SAAAA,GACAO,EAAAd,eAAAF,IACAa,EAAAQ,OAAAZ,OAIA,IAAAM,GACAF,EAAAO,QAAAJ,GAGAH,EAAAI,QAvWA,GAAAK,MAAAA,SACArB,EAAA,SAAAM,GAAA,MAAA,kBAAAA,IACAH,EAAA,SAAAG,GAAA,MAAA,mBAAAe,EAAAnB,KAAAI,IAuCAO,EAAA,WACA,GACAP,GAAAM,EADAU,IAgIA,OA7HAV,IAEAO,QAAA,SAAAI,GACA,GAAAD,EAAA,CACA,GAAAE,GAAAF,CACAA,GAAAG,OACAnB,EAAAW,EAAAM,GAEAC,EAAApB,QACAX,EAAA,WAEA,IAAA,GADAiC,GACAC,EAAA,EAAAC,EAAAJ,EAAApB,OAAAwB,EAAAD,EAAAA,IACAD,EAAAF,EAAAG,GACArB,EAAAY,KAAAQ,EAAA,GAAAA,EAAA,GAAAA,EAAA,QAQAjB,OAAA,SAAAD,GACAI,EAAAO,QAAAU,EAAArB,KAIAY,OAAA,SAAAU,GACA,GAAAR,EAAA,CACA,GAAAE,GAAAF,CAEAA,GAAAlB,QACAX,EAAA,WAEA,IAAA,GADAiC,GACAC,EAAA,EAAAC,EAAAJ,EAAApB,OAAAwB,EAAAD,EAAAA,IACAD,EAAAF,EAAAG,GACAD,EAAA,GAAAI,OAQAd,SACAE,KAAA,SAAAQ,EAAAK,EAAAC,GACA,GAAAC,GAAApB,IAEAqB,EAAA,SAAA5B,GACA,IACA2B,EAAAd,SAAAnB,EAAA0B,GAAAA,EAAArB,GAAAC,IACA,MAAA6B,GACAF,EAAAxB,OAAA0B,GACAzC,EAAAyC,KAIAC,EAAA,SAAA5B,GACA,IACAyB,EAAAd,SAAAnB,EAAA+B,GAAAA,EAAAxB,GAAAC,IACA,MAAA2B,GACAF,EAAAxB,OAAA0B,GACAzC,EAAAyC,KAIAE,EAAA,SAAAP,GACA,IACAG,EAAAb,QAAApB,EAAAgC,GAAAA,EAAA3B,GAAAyB,IACA,MAAAK,GACAzC,EAAAyC,IAUA,OANAb,GACAA,EAAAgB,MAAAJ,EAAAE,EAAAC,IAEA/B,EAAAY,KAAAgB,EAAAE,EAAAC,GAGAJ,EAAAjB,SAGAuB,QAAA,SAAAb,GACA,MAAAc,MAAAtB,KAAA,KAAAQ,IAGAe,UAAA,SAAAf,GAEA,QAAAgB,GAAApC,EAAAqC,GACA,GAAAV,GAAApB,GAMA,OALA8B,GACAV,EAAAd,QAAAb,GAEA2B,EAAAxB,OAAAH,GAEA2B,EAAAjB,QAGA,QAAA4B,GAAAtC,EAAAuC,GACA,GAAAC,GAAA,IACA,KACAA,GAAApB,GAAArB,KACA,MAAA8B,GACA,MAAAO,GAAAP,GAAA,GAEA,MAAAW,IAAA9C,EAAA8C,EAAA5B,MACA4B,EAAA5B,KAAA,WACA,MAAAwB,GAAApC,EAAAuC,IACA,SAAAE,GACA,MAAAL,GAAAK,GAAA,KAGAL,EAAApC,EAAAuC,GAIA,MAAAL,MAAAtB,KAAA,SAAAZ,GACA,MAAAsC,GAAAtC,GAAA,IACA,SAAAyC,GACA,MAAAH,GAAAG,GAAA,SAUA9B,EAAA,SAAAX,GACA,MAAAA,IAAAN,EAAAM,EAAAY,MAAAZ,GAEAY,KAAA,SAAAQ,GACA,GAAAO,GAAApB,GAIA,OAHApB,GAAA,WACAwC,EAAAd,QAAAO,EAAApB,MAEA2B,EAAAjB,WA0CAP,EAAA,SAAAD,GACA,GAAAyB,GAAApB,GAEA,OADAoB,GAAAxB,OAAAD,GACAyB,EAAAjB,SAGAa,EAAA,SAAArB,GACA,OACAU,KAAA,SAAAQ,EAAAK,GACA,GAAAE,GAAApB,GASA,OARApB,GAAA,WACA,IACAwC,EAAAd,SAAAnB,EAAA+B,GAAAA,EAAAxB,GAAAC,IACA,MAAA2B,GACAF,EAAAxB,OAAA0B,GACAzC,EAAAyC,MAGAF,EAAAjB,WAmBAgC,EAAA,SAAA1C,EAAAoB,EAAAK,EAAAC,GACA,GACAiB,GADAhB,EAAApB,IAGAqB,EAAA,SAAA5B,GACA,IACA,OAAAN,EAAA0B,GAAAA,EAAArB,GAAAC,GACA,MAAA6B,GAEA,MADAzC,GAAAyC,GACA1B,EAAA0B,KAIAC,EAAA,SAAA5B,GACA,IACA,OAAAR,EAAA+B,GAAAA,EAAAxB,GAAAC,GACA,MAAA2B,GAEA,MADAzC,GAAAyC,GACA1B,EAAA0B,KAIAE,EAAA,SAAAP,GACA,IACA,OAAA9B,EAAAgC,GAAAA,EAAA3B,GAAAyB,GACA,MAAAK,GACAzC,EAAAyC,IAmBA,OAfA1C,GAAA,WACAwB,EAAAX,GAAAY,KAAA,SAAAZ,GACA2C,IACAA,GAAA,EACAhB,EAAAd,QAAAF,EAAAX,GAAAY,KAAAgB,EAAAE,EAAAC,MACA,SAAA7B,GACAyC,IACAA,GAAA,EACAhB,EAAAd,QAAAiB,EAAA5B,MACA,SAAAsB,GACAmB,GACAhB,EAAAb,OAAAiB,EAAAP,QAIAG,EAAAjB,QAwDA,QACAH,MAAAA,EACAJ,OAAAA,EACAuC,KAAAA,EACAtC,IAAAA,GA/XAwC,OAAAC,EAAA3D,SAAA,SAAAkC,GACA0B,WAAA,WACA1B,KACA,IACA,SAAAS,GACAkB,QAAAN,MAAA,UAAAZ,EAAAmB,QA6XA,IAAAC,QAAA,SAAAA,GACA,GAAAC,GAAA,sBACAC,EAAA,wBAIAF,GAAAG,UACAH,EAAAI,UACAJ,EAAAK,QAGAL,EAAAM,YACAH,OAAA,GACAC,OAAA,GACAC,KAAA,GAKA,IAAAE,GAAA,GAAAC,MAuDA,OAtDAR,GAAAS,OAAAT,EAAAS,WACAT,EAAAS,OAAAC,IAAA,GACAV,EAAAS,OAAAE,IAAAJ,EAAAK,YAAA,eAAAC,QAAA,OAAA,IACAb,EAAAS,OAAAK,IAAAP,EAAAK,YAAA,8BAAAC,QAAA,OAAA,IACAb,EAAAS,OAAAM,IAAAR,EAAAK,YAAA,cAAAC,QAAA,OAAA,IACAb,EAAAS,OAAAO,KAAAT,EAAAK,YAAA,iBAAAZ,EAAAS,OAAAM,KAAAF,QAAA,OAAA,IAWAb,EAAAiB,kBAAA,SAAAC,GACA,MAAAA,GAAAC,UAAAD,EAAAE,YAAA,OAAA,GAAA,IAWApB,EAAAqB,aAAA,SAAAH,GACA,GAAAI,GAAArC,KAAAgC,kBAAAC,EAEA,OAAAI,GAAAC,MAAAtB,GACA,QAEAqB,EAAAC,MAAArB,GACA,QAGA,QAYAF,EAAAwB,aAAA,SAAAN,GACA,MAAAA,GAAAL,QAAA,YAAA,KAGAb,GACAA,YAGAA,OAAA,SAAAA,EAAAJ,GAsNA,MAvMAI,GAAAyB,WAAA,WACA,GAIAC,GAAAR,EAJA7D,EAAAuC,EAAAtC,QACAF,KACAuE,EAAA,EACAC,EAAAC,UAAAhF,MAGAgF,WAAAhF,QACAQ,EAAAO,SAGA,KAAA,GAAAkE,GAAA1D,EAAA,EAAA0D,EAAAD,UAAAzD,GAAAA,IAKA8C,EAJAa,MAAAnF,QAAAkF,GAIAA,EAAA,GAHAA,EAMAJ,EAAAzC,KAAAoC,aAAAH,GAGA,SAAAc,GACA5E,EAAA2B,KAAAiD,EAAAvE,SAEAuC,EAAA,OAAA0B,GAAAR,GAAAvD,KACA,WACAqE,EAAApE,UACAP,EAAAQ,QAAAoE,SAAAN,EAAAO,MAAAN,KAEA,SAAApC,GACAwC,EAAA9E,OAAAsC,MAEAI,EAAAtC,QAWA,OARAsC,GAAAzC,IAAAC,GAAAO,KACA,WACAN,EAAAO,WAEA,SAAA4B,GACAnC,EAAAH,OAAAsC,KAGAnC,EAAAI,SAeAuC,EAAAmC,UAAA,SAAAjB,GACA,GAAA7D,GAAAuC,EAAAtC,QACA8E,EAAAnD,KAAAuC,aAAAN,GACAmB,EAAA,GAAAC,MAeA,OAbApB,GAAAjC,KAAAqB,WAAAH,OAAAe,EAEAmB,EAAAE,OAAA,WACAvC,EAAAG,OAAAiC,GAAApC,EAAAG,OAAAe,GAAAjC,KACA5B,EAAAO,QAAAqB,OAGAoD,EAAAG,QAAA,WACAnF,EAAAH,OAAA,wBAAAgE,IAGAmB,EAAAI,IAAAvB,EAEA7D,EAAAI,SAmCAuC,EAAA0C,UAAA,SAAAxB,GACA,GACAyB,GAAAP,EAAAQ,EAAArC,EADAlD,EAAAuC,EAAAtC,OAGAyE,OAAAnF,QAAAsE,KACAA,GAAAA,GAIA,KAAA,GAAA9C,GAAA,EAAAuE,EAAAzB,EAAA9C,GAAAA,IACA,GAAAa,KAAAwB,OAAAxB,KAAAgC,kBAAA0B,IAAA,CACAC,EAAAD,CACA,OA2BA,MAvBAC,IAIAR,EAAAnD,KAAAuC,aAAAoB,GACArC,EAAA,GAAAC,OAEAmC,EAAA1D,KAAAqB,WAAAF,OAAAwC,EAEArC,EAAAsC,iBAAA,UAAA,WACA7C,EAAAI,OAAAgC,GAAApC,EAAAI,OAAAuC,GAAA1D,KACA5B,EAAAO,QAAAqB,QAGAsB,EAAAiC,QAAA,WACAnF,EAAAH,OAAA,wBAAAyF,IAGApC,EAAAkC,IAAAE,EACApC,EAAAuC,QAAA,OACAvC,EAAAwC,QAnBA1F,EAAAH,OAAA,yDAsBAG,EAAAI,SAgBAuC,EAAAgD,SAAA,SAAA9B,GACA,GAAA7D,GAAAuC,EAAAtC,QACA2F,EAAA,GAAAC,gBACAd,EAAAnD,KAAAuC,aAAAN,GACAiC,EAAAlE,KAAAqB,WAAAD,KAAAa,CAyBA,OAvBA+B,GAAAJ,iBAAA,OAAA,WACA,GAAA,MAAAI,EAAAG,OAEA,WADA/F,GAAAH,OAAA+F,EAAAI,aAIA,KACA,GAAAC,GAAAC,KAAAC,MAAAP,EAAAI,aACArD,GAAAK,KAAA+B,GAAApC,EAAAK,KAAA8C,GAAAG,EAEAjG,EAAAO,QAAA0F,GAEA,MAAA1E,GACA,GAAAyB,GAAA4C,EAAAI,YACArD,GAAAK,KAAA+B,GAAApC,EAAAK,KAAA8C,GAAA9C,EAEAhD,EAAAO,QAAAyC,MAIA4C,EAAAQ,KAAA,MAAAN,GAAA,GACAF,EAAAS,OAEArG,EAAAI,SAGAuC,GACAA,WAAAJ,GAGAI,OAAA,SAAAA,EAAAJ,GAiEA,MAhEAI,GAAA2D,WAYA3D,EAAA4D,aAAA,SAAAC,EAAAC,GACA7E,KAAA0E,QAAAE,KAIA5E,KAAA0E,QAAAE,GAAAC,QAeA9D,EAAA+D,YAAA,WAOA,IAAA,GAFAD,GAEAD,EANAxG,EAAAuC,EAAAtC,QACAF,KACAuE,EAAA,EACAC,EAAA,EAGAxD,EAAA,EAAAyF,EAAAhC,UAAAzD,GAAAA,KACA0F,EAAA7E,KAAA0E,QAAAE,KAKAjC,GAAAkC,EAAAjH,OAEAO,EAAA2B,KAAAE,KAAAwC,WAAAuC,MAAA/E,KAAA6E,KANAzG,EAAAH,OAAA,WAAA2G,EAAA,0BAoBA,OAXAjE,GAAAzC,IAAAC,GAAAO,KACA,WACAN,EAAAO,WAEA,SAAA4B,GACAnC,EAAAH,OAAAsC,IAEA,WACAnC,EAAAQ,QAAAoE,SAAAN,EAAAO,MAAAN,MAGAvE,EAAAI,SAGAuC,GACAA,WAAAJ,GAGAI,OAAA,SAAAA,EAAAJ,GA4DA,MAnDAI,GAAAiE,aAAA,SAAA/C,GACA,GACAyC,GADAtG,EAAAuC,EAAAtC,OA+CA,OA5CA0C,GAAAgD,SAAA9B,GAAAvD,KACA,SAAAuG,GACAlE,EAAAM,WAAAH,OAAA+D,EAAAC,WAAA,GACAnE,EAAAM,WAAAF,OAAA8D,EAAAE,WAAA,GACApE,EAAAM,WAAAD,KAAA6D,EAAAG,UAAA,EAGA,KAAA,GAAAR,GAAAzF,EAAA,EAAAyF,EAAAK,EAAAP,QAAAvF,GAAAA,IACA4B,EAAA4D,aAAAC,EAAAzB,KAAAyB,EAAAC,OAGA,OAAAI,GAAAH,aAOAJ,EADA,QAAAO,EAAAH,YACAO,OAAAC,KAAAvE,EAAA2D,aAGA5B,MAAAnF,QAAAsH,EAAAH,aAKAG,EAAAH,aAJAG,EAAAH,iBAOA/D,GAAA+D,YAAAC,MAAAhE,EAAA2D,GAAAhG,KACA,WACAN,EAAAO,WAEA,SAAA4B,GACAnC,EAAAH,OAAAsC,IAEA,SAAAjB,GACAlB,EAAAQ,OAAAU,UAzBAlB,GAAAO,WA4BA,SAAA4B,GACAnC,EAAAH,OAAAsC,KAGAnC,EAAAI,SAGAuC,GACAA,WAAAJ,GCj0BAI,OAAA,SAAAA,EAAAwE,GACA,YA8GA,OArGAxE,GAAAyE,KAAA,SAAAC,GAGA,GAFAA,EAAAA,MAEA1E,EAAA2E,SAAAD,EAAAE,QACA3F,KAAA2F,OAAAJ,EAAAK,eAAAH,EAAAE,YAEA,IAAA5E,EAAA8E,SAAAJ,EAAAE,QACA3F,KAAA2F,OAAAF,EAAAE,WAKA,IAFA3F,KAAA2F,OAAAJ,EAAAO,qBAAA,UAAA,IAEA9F,KAAA2F,OAAA,CACA,GAAApF,GAAA,GAAAwF,gBAAA,2BAEA,YADAhF,GAAAiF,SAAAzF,EAAA,mDAKAP,KAAA1C,QAAA0C,KAAA2F,OAAAM,WAAA,MACAjG,KAAAkG,MACAC,MAAAnG,KAAA2F,OAAAQ,MACAC,OAAApG,KAAA2F,OAAAS,SAWArF,EAAAiF,SAAA,SAAAzF,EAAA8F,GACAxF,QAAAN,MAAA,WAAA8F,EAAA,MAAA9F,EAAAO,QAOAC,EAAAuF,KAAA,aAUAvF,EAAApD,QAAAmF,MAAAnF,QAUAoD,EAAA2E,SAAA,SAAA5H,GACA,MAAA,gBAAAA,IAWAiD,EAAAwF,SAAA,SAAAzI,GACA,MAAA,gBAAAA,IAWAiD,EAAAC,QAAA,SAAAlD,GACA,MAAAA,aAAA0I,mBAWAzF,EAAA8E,SAAA,SAAA/H,GACA,MAAAA,aAAA2I,oBAGA1F,GACAA,WAAAwE,UClHAxE,OAAA,SAAAA,EAAAL,GACA,YAkHA,OAzGAK,GAAA2F,UAAA,WACA,MAAAhG,GAAAiG,aAAAjG,EAAAiG,YAAAC,IACA,WACA,MAAAlG,GAAAiG,YAAAC,OAIA,WACA,OAAA,GAAAC,OAAAC,cAWA/F,EAAAgG,SAAA,SAAAtB,GACA,GAAAsB,GAAA1B,OAAA2B,OAAAjG,EAAAgG,SAAAE,UAGA,OAFAF,GAAAG,IAAAzB,GAEAsB,GAGAhG,EAAAgG,SAAAE,WAUAC,IAAA,SAAAzB,GAIA,GAHAA,EAAAA,MAGA,kBAAAA,GAAA0B,QAAA,kBAAA1B,GAAA2B,OAAA,CACA,GAAA7G,GAAA,GAAAwF,gBAAA,+BAEA,YADAhF,GAAAiF,SAAAzF,EAAA,2EAIAP,KAAAqH,WAAA,EAGArH,KAAAsH,aAAA,EACAtH,KAAAuH,OAAA,KAAA9B,EAAA+B,KAAA,IAEAxH,KAAAmH,OAAA1B,EAAA0B,OACAnH,KAAAoH,OAAA3B,EAAA2B,QAOAK,MAAA,WACA,GAAAC,GAAA1H,IAUA,IARA0H,EAAAC,KAAAC,sBAAAF,EAAAD,MAAAI,KAAAH,IAEAA,EAAAI,KAAA/G,EAAA2F,YACAgB,EAAAK,IAAAL,EAAAI,KAAAJ,EAAAM,MACAN,EAAAM,MAAAN,EAAAI,OAIAJ,EAAAK,IAAA,KAAA,CAMA,IAFAL,EAAAJ,cAAAI,EAAAK,IAEAL,EAAAJ,cAAAI,EAAAH,QACAG,EAAAP,OAAAO,EAAAH,OAAA,KAEAG,EAAAJ,cAAAI,EAAAH,MAGAG,GAAAN,WAOAa,MAAA,WACAjI,KAAAgI,MAAAjH,EAAA2F,YACA1G,KAAAqH,WAAA,EACAO,sBAAA5H,KAAAyH,MAAAI,KAAA7H,QAMAkI,KAAA,WACAlI,KAAAqH,WAAA,EACAc,qBAAAnI,KAAA2H,QAIA5G,GACAA,WAAAL,QClHAK,OAAA,SAAAA,EAAAL,GACA,YAoLA,SAAA0H,GAAAzI,GACA,MAAA,gBAAAA,GAAA0I,MAAA1I,EAAA0I,MAAA1I,EAAA2I,QAeA,QAAAC,GAAAjD,GACA,GAAAkD,KAGAlD,GAAAA,EAAAmD,OAAA7G,QAAA,KAAA,QAGA,KAAA,GAAA8G,GAAAvJ,EAAA,EAAAuJ,EAAAC,EAAAxJ,GAAAA,IAGA,KAAAmG,EAAAsD,QAAAF,KACAF,EAAA1I,KAAA4I,GACApD,EAAAA,EAAA1D,QAAA8G,EAAA,IAeA,OAVApD,GAAAA,EAAA1D,QAAA,MAAA,IAAAiH,cAGAC,EAAAxD,GACAkD,EAAA1I,KAAA,SAAAgJ,EAAAxD,IAEAA,GACAkD,EAAA1I,KAAAwF,GAGAkD,EAAAO,KAAA,KAWA,QAAAC,GAAArJ,GAIA,IAAA,GAAA+I,GAHAF,KAGArJ,EAAA,EAAAuJ,EAAAC,EAAAxJ,GAAAA,IACAQ,EAAA+I,EAAA,QACAF,EAAA1I,KAAA4I,EAIA,IAAAnL,GAAA0L,EAAAb,EAAAzI,GASA,OAJA,KAAA6I,EAAAI,QAAArL,IACAiL,EAAA1I,KAAAvC,GAGAiL,EAAAO,KAAA,KASA,QAAAG,GAAAvJ,GAIA,IAAA,GAAApC,GAHAiL,EAAAQ,EAAArJ,GAGAR,EAAA,EAAAmG,EAAAkD,EAAAW,MAAA,KAAA5L,EAAA+H,EAAAnG,GAAAA,IACAiK,EAAA7L,IAAA,CAGAyB,GAAAwJ,KACAxJ,EAAAwJ,GAAA7I,EAAA6I,GACA7I,EAAA0J,kBAUA,QAAAC,GAAA3J,GACA,GAAApC,GAAA0L,EAAAb,EAAAzI,GACAyJ,GAAA7L,IAAA,EAEAgM,EAAAhM,KACA6L,EAAAG,EAAAhM,KAAA,GAUA,QAAAiM,GAAA7J,GACAyJ,KAtPA,IAAA,GAlDApK,MACAoK,KAEAH,GAEAQ,EAAA,YACAC,EAAA,MACAC,GAAA,QACAC,GAAA,QACAC,GAAA,OACAC,GAAA,MACAC,GAAA,WACAC,GAAA,MACAC,GAAA,QACAC,GAAA,SACAC,GAAA,WACAC,GAAA,MACAC,GAAA,OACAC,GAAA,OACAC,GAAA,KACAC,GAAA,QACAC,GAAA,OACAC,GAAA,SACAC,GAAA,SACAC,GAAA,aACAC,GAAA,cACAC,GAAA,SACAC,IAAA,UACAC,IAAA,aAGAC,IAAA,IACAC,IAAA,IACAC,IAAA,IACAC,IAAA,IACAC,IAAA,IACAC,IAAA,IACAC,IAAA,IACAC,IAAA,IACAC,IAAA,IACAC,IAAA,IACAC,IAAA,IACAC,IAAA,IACAC,IAAA,IACAC,IAAA,KACAC,IAAA,IACAC,IAAA,KAIA7M,EAAA,EAAA,GAAAA,EAAAA,IACA8J,EAAA,GAAA9J,GAAA8M,OAAAC,aAAA,GAAA/M,GAAA0J,aAGA,KAAA1J,EAAA,EAAA,GAAAA,EAAAA,IACA8J,EAAA,GAAA9J,GAAA,GAAAA,CAGA,KAAAA,EAAA,EAAA,GAAAA,EAAAA,IACA8J,EAAA,IAAA9J,GAAA,IAAAA,CAGA,KAAAA,EAAA,EAAA,GAAAA,EAAAA,IACA8J,EAAA,GAAA9J,GAAA,SAAAA,CAIA,IAAA2J,IACAqD,IAAA,IACAC,IAAA,IACAC,IAAA,IACAC,IAAA,IACAC,EAAA,IACAC,IAAA,IACAC,IAAA,IACAC,IAAA,IACAC,IAAA,IACAC,IAAA,IACAC,IAAA,IACAC,EAAA,IACAC,IAAA,IACAC,IAAA,IACAC,IAAA,IACAC,IAAA,IACAC,IAAA,IACAC,IAAA,IACAC,IAAA,KACAC,KAAA,KAIA/D,GACAgE,WAAA,OACAC,OAAA,QAIA7E,GAAA,OAAA,OAAA,MAAA,QA0MA,OAxMAjI,GAAAkD,iBAAA,UAAAsF,GACAxI,EAAAkD,iBAAA,QAAA0F,GACA5I,EAAAkD,iBAAA,OAAA4F,GAKAzI,EAAAuE,QAWAvE,EAAAuE,KAAAuC,KAAA,SAAAvC,EAAApG,GACA,GAAA,kBAAAA,GAAA,CACA,GAAAqB,GAAA,GAAAkN,aAAA,oBAEA,YADA1M,GAAAiF,SAAAzF,EAAA,wDAIA+E,EAAAvE,EAAApD,QAAA2H,GAAAA,GAAAA,EAEA,KAAA,GAAA/H,GAAA4B,EAAA,EAAA5B,EAAA+H,EAAAnG,GAAAA,IAAA,CACA,GAAAqJ,GAAAD,EAAAhL,EAEAyB,GAAAwJ,GAAAtJ,IAUA6B,EAAAuE,KAAAoI,OAAA,SAAApI,GACAA,EAAAvE,EAAApD,QAAA2H,GAAAA,GAAAA,EAEA,KAAA,GAAA/H,GAAA4B,EAAA,EAAA5B,EAAA+H,EAAAnG,GAAAA,IAAA,CACA,GAAAqJ,GAAAD,EAAAhL,EAEAyB,GAAAwJ,GAAAvJ,SAYA8B,EAAAuE,KAAAqI,QAAA,SAAArI,GACA,GAAAkD,GAAAD,EAAAjD,GACAqI,GAAA,CAGArI,GAAAkD,EAAAW,MAAA,IACA,KAAA,GAAA5L,GAAA4B,EAAA,EAAA5B,EAAA+H,EAAAnG,GAAAA,IACAwO,EAAAA,KAAAvE,EAAA7L,EAGA,OAAAoQ,IAoIA5M,GACAA,WAAAL,QC/SAK,OAAA,SAAAA,GACA,YA2MA,OAlMAA,GAAA6M,KAAA,SAAAnI,GACA,GAAAmI,GAAAvI,OAAA2B,OAAAjG,EAAA6M,KAAA3G,UAGA,OAFA2G,GAAA1G,IAAAzB,GAEAmI,GAGA7M,EAAA6M,KAAA3G,WAcAC,IAAA,SAAAzB,GACAA,EAAAA,KAEA,IAAAlF,GAAAnD,CAIA,IAAA,kBAAAqI,GAAAuB,OAGA,MAFAzG,GAAA,GAAAkN,aAAA,oCACA1M,GAAAiF,SAAAzF,EAAA,gEAUA,IALAP,KAAAgH,OAAAvB,EAAAuB,OAAAa,KAAA7H,KAAAyF,EAAAoI,sBAGAzQ,EAAA4C,KAAAgH,UAEA5J,GAAA,kBAAAA,GAAAgK,QAAA,kBAAAhK,GAAA+J,QACA,kBAAA/J,GAAA8J,KAAA,kBAAA9J,GAAA0Q,QAGA,MAFAvN,GAAA,GAAAkN,aAAA,mDACA1M,GAAAiF,SAAAzF,EAAA,yFAYA,IAPAP,KAAA+N,SAAA3Q,GACA4C,KAAAgO,KAAA,EACAhO,KAAAiO,QAAAxI,EAAAwI,SAAAC,EAAAA,EACAlO,KAAAmO,UAAA,EACAnO,KAAAoO,MAAA,EAGA3I,EAAA4I,KAAA,CACA,IAAA5I,EAAAwI,QAWA,MAFA1N,GAAA,GAAAkN,aAAA,oCACA1M,GAAAiF,SAAAzF,EAAA,8DATA,MAAAP,KAAA+N,QAAAnQ,OAAAoC,KAAAiO,SACAjO,KAAA+N,QAAAO,QAAAtO,KAAAgH,SAGAhH,MAAAgO,KAAAhO,KAAAiO,QACAjO,KAAAmO,UAAAnO,KAAAiO,QAAA,IAgBAM,IAAA,SAAA9I,GACAA,EAAAA,KAEA,IAAAiC,GAAA1H,IAGA,IAAA0H,EAAAqG,QAAA,GAAAD,UAAA,CACA,GAAApG,EAAAsG,OAAAtG,EAAAuG,QACA,MAIA,KAAA,GAAAO,GAAA,EAAAA,EAAA9G,EAAAsG,MAAAtG,EAAAqG,QAAAnQ,OAAA8J,EAAAuG,QAAAO,IACA9G,EAAAqG,QAAAO,QAAA5G,EAAAV,SAGAU,GAAAsG,KAAAtG,EAAAqG,QAAAnQ,OACA8J,EAAAyG,UAAAzG,EAAAsG,KAAA,EAKA,GAAA5Q,GAAAsK,EAAAqG,QAAA,EACA3Q,GAAA8J,IAAAzB,EAGA,KAAA,GAAAtG,GAAA,EAAAA,EAAAuI,EAAAsG,KAAA7O,IACAuI,EAAAqG,QAAA5O,EAAA,GAAAuI,EAAAqG,QAAA5O,EAGAuI,GAAAqG,QAAArG,EAAAyG,WAAA/Q,EACAsK,EAAA0G,SASAK,gBAAA,WACA,MAAAzO,MAAA+N,QAAAW,MAAA1O,KAAA+N,QAAAnQ,OAAAoC,KAAAoO,QAOAO,MAAA,WACA3O,KAAAoO,MAAA,EACApO,KAAAgO,KAAA,EACAhO,KAAAmO,UAAA,EACAnO,KAAA+N,QAAAnQ,OAAA,EACAoC,KAAA+N,QAAAjO,KAAAE,KAAAgH,WASAG,OAAA,SAAAyH,GAeA,IAdA,GACAxR,GADA+B,EAAAa,KAAAmO,UAWAU,EAAAC,KAAAC,IAAA/O,KAAA+N,QAAAnQ,OAAAoC,KAAAoO,MAAA,GAGAjP,GAAA0P,GAMA,GALAzR,EAAA4C,KAAA+N,QAAA5O,GAEA/B,EAAA+J,OAAAyH,GAGAxR,EAAA0Q,UAeA3O,QAfA,CAMA,IAAA,GAAA6P,GAAA7P,EAAA6P,EAAA,EAAAA,IACAhP,KAAA+N,QAAAiB,GAAAhP,KAAA+N,QAAAiB,EAAA,EAGAhP,MAAA+N,QAAA,GAAA3Q,EACA4C,KAAAoO,QACAS,MAYAzH,OAAA,WAGA,IAAA,GAFAyH,GAAAC,KAAAC,IAAA/O,KAAA+N,QAAAnQ,OAAAoC,KAAAoO,MAAA,GAEAjP,EAAAa,KAAAmO,UAAAhP,GAAA0P,EAAA1P,IACAa,KAAA+N,QAAA5O,GAAAiI,WAKArG,GACAA,YC7MAA,OAAA,SAAAA,EAAA9B,GACA,YA2QA,OA1PA8B,GAAAkO,SAAA,SAAAxJ,GACA,GAAAwJ,GAAA5J,OAAA2B,OAAAjG,EAAAkO,SAAAhI,UAGA,OAFAgI,GAAA/H,IAAAzB,GAEAwJ,GAGAlO,EAAAkO,SAAAhI,WAWAC,IAAA,SAAAzB,GACAA,EAAAA,MAEAzF,KAAAkP,MAAAzJ,EAAAyJ,OAAA,EACAlP,KAAAmP,SAAA1J,EAAA0J,UAAA,EACAnP,KAAAoP,WAAA3J,EAAA2J,YAAA,GAIApP,KAAAqP,cAAA,EAEArP,KAAAsP,WAAA7J,EAAA6J,WAEAtP,KAAAuP,OAAA9J,EAAA8J,SACAf,EAAA,EACAgB,EAAA,EACArJ,MAAApF,EAAAmF,KAAAC,MACAC,OAAArF,EAAAmF,KAAAE,QAGApG,KAAA+N,WACA/N,KAAAyP,aAOAd,MAAA,WACA,GAAA3O,KAAAqP,aACA,IAAA,GAAAlQ,GAAA,EAAA,EAAAA,EAAAA,IACAa,KAAAyP,SAAAtQ,GAAAwP,OAIA3O,MAAAqP,cAAA,EACArP,KAAA+N,QAAAnQ,OAAA,GAYA2Q,IAAA,SAAAmB,GAMA,IALA,GAEAC,GAAAd,EAFAe,EAAA5P,KACA+N,KAIA6B,EAAAH,SAAA7R,QAAAoC,KAAAqP,cAAA,CACAM,EAAA3P,KAAA6P,UAAAH,EAEA,KAAA,GAAAvQ,GAAA,EAAAvB,EAAA+R,EAAA/R,OAAAA,EAAAuB,EAAAA,IACA0P,EAAAc,EAAAxQ,GAEA4O,EAAAjO,KAAAiF,MAAAgJ,EAAA/N,KAAAyP,SAAAZ,GAAAN,IAAAmB,GAGA,OAAA3B,GAGA,MAAA6B,GAAA7B,SASA+B,IAAA,WAIA,IAAA,GAFA3Q,GAAAuQ,EAAAtS,EADAsK,EAAA1H,KAGAgP,EAAA,EAAApR,EAAAgF,UAAAhF,OAAAA,EAAAoR,EAAAA,IAIA,GAHAU,EAAA9M,UAAAoM,GAGAjO,EAAApD,QAAA+R,GACAhI,EAAAoI,IAAA/K,MAAA/E,KAAA0P,OAMA,IAAAhI,EAAA+H,SAAA7R,QAAA8J,EAAA2H,aACA3H,EAAAqI,cAAAL,OASA,IAHAhI,EAAAqG,QAAAjO,KAAA4P,GAGAhI,EAAAqG,QAAAnQ,OAAA8J,EAAA0H,YAAA1H,EAAAwH,MAAAxH,EAAAyH,SAAA,CAIA,IAHAzH,EAAAsI,SAGA7Q,EAAA,EAAA/B,EAAAsK,EAAAqG,QAAA5O,GAAAA,IACAuI,EAAAqI,cAAA3S,EAGAsK,GAAAqG,QAAAnQ,OAAA,IAYAmS,cAAA,SAAAL,GAIA,IAAA,GAHAC,GAAA3P,KAAA6P,UAAAH,GAGAvQ,EAAA,EAAAvB,EAAA+R,EAAA/R,OAAAA,EAAAuB,EAAAA,IACAa,KAAAyP,SAAAE,EAAAxQ,IAAA2Q,IAAAJ,IAaAG,UAAA,SAAAH,GACA,GAAAC,MAEAM,EAAAjQ,KAAAuP,OAAAf,EAAAxO,KAAAuP,OAAApJ,MAAA,EACA+J,EAAAlQ,KAAAuP,OAAAC,EAAAxP,KAAAuP,OAAAnJ,OAAA,EAGAoI,EAAAkB,EAAAlB,IAAAvP,EAAAyQ,EAAAlB,EAAAkB,EAAAS,SAAA3B,EACAgB,EAAAE,EAAAF,IAAAvQ,EAAAyQ,EAAAF,EAAAE,EAAAS,SAAAX,EAGAY,EAAAF,EAAAV,GAAAA,EAAAE,EAAAtJ,QAAApG,KAAAuP,OAAAC,EACAa,EAAAb,EAAAE,EAAAtJ,QAAA8J,GAAAV,EAAAxP,KAAAuP,OAAAC,EAAAxP,KAAAuP,OAAAnJ,MAwBA,OArBA6J,GAAAzB,GAAAA,EAAAkB,EAAAvJ,OAAAnG,KAAAuP,OAAAf,IACA4B,GACAT,EAAA7P,KAAA,GAGAuQ,GACAV,EAAA7P,KAAA,IAKA0O,EAAAkB,EAAAvJ,OAAA8J,GAAAzB,EAAAxO,KAAAuP,OAAAf,EAAAxO,KAAAuP,OAAApJ,QACAiK,GACAT,EAAA7P,KAAA,GAGAuQ,GACAV,EAAA7P,KAAA,IAIA6P,GAQAK,OAAA,WAIA,GAHAhQ,KAAAqP,cAAA,GAGArP,KAAAyP,SAAA7R,OASA,IAAA,GALA0S,GAAAtQ,KAAAuP,OAAApJ,MAAA,EAAA,EACAoK,EAAAvQ,KAAAuP,OAAAnJ,OAAA,EAAA,EACAoI,EAAAxO,KAAAuP,OAAAf,EACAgB,EAAAxP,KAAAuP,OAAAC,EAEArQ,EAAA,EAAA,EAAAA,EAAAA,IACAa,KAAAyP,SAAAtQ,GAAA4B,EAAAkO,UACAM,QACAf,EAAAA,GAAArP,EAAA,IAAA,EAAAmR,EAAA,GACAd,EAAAA,GAAArQ,GAAA,EAAAoR,EAAA,GACApK,MAAAmK,EACAlK,OAAAmK,GAEArB,MAAAlP,KAAAkP,MAAA,EACAC,SAAAnP,KAAAmP,SACAC,WAAApP,KAAAoP,WACAE,WAAAtP,QASAoH,OAAA,WAEA,IAAApH,KAAA+N,QAAAnQ,QAAA,IAAAoC,KAAAkP,OACAlP,KAAAsP,YAAAtP,KAAAsP,WAAAD,gBAEAtO,EAAAzD,QAAAkT,YAAA,MACAzP,EAAAzD,QAAAmT,WAAAzQ,KAAAuP,OAAAf,EAAAxO,KAAAuP,OAAAC,EAAAxP,KAAAuP,OAAApJ,MAAAnG,KAAAuP,OAAAnJ,QAEApG,KAAAyP,SAAA7R,QACA,IAAA,GAAAuB,GAAA,EAAA,EAAAA,EAAAA,IACAa,KAAAyP,SAAAtQ,GAAAiI,WAOArG,GACAA,YC/QAA,OAAA,SAAAA,EAAA+N,EAAA7P,GACA,YAGA,IAAAyR,IACA,IACA,IACA,KACA,KACA,MACA,MACA,aACA,UACA,QACA,aACA,QACA,QACA,SAgaA,OAvZA3P,GAAA4P,OAAA,SAAAnC,EAAAgB,GACA,GAAAmB,GAAAtL,OAAA2B,OAAAjG,EAAA4P,OAAA1J,UAGA,OAFA0J,GAAAzJ,IAAAsH,EAAAgB,GAEAmB,GAGA5P,EAAA4P,OAAA1J,WAUAC,IAAA,SAAAsH,EAAAgB,GAIA,MAHAxP,MAAAwO,EAAAA,GAAA,EACAxO,KAAAwP,EAAAA,GAAA,EAEAxP,MAUA8P,IAAA,SAAAa,EAAA/B,GACA5O,KAAAwO,IAAAmC,EAAAnC,GAAA,IAAAI,GAAA,GACA5O,KAAAwP,IAAAmB,EAAAnB,GAAA,IAAAZ,GAAA,IAaAgC,MAAA,SAAAC,EAAAC,EAAAC,EAAAC,GACAhR,KAAAiR,MAAAJ,IAAA5R,EAAA4R,IAAA3C,EAAAA,GACAlO,KAAAkR,MAAAH,IAAA9R,EAAA8R,EAAA7C,EAAAA,EACAlO,KAAAmR,MAAAL,IAAA7R,EAAA6R,IAAA5C,EAAAA,GACAlO,KAAAoR,MAAAJ,IAAA/R,EAAA+R,EAAA9C,EAAAA,EAGAlO,KAAAqR,GAAArR,KAAAwO,EACAxO,KAAAsR,GAAAtR,KAAAwP,EAGAnK,OAAAkM,iBAAAvR,MACAwO,GACAD,IAAA,WACA,MAAAvO,MAAAqR,IAEAnK,IAAA,SAAApJ,GACAkC,KAAAqR,GAAAvC,EAAA0C,IAAA1C,EAAAC,IAAAjR,EAAAkC,KAAAiR,OAAAjR,KAAAkR,SAGA1B,GACAjB,IAAA,WACA,MAAAvO,MAAAsR,IAEApK,IAAA,SAAApJ,GACAkC,KAAAsR,GAAAxC,EAAA0C,IAAA1C,EAAAC,IAAAjR,EAAAkC,KAAAmR,OAAAnR,KAAAoR,aAkBArQ,EAAA0Q,OAAA,SAAAhM,GACA,GAAAgM,GAAApM,OAAA2B,OAAAjG,EAAA0Q,OAAAxK,UAGA,OAFAwK,GAAAvK,IAAAzB,GAEAgM,GAGA1Q,EAAA0Q,OAAAxK,WAOAyK,cAAA,SAAA9C,GACA5O,KAAA2R,SAAA7B,IAAA9P,KAAA4R,aAAAhD,GACA5O,KAAAmQ,SAAAL,IAAA9P,KAAA2R,SAAA/C,GAEA5O,KAAA6R,cAOAC,SAAA,WACA9R,KAAA1C,QAAAyU,UAAA/R,KAAAgS,MACAhS,KAAA1C,QAAA2U,SAAAjS,KAAAmQ,SAAA3B,EAAAxO,KAAAmQ,SAAAX,EAAAxP,KAAAmG,MAAAnG,KAAAoG,SAOA8L,UAAA,WACAlS,KAAA1C,QAAA4U,UAAAlS,KAAAoD,MAAApD,KAAAmQ,SAAA3B,EAAAxO,KAAAmQ,SAAAX,IASA2C,iBAAA,SAAAvD,GACA5O,KAAA0R,cAAA9C,GAEA5O,KAAAoS,iBAAAjL,OAAAyH,IAOAyD,cAAA,WACArS,KAAAoS,iBAAAhL,QACA9J,QAAA0C,KAAA1C,QACAkR,EAAAxO,KAAAmQ,SAAA3B,EACAgB,EAAAxP,KAAAmQ,SAAAX,KAUA8C,cAAA,SAAAnP,GACAnD,KAAAoS,iBAAApS,KAAAuS,WAAApP,IASA2K,QAAA,WACA,MAAA9N,MAAA6R,WAAA,GAiCA3K,IAAA,SAAAzB,GACAA,EAAAA,KAEA,IAAAiC,GAAA1H,IAEA0H,GAAAyI,UAAAzI,EAAAyI,UAAApP,EAAA4P,UAAAzJ,IAAAzB,EAAA+I,EAAA/I,EAAA+J,GACA9H,EAAAiK,UAAAjK,EAAAiK,UAAA5Q,EAAA4P,UAAAzJ,IAAAzB,EAAA+M,GAAA/M,EAAAgN,IACA/K,EAAAkK,cAAAlK,EAAAkK,cAAA7Q,EAAA4P,UAAAzJ,IAAAzB,EAAAiN,IAAAjN,EAAAkN,KAEAjL,EAAAmK,WAAApM,EAAAoM,YAAA,EACAnK,EAAApK,QAAAmI,EAAAnI,SAAAyD,EAAAzD,QAGAyD,EAAAC,QAAAyE,EAAArC,QAAArC,EAAA8E,SAAAJ,EAAArC,QACAsE,EAAAtE,MAAAqC,EAAArC,MACAsE,EAAAvB,MAAAV,EAAArC,MAAA+C,MACAuB,EAAAtB,OAAAX,EAAArC,MAAAgD,OAGAsB,EAAAkL,QAAAlL,EAAAgK,cACAhK,EAAAmL,KAAAnL,EAAAwK,WAGAzM,EAAA8M,YACA7K,EAAA6K,WAAA9M,EAAA8M,WAGA7K,EAAA0K,iBAAA3M,EAAA8M,WAAAlN,OAAAC,KAAAG,EAAA8M,YAAA,IACA7K,EAAAvB,MAAAuB,EAAA0K,iBAAAjM,MACAuB,EAAAtB,OAAAsB,EAAA0K,iBAAAhM,OAGAsB,EAAAkL,QAAAlL,EAAAyK,iBACAzK,EAAAmL,KAAAnL,EAAA2K,gBAIA3K,EAAAsK,MAAAvM,EAAAuM,MACAtK,EAAAvB,MAAAV,EAAAU,MACAuB,EAAAtB,OAAAX,EAAAW,OAGAsB,EAAAkL,QAAAlL,EAAAgK,cACAhK,EAAAmL,KAAAnL,EAAAoK,SAIA,KAAA,GAAAgB,KAAArN,GACAA,EAAAhI,eAAAqV,IAAA,KAAApC,EAAA9H,QAAAkK,KACApL,EAAAoL,GAAArN,EAAAqN,KAcAC,GAAAvE,KACA,MAAAxO,MAAAmQ,SAAA3B,GASAuE,GAAAvD,KACA,MAAAxP,MAAAmQ,SAAAX,GASAuD,GAAAP,MACA,MAAAxS,MAAA2R,SAAAnD,GASAuE,GAAAN,MACA,MAAAzS,MAAA2R,SAAAnC,GASAuD,GAAAL,OACA,MAAA1S,MAAA4R,aAAApD,GASAuE,GAAAJ,OACA,MAAA3S,MAAA4R,aAAApC,GAGAuD,GAAAvE,GAAA1Q,GACAkC,KAAAmQ,SAAA3B,EAAA1Q,GAEAiV,GAAAvD,GAAA1R,GACAkC,KAAAmQ,SAAAX,EAAA1R,GAEAiV,GAAAP,IAAA1U,GACAkC,KAAA2R,SAAAnD,EAAA1Q,GAEAiV,GAAAN,IAAA3U,GACAkC,KAAA2R,SAAAnC,EAAA1R,GAEAiV,GAAAL,KAAA5U,GACAkC,KAAA4R,aAAApD,EAAA1Q,GAEAiV,GAAAJ,KAAA7U,GACAkC,KAAA4R,aAAApC,EAAA1R,GAWAkV,aAAA,SAAAtD,GAEA,GAAAlB,GAAAkB,EAAAlB,IAAAvP,EAAAyQ,EAAAlB,EAAAkB,EAAAS,SAAA3B,EACAgB,EAAAE,EAAAF,IAAAvQ,EAAAyQ,EAAAF,EAAAE,EAAAS,SAAAX,CAEA,OAAAxP,MAAAmQ,SAAA3B,EAAAA,EAAAkB,EAAAvJ,OACAnG,KAAAmQ,SAAA3B,EAAAxO,KAAAmG,MAAAqI,GACAxO,KAAAmQ,SAAAX,EAAAA,EAAAE,EAAAtJ,QACApG,KAAAmQ,SAAAX,EAAAxP,KAAAoG,OAAAoJ,GACA,GAGA,GAuBArI,OAAA,SAAAyH,GACA5O,KAAA4S,QAAAhE,IAqBAxH,OAAA,WACApH,KAAA6S,SAIA9R,GACAA,WAAA+N,MChbA/N,OAAA,SAAAA,EAAA9B,GACA,YAkTA,OA1SA8B,GAAAkS,UAAA,SAAAxN,GACA,GAAAwN,GAAA5N,OAAA2B,OAAAjG,EAAAkS,UAAAhM,UAGA,OAFAgM,GAAA/L,IAAAzB,GAEAwN,GAGAlS,EAAAkS,UAAAhM,WAUAC,IAAA,SAAAzB,GACAA,EAAAA,MAEAzF,KAAAkT,YAAAzN,EAAAyN,YACAlT,KAAAmT,OAAA1N,EAAA0N,OACAnT,KAAAoT,WAAA3N,EAAA2N,WAEApT,KAAAmG,MAAAV,EAAAyN,YAAAzL,MAAAtB,MACAnG,KAAAoG,OAAAX,EAAAyN,YAAAzL,MAAArB,OAEApG,KAAAqT,aAAA,EACArT,KAAAsH,aAAA,EACAtH,KAAAmH,OAAAnH,KAAA4S,QACA5S,KAAAoH,OAAApH,KAAA6S,MAUAD,QAAA,SAAAhE,GAOA,IALAA,GAAA,EAAAA,EAAA,IAAAA,EAAAA,IAAA,EAEA5O,KAAAsH,cAAAsH,EAGA5O,KAAAsH,cAAAtH,KAAAoT,YACApT,KAAAqT,eAAArT,KAAAqT,aAAArT,KAAAmT,OAAAvV,OAEAoC,KAAAsH,cAAAtH,KAAAoT,YAcAP,KAAA,SAAApN,GACAA,EAAAA,KAEA,IAAAnI,GAAAmI,EAAAnI,SAAAyD,EAAAzD,QAGAgW,EAAAtT,KAAAmT,OAAAnT,KAAAqT,cAAArT,KAAAkT,YAAAK,aAAA,EACAC,EAAAxT,KAAAmT,OAAAnT,KAAAqT,cAAArT,KAAAkT,YAAAK,aAAA,CAEAjW,GAAA4U,UACAlS,KAAAkT,YAAA9P,MACAoQ,EAAAxT,KAAAkT,YAAAzL,MAAAtB,MAAAmN,EAAAtT,KAAAkT,YAAAzL,MAAArB,OACApG,KAAAkT,YAAAzL,MAAAtB,MAAAnG,KAAAkT,YAAAzL,MAAArB,OACAX,EAAA+I,EAAA/I,EAAA+J,EACAxP,KAAAkT,YAAAzL,MAAAtB,MAAAnG,KAAAkT,YAAAzL,MAAArB,SAQAqN,KAAA,WAEAzT,KAAAmH,OAAAnH,KAAA4S,QACA5S,KAAAoH,OAAApH,KAAA6S,MAOA3K,KAAA,WAMAlI,KAAAmH,OAAApG,EAAAuF,KACAtG,KAAAoH,OAAArG,EAAAuF,MAOAoN,MAAA,WACA1T,KAAAmH,OAAApG,EAAAuF,OAeAvF,EAAAmS,YAAA,SAAAzN,GACA,GAAAyN,GAAA7N,OAAA2B,OAAAjG,EAAAmS,YAAAjM,UAGA,OAFAiM,GAAAhM,IAAAzB,GAEAyN,GAGAnS,EAAAmS,YAAAjM,WAYAC,IAAA,SAAAzB,GAKA,GAJAA,EAAAA,MAEAzF,KAAAuS,eAEAxR,EAAAC,QAAAyE,EAAArC,SAAArC,EAAA8E,SAAAJ,EAAArC,OASA,CACA,GAAA7C,GAAA,GAAAkN,aAAA,iBAEA,YADA1M,GAAAiF,SAAAzF,EAAA,kDAVAP,KAAAoD,MAAAqC,EAAArC,MACApD,KAAAyH,OACAtB,MAAAV,EAAAkO,WACAvN,OAAAX,EAAAmO,aAGA5T,KAAAuT,aAAA9N,EAAArC,MAAA+C,MAAAV,EAAAkO,WAAA,EAQAlO,EAAA8M,YACAvS,KAAA6T,iBAAApO,EAAA8M,aAoCAsB,iBAAA,SAAAtB,GACA,GAAAhS,EAEA,KAAAgS,GAAA,IAAAlN,OAAAC,KAAAiN,GAAA3U,OAGA,MAFA2C,GAAA,GAAAwF,gBAAA,4BACAhF,GAAAiF,SAAAzF,EAAA,wEAKA,IAAA0S,GAAAE,EAAAC,EAAAU,CACA,KAAA,GAAA3Q,KAAAoP,GACA,GAAAA,EAAA9U,eAAA0F,GAAA,CAWA,GAPA8P,EAAAV,EAAApP,GACAgQ,EAAAF,EAAAE,OACAC,EAAAH,EAAAG,WAGAU,KAEAX,IAAAlU,EAGA,MAFAsB,GAAA,GAAAwF,gBAAA,kCACAhF,GAAAiF,SAAAzF,EAAA,aAAA4C,EAAA,mCAKA,IAAApC,EAAAwF,SAAA4M,GACAW,EAAAhU,KAAAqT,OAGA,IAAApS,EAAA2E,SAAAyN,GACAW,EAAA9T,KAAA+T,aAAAZ,OAGA,IAAApS,EAAApD,QAAAwV,GACA,IAAA,GAAA1L,GAAAtI,EAAA,EAAAsI,EAAA0L,EAAAhU,GAAAA,IAGA4B,EAAA2E,SAAA+B,GAGAqM,EAAAhU,KAAAiF,MAAA+O,EAAA9T,KAAA+T,aAAAtM,IAIAqM,EAAAhU,KAAA2H,EAKAzH,MAAAuS,WAAApP,GAAApC,EAAAkS,WACAC,YAAAlT,KACAmT,OAAAW,EACAV,WAAAA,MAcAW,aAAA,SAAAZ,GACA,GAKAhU,GALA2U,KACAE,EAAAb,EAAAhK,MAAA,MAAA8K,IAAAC,QAGAC,EAAAH,EAAA,GAAAA,EAAA,GAAA,EAAA,EAIA,IAAA,IAAAG,EACA,IAAAhV,EAAA6U,EAAA,GAAA7U,GAAA6U,EAAA,GAAA7U,IACA2U,EAAAhU,KAAAX,OAKA,KAAAA,EAAA6U,EAAA,GAAA7U,GAAA6U,EAAA,GAAA7U,IACA2U,EAAAhU,KAAAX,EAIA,OAAA2U,KAIA/S,GACAA,YC3SAA,OAAA,SAAAA,EAAAL,EAAA0T,EAAAnV,GACA,YAMA,OAHA8B,GAAAS,OAAAT,EAAAS,WACAT,EAAAS,OAAA4S,aAAA,gBAAA1T,IAAA,OAAAA,EAAA0T,aAEArT,EAAAS,OAAA4S,cAOArT,EAAAsT,SASAtT,EAAAsT,MAAAnN,IAAA,SAAA3J,EAAAO,GACAA,IAAAmB,EACAe,KAAAsU,OAAA/W,GAGA6W,EAAAG,QAAAhX,EAAA+G,KAAAkQ,UAAA1W,KAYAiD,EAAAsT,MAAA9F,IAAA,SAAAhR,GACA,GAAAO,GAAAsW,EAAAK,QAAAlX,EAEA,KACAO,EAAAwG,KAAAC,MAAAzG,GAEA,MAAA6B,IAEA,MAAA7B,IASAiD,EAAAsT,MAAAC,OAAA,SAAA/W,GACA6W,EAAAM,WAAAnX,IAOAwD,EAAAsT,MAAA1F,MAAA,WACAyF,EAAAzF,SAGA5N,GA7DAA,GA8DAA,WAAAL,OAAAA,OAAA0T,cC/EArT,OAAA,SAAAA,EAAA+N,EAAA7P,GACA,YAkaA,OA1ZA8B,GAAA4T,WAAA,SAAAlP,GACA,GAAAkP,GAAAtP,OAAA2B,OAAAjG,EAAA4T,WAAA1N,UAGA,OAFA0N,GAAAzN,IAAAzB,GAEAkP,GAGA5T,EAAA4T,WAAA1N,WAgBAC,IAAA,SAAAzB,GACAA,EAAAA,KAEA,IAAAiC,GAAA1H,IAGA,KAAAyF,EAAAU,QAAAV,EAAAW,OAAA,CACA,GAAA7F,GAAA,GAAAwF,gBAAA,gCAEA,YADAhF,GAAAiF,SAAAzF,EAAA,yEAIAmH,EAAAvB,MAAAV,EAAAU,MACAuB,EAAAtB,OAAAX,EAAAW,OAIAsB,EAAAkN,UAAAnP,EAAAmP,WAAA,GACAlN,EAAAmN,WAAApP,EAAAoP,YAAA,GAEAnN,EAAApK,QAAAmI,EAAAnI,SAAAyD,EAAAzD,QAEAoK,EAAAoN,YAAApN,EAAApK,QAAAqI,OAAAQ,MACAuB,EAAAqN,aAAArN,EAAApK,QAAAqI,OAAAS,OAIAsB,EAAAsN,iBAAAzP,SAAA0P,cAAA,UACAvN,EAAAwN,kBAAAxN,EAAAsN,iBAAA/O,WAAA,MAGAyB,EAAAsN,iBAAA7O,MAAAuB,EAAAyN,SAAAzN,EAAAvB,MAAAuB,EAAAkN,UACAlN,EAAAsN,iBAAA5O,OAAAsB,EAAA0N,UAAA1N,EAAAtB,OAAAsB,EAAAmN,WAKAnN,EAAA2N,MAAA3N,EAAAyN,SAAAzN,EAAAoN,YACApN,EAAA4N,MAAA5N,EAAA0N,UAAA1N,EAAAqN,aAEArN,EAAA6N,UAGA7N,EAAA8N,eAGA9N,EAAA+N,YAEA/N,EAAA8G,EAAA/I,EAAA+I,GAAA,EACA9G,EAAA8H,EAAA/J,EAAA+J,GAAA,EACA9H,EAAAgO,GAAAjQ,EAAAiQ,IAAA,EACAhO,EAAAiO,GAAAlQ,EAAAkQ,IAAA,GAWAC,WAAA,SAAAnQ,GAGA,GAFAA,EAAAA,OAEA1E,EAAAC,QAAAyE,EAAArC,SAAArC,EAAA8E,SAAAJ,EAAArC,OA+BA,CACA,GAAA7C,GAAA,GAAAkN,aAAA,iBAEA,YADA1M,GAAAiF,SAAAzF,EAAA,kDAhCA,GAAA6C,GAAAqC,EAAArC,MACAyS,EAAApQ,EAAAoQ,UACAC,GAAA1S,EAAA+C,MAAAnG,KAAA4U,UAAA,IAAAxR,EAAAgD,OAAApG,KAAA6U,WAAA,EAEA,KAAAgB,EAEA,GAAA7V,KAAAyV,SAAA7X,OAAA,EAAA,CACA,GAAAmY,GAAA/V,KAAAyV,SAAAzV,KAAAyV,SAAA7X,OAAA,GACAoY,GAAAD,EAAA3S,MAAA+C,MAAAnG,KAAA4U,UAAA,IACAmB,EAAA3S,MAAAgD,OAAApG,KAAA6U,WAAA,EAEAgB,GAAAE,EAAAF,UAAAG,EAAA,MAIAH,GAAA,CAIA7V,MAAAyV,SAAA3V,MACA+V,UAAAA,EACAI,SAAAJ,EAAAC,EAAA,EACA1S,MAAAA,IAIApD,KAAAyV,SAAAS,KAAA,SAAAC,EAAAC,GACA,MAAAD,GAAAN,UAAAO,EAAAP,aAoBAQ,SAAA,SAAA5Q,GACAA,EAAAA,MACAA,EAAA2B,OAAA3B,EAAA2B,SAAAnI,GAAA,EAAAwG,EAAA2B,MAEA,IACAhG,GADAsG,EAAA1H,IAIA,IAAAe,EAAApD,QAAA8H,EAAArE,KAAA,IAAA,CACAA,IAEA,KAAA,GAAAkS,GAAAgD,EAAA,EAAAhD,EAAA7N,EAAArE,KAAAkV,GAAAA,IACA,IAAA,GAAAC,GAAA,EAAA3Y,EAAA0V,EAAA1V,OAAAA,EAAA2Y,EAAAA,IACAnV,EAAAtB,KAAAwT,EAAAiD,QAKAnV,GAAAqE,EAAArE,IAGApB,MAAAuV,OAAA9P,EAAAtC,MAAA/B,EACApB,KAAAuV,OAAA9P,EAAAtC,MAAAqT,OAAA/Q,EAAA+Q,QAAA,EACAxW,KAAAuV,OAAA9P,EAAAtC,MAAAiE,OAAA3B,EAAA2B,OAGA3B,EAAA2B,SACApH,KAAAwV,YAAA1V,KAAA2F,EAAAtC,MAEAnD,KAAAwV,YAAAU,KAAA,SAAAC,EAAAC,GACA,MAAA1O,GAAA6N,OAAAY,GAAAK,OAAA9O,EAAA6N,OAAAa,GAAAI,SAGAxW,KAAAyW,oBAiBAC,kBAAA,SAAAvT,EAAAuM,GAcA,IAAA,GADAb,GAXAL,EAAAkB,EAAAlB,IAAAvP,EAAAyQ,EAAAlB,EAAAkB,EAAAS,SAAA3B,EACAgB,EAAAE,EAAAF,IAAAvQ,EAAAyQ,EAAAF,EAAAE,EAAAS,SAAAX,EAGA8D,EAAAtT,KAAA2W,QAAAnH,GACAgE,EAAAxT,KAAA4W,QAAApI,GAEAqI,EAAA7W,KAAA2W,QAAAnH,EAAAE,EAAAtJ,QACA0Q,EAAA9W,KAAA4W,QAAApI,EAAAkB,EAAAvJ,OAIAmQ,EAAAhD,EAAAuD,GAAAP,EAAAA,IACA,IAAA,GAAAC,GAAA/C,EAAAsD,GAAAP,EAAAA,IAGA,GAFA1H,EAAA0H,EAAAD,EAAAtW,KAAAmG,MAEAnG,KAAAuV,OAAApS,GAAA0L,GACA,OAAA,CAKA,QAAA,GAaAkI,YAAA,SAAA5T,EAAAqL,EAAAgB,GACA,GAAA8D,GAAAtT,KAAA2W,QAAAnH,GACAgE,EAAAxT,KAAA4W,QAAApI,GACAK,EAAA2E,EAAAF,EAAAtT,KAAAmG,KAEA,OAAAnG,MAAAuV,OAAApS,GAAA0L,IAOAzH,OAAA,WACA,GAAAM,GAAA1H,IAGA0H,GAAAgO,GAAA5G,EAAA0C,IAAA1C,EAAAC,IAAArH,EAAAgO,GAAA,GAAAhO,EAAA2N,OACA3N,EAAAiO,GAAA7G,EAAA0C,IAAA1C,EAAAC,IAAArH,EAAAiO,GAAA,GAAAjO,EAAA4N,OAEA5N,EAAApK,QAAA4U,UACAxK,EAAAsN,iBACAtN,EAAAgO,GAAAhO,EAAAiO,GAAAjO,EAAAoN,YAAApN,EAAAqN,aACArN,EAAA8G,EAAA9G,EAAA8H,EAAA9H,EAAAoN,YAAApN,EAAAqN,eAUAiC,YAAA,SAAA7T,GAuBA,IAtBA,GAmBAqL,GAAAgB,EAAAyH,EAAAC,EAAA9T,EAAA+T,EAAAhR,EAAAuP,EAAAC,EAnBAjO,EAAA1H,KAEAoX,EAAA1P,EAAA6N,OAAApS,GAGAmQ,EAAA5L,EAAAiP,UACAnD,EAAA9L,EAAAkP,UACA/H,EAAA2E,EAAAF,EAAA5L,EAAAvB,MAGAkR,EAAA7D,EAAA9L,EAAAkN,UAAAlN,EAAAgO,GACA4B,EAAAhE,EAAA5L,EAAAmN,WAAAnN,EAAAiO,GAGA4B,EAAAzI,EAAA0I,KAAA9P,EAAAoN,YAAApN,EAAAkN,WAAA,EACA6C,EAAA3I,EAAA0I,KAAA9P,EAAAqN,aAAArN,EAAAmN,YAAA,EACAiB,EAAAyB,EAAAE,EAEAC,EAAA,EAIA5B,EAAA4B,GACAT,EAAAG,EAAAvI,GAEAoI,IACAC,EAAAxP,EAAAiQ,YAAAV,GACA7T,EAAA8T,EAAA9T,MAEAoL,EAAA6I,EAAAK,EAAAH,EAAA7P,EAAAkN,UACApF,EAAA8H,GAAAI,EAAAH,EAAA,GAAA7P,EAAAmN,WAEAsC,EAAAF,EAAAC,EAAArB,UACA1P,EAAA/C,EAAA+C,MAAAuB,EAAAkN,UAEAc,EAAAyB,EAAAhR,EAAAuB,EAAAkN,UACAe,GAAAwB,EAAAhR,EAAA,GAAAuB,EAAAmN,WAEAnN,EAAApK,QAAA4U,UACA9O,EACAsS,EAAAC,EAAAjO,EAAAkN,UAAAlN,EAAAmN,WACArG,EAAAgB,EAAA9H,EAAAkN,UAAAlN,EAAAmN,eAIA6C,EAAAH,IAAA,EACA1I,EAAA2E,KAAAF,EAAA5L,EAAAvB,MAGA0I,KAcA8H,QAAA,SAAAnH,GAGA,MAFAA,GAAAA,GAAA,GAEAxP,KAAA2V,GAAAnG,GAAAxP,KAAA6U,WAAA,GAYA+B,QAAA,SAAApI,GAGA,MAFAA,GAAAA,GAAA,GAEAxO,KAAA0V,GAAAlH,GAAAxO,KAAA4U,UAAA,GAYA+C,YAAA,SAAAV,GAMA,IALA,GAEApI,GACA+I,EAHApG,EAAA,EACAzC,EAAA/O,KAAAyV,SAAA7X,OAAA,EAIAmR,GAAAyC,GAAA,CAIA,GAHA3C,GAAA2C,EAAAzC,GAAA,EAAA,EACA6I,EAAA5X,KAAAyV,SAAA5G,GAEAoI,GAAAW,EAAA/B,WAAAoB,GAAAW,EAAA3B,SACA,MAAA2B,EAEAX,GAAAW,EACApG,EAAA3C,EAAA,EAGAE,EAAAF,EAAA,IAQA4H,gBAAA,WAKA,IAAA,GAHAQ,GAAAC,EAAA9T,EAAAoL,EAAAgB,EAAAkG,EAAAC,EAAAwB,EAAAhR,EAGAiR,EAJA1P,EAAA1H,KAIAb,EAAA,EAAAiY,EAAA1P,EAAA6N,OAAA7N,EAAA8N,YAAArW,IAAAA,IACA,IAAA,GAAA6P,GAAA,EAAA6I,EAAAT,EAAAxZ,OAAAia,EAAA7I,EAAAA,IACAiI,EAAAG,EAAApI,GAGAiI,IAIAC,EAAAxP,EAAAiQ,YAAAV,GACA7T,EAAA8T,EAAA9T,MAEAoL,EAAAQ,EAAAtH,EAAAvB,MAAAuB,EAAAkN,UACApF,GAAAR,EAAAtH,EAAAvB,MAAA,GAAAuB,EAAAmN,WAEAsC,EAAAF,EAAAC,EAAArB,UACA1P,EAAA/C,EAAA+C,MAAAuB,EAAAkN,UAEAc,EAAAyB,EAAAhR,EAAAuB,EAAAkN,UACAe,GAAAwB,EAAAhR,EAAA,GAAAuB,EAAAmN,WAEAnN,EAAAwN,kBAAAhD,UACA9O,EACAsS,EAAAC,EAAAjO,EAAAkN,UAAAlN,EAAAmN,WACArG,EAAAgB,EAAA9H,EAAAkN,UAAAlN,EAAAmN,eAOA9T,GACAA,WAAA+N","file":"kontra.min.js","sourcesContent":["/**\n * The MIT License\n *\n * Copyright (c) 2010-2012 Google, Inc. http://angularjs.org\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\nwindow.q = qFactory(function(callback) {\n setTimeout(function() {\n callback();\n }, 0);\n}, function(e) {\n console.error('qLite: ' + e.stack);\n});\n\n/**\n * Constructs a promise manager.\n *\n * @param {function(Function)} nextTick Function for executing functions in the next turn.\n * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for\n * debugging purposes.\n * @returns {object} Promise manager.\n */\nfunction qFactory(nextTick, exceptionHandler) {\n var toString = ({}).toString;\n var isFunction = function isFunction(value){return typeof value == 'function';};\n var isArray = function isArray(value) {return toString.call(value) === '[object Array]';};\n\n function forEach(obj, iterator, context) {\n var key;\n if (obj) {\n if (isFunction(obj)) {\n for (key in obj) {\n // Need to check if hasOwnProperty exists,\n // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function\n if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {\n iterator.call(context, obj[key], key);\n }\n }\n } else if (obj.forEach && obj.forEach !== forEach) {\n obj.forEach(iterator, context);\n } else if (isArray(obj)) {\n for (key = 0; key < obj.length; key++)\n iterator.call(context, obj[key], key);\n } else {\n for (key in obj) {\n if (obj.hasOwnProperty(key)) {\n iterator.call(context, obj[key], key);\n }\n }\n }\n }\n return obj;\n }\n\n /**\n * @ngdoc method\n * @name $q#defer\n * @function\n *\n * @description\n * Creates a `Deferred` object which represents a task which will finish in the future.\n *\n * @returns {Deferred} Returns a new instance of deferred.\n */\n var defer = function() {\n var pending = [],\n value, deferred;\n\n deferred = {\n\n resolve: function(val) {\n if (pending) {\n var callbacks = pending;\n pending = undefined;\n value = ref(val);\n\n if (callbacks.length) {\n nextTick(function() {\n var callback;\n for (var i = 0, ii = callbacks.length; i < ii; i++) {\n callback = callbacks[i];\n value.then(callback[0], callback[1], callback[2]);\n }\n });\n }\n }\n },\n\n\n reject: function(reason) {\n deferred.resolve(createInternalRejectedPromise(reason));\n },\n\n\n notify: function(progress) {\n if (pending) {\n var callbacks = pending;\n\n if (pending.length) {\n nextTick(function() {\n var callback;\n for (var i = 0, ii = callbacks.length; i < ii; i++) {\n callback = callbacks[i];\n callback[2](progress);\n }\n });\n }\n }\n },\n\n\n promise: {\n then: function(callback, errback, progressback) {\n var result = defer();\n\n var wrappedCallback = function(value) {\n try {\n result.resolve((isFunction(callback) ? callback : defaultCallback)(value));\n } catch(e) {\n result.reject(e);\n exceptionHandler(e);\n }\n };\n\n var wrappedErrback = function(reason) {\n try {\n result.resolve((isFunction(errback) ? errback : defaultErrback)(reason));\n } catch(e) {\n result.reject(e);\n exceptionHandler(e);\n }\n };\n\n var wrappedProgressback = function(progress) {\n try {\n result.notify((isFunction(progressback) ? progressback : defaultCallback)(progress));\n } catch(e) {\n exceptionHandler(e);\n }\n };\n\n if (pending) {\n pending.push([wrappedCallback, wrappedErrback, wrappedProgressback]);\n } else {\n value.then(wrappedCallback, wrappedErrback, wrappedProgressback);\n }\n\n return result.promise;\n },\n\n \"catch\": function(callback) {\n return this.then(null, callback);\n },\n\n \"finally\": function(callback) {\n\n function makePromise(value, resolved) {\n var result = defer();\n if (resolved) {\n result.resolve(value);\n } else {\n result.reject(value);\n }\n return result.promise;\n }\n\n function handleCallback(value, isResolved) {\n var callbackOutput = null;\n try {\n callbackOutput = (callback ||defaultCallback)();\n } catch(e) {\n return makePromise(e, false);\n }\n if (callbackOutput && isFunction(callbackOutput.then)) {\n return callbackOutput.then(function() {\n return makePromise(value, isResolved);\n }, function(error) {\n return makePromise(error, false);\n });\n } else {\n return makePromise(value, isResolved);\n }\n }\n\n return this.then(function(value) {\n return handleCallback(value, true);\n }, function(error) {\n return handleCallback(error, false);\n });\n }\n }\n };\n\n return deferred;\n };\n\n\n var ref = function(value) {\n if (value && isFunction(value.then)) return value;\n return {\n then: function(callback) {\n var result = defer();\n nextTick(function() {\n result.resolve(callback(value));\n });\n return result.promise;\n }\n };\n };\n\n\n /**\n * @ngdoc method\n * @name $q#reject\n * @function\n *\n * @description\n * Creates a promise that is resolved as rejected with the specified `reason`. This api should be\n * used to forward rejection in a chain of promises. If you are dealing with the last promise in\n * a promise chain, you don't need to worry about it.\n *\n * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of\n * `reject` as the `throw` keyword in JavaScript. This also means that if you \"catch\" an error via\n * a promise error callback and you want to forward the error to the promise derived from the\n * current promise, you have to \"rethrow\" the error by returning a rejection constructed via\n * `reject`.\n *\n * ```js\n * promiseB = promiseA.then(function(result) {\n * // success: do something and resolve promiseB\n * // with the old or a new result\n * return result;\n * }, function(reason) {\n * // error: handle the error if possible and\n * // resolve promiseB with newPromiseOrValue,\n * // otherwise forward the rejection to promiseB\n * if (canHandle(reason)) {\n * // handle the error and recover\n * return newPromiseOrValue;\n * }\n * return $q.reject(reason);\n * });\n * ```\n *\n * @param {*} reason Constant, message, exception or an object representing the rejection reason.\n * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.\n */\n var reject = function(reason) {\n var result = defer();\n result.reject(reason);\n return result.promise;\n };\n\n var createInternalRejectedPromise = function(reason) {\n return {\n then: function(callback, errback) {\n var result = defer();\n nextTick(function() {\n try {\n result.resolve((isFunction(errback) ? errback : defaultErrback)(reason));\n } catch(e) {\n result.reject(e);\n exceptionHandler(e);\n }\n });\n return result.promise;\n }\n };\n };\n\n\n /**\n * @ngdoc method\n * @name $q#when\n * @function\n *\n * @description\n * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.\n * This is useful when you are dealing with an object that might or might not be a promise, or if\n * the promise comes from a source that can't be trusted.\n *\n * @param {*} value Value or a promise\n * @returns {Promise} Returns a promise of the passed value or promise\n */\n var when = function(value, callback, errback, progressback) {\n var result = defer(),\n done;\n\n var wrappedCallback = function(value) {\n try {\n return (isFunction(callback) ? callback : defaultCallback)(value);\n } catch (e) {\n exceptionHandler(e);\n return reject(e);\n }\n };\n\n var wrappedErrback = function(reason) {\n try {\n return (isFunction(errback) ? errback : defaultErrback)(reason);\n } catch (e) {\n exceptionHandler(e);\n return reject(e);\n }\n };\n\n var wrappedProgressback = function(progress) {\n try {\n return (isFunction(progressback) ? progressback : defaultCallback)(progress);\n } catch (e) {\n exceptionHandler(e);\n }\n };\n\n nextTick(function() {\n ref(value).then(function(value) {\n if (done) return;\n done = true;\n result.resolve(ref(value).then(wrappedCallback, wrappedErrback, wrappedProgressback));\n }, function(reason) {\n if (done) return;\n done = true;\n result.resolve(wrappedErrback(reason));\n }, function(progress) {\n if (done) return;\n result.notify(wrappedProgressback(progress));\n });\n });\n\n return result.promise;\n };\n\n\n function defaultCallback(value) {\n return value;\n }\n\n\n function defaultErrback(reason) {\n return reject(reason);\n }\n\n\n /**\n * @ngdoc method\n * @name $q#all\n * @function\n *\n * @description\n * Combines multiple promises into a single promise that is resolved when all of the input\n * promises are resolved.\n *\n * @param {Array.|Object.} promises An array or hash of promises.\n * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,\n * each value corresponding to the promise at the same index/key in the `promises` array/hash.\n * If any of the promises is resolved with a rejection, this resulting promise will be rejected\n * with the same rejection value.\n */\n function all(promises) {\n var deferred = defer(),\n counter = 0,\n results = isArray(promises) ? [] : {};\n\n forEach(promises, function(promise, key) {\n counter++;\n ref(promise).then(function(value) {\n if (results.hasOwnProperty(key)) return;\n results[key] = value;\n if (!(--counter)) deferred.resolve(results);\n }, function(reason) {\n if (results.hasOwnProperty(key)) return;\n deferred.reject(reason);\n }, function(reason) {\n if (results.hasOwnProperty(key)) return;\n deferred.notify(reason);\n });\n });\n\n if (counter === 0) {\n deferred.resolve(results);\n }\n\n return deferred.promise;\n }\n\n return {\n defer: defer,\n reject: reject,\n when: when,\n all: all\n };\n}\nvar kontra = (function(kontra) {\n var isImage = /(jpeg|jpg|gif|png)$/;\n var isAudio = /(wav|mp3|ogg|aac|m4a)$/;\n var folderSeparator = /(\\\\|\\/)/g;\n\n // all assets are stored by name as well as by URL\n kontra.images = {};\n kontra.audios = {};\n kontra.data = {};\n\n // base asset path for determining asset URLs\n kontra.assetPaths = {\n images: '',\n audios: '',\n data: '',\n };\n\n // audio playability\n // @see https://github.com/Modernizr/Modernizr/blob/master/feature-detects/audio.js\n var audio = new Audio();\n kontra.canUse = kontra.canUse || {};\n kontra.canUse.wav = '';\n kontra.canUse.mp3 = audio.canPlayType('audio/mpeg;').replace(/^no$/,'');\n kontra.canUse.ogg = audio.canPlayType('audio/ogg; codecs=\"vorbis\"').replace(/^no$/,'');\n kontra.canUse.aac = audio.canPlayType('audio/aac;').replace(/^no$/,'');\n kontra.canUse.m4a = (audio.canPlayType('audio/x-m4a;') || kontra.canUse.aac).replace(/^no$/,'');\n\n /**\n * Get the extension of an asset.\n * @see http://jsperf.com/extract-file-extension\n * @memberOf kontra\n *\n * @param {string} url - The URL to the asset.\n *\n * @returns {string}\n */\n kontra.getAssetExtension = function getAssetExtension(url) {\n return url.substr((~-url.lastIndexOf(\".\") >>> 0) + 2);\n };\n\n /**\n * Get the type of asset based on its extension.\n * @memberOf kontra\n *\n * @param {string} url - The URL to the asset.\n *\n * @returns {string} Image, Audio, Data.\n */\n kontra.getAssetType = function getAssetType(url) {\n var extension = this.getAssetExtension(url);\n\n if (extension.match(isImage)) {\n return 'Image';\n }\n else if (extension.match(isAudio)) {\n return 'Audio';\n }\n else {\n return 'Data';\n }\n };\n\n /**\n * Get the name of an asset.\n * @memberOf kontra\n *\n * @param {string} url - The URL to the asset.\n *\n * @returns {string}\n */\n kontra.getAssetName = function getAssetName(url) {\n return url.replace(/\\.[^/.]+$/, \"\");\n };\n\n return kontra;\n})(kontra || {});\n/*jshint -W084 */\n\nvar kontra = (function(kontra, q) {\n /**\n * Load an Image, Audio, or data file.\n * @memberOf kontra\n *\n * @param {string|string[]} - Comma separated list of assets to load.\n *\n * @returns {Promise} A deferred promise.\n *\n * @example\n * kontra.loadAsset('car.png');\n * kontra.loadAsset(['explosion.mp3', 'explosion.ogg']);\n * kontra.loadAsset('bio.json');\n * kontra.loadAsset('car.png', ['explosion.mp3', 'explosion.ogg'], 'bio.json');\n */\n kontra.loadAssets = function loadAsset() {\n var deferred = q.defer();\n var promises = [];\n var numLoaded = 0;\n var numAssets = arguments.length;\n var type, name, url;\n\n if (!arguments.length) {\n deferred.resolve();\n }\n\n for (var i = 0, asset; asset = arguments[i]; i++) {\n if (!Array.isArray(asset)) {\n url = asset;\n }\n else {\n url = asset[0];\n }\n\n type = this.getAssetType(url);\n\n // create a closure for event binding\n (function(assetDeferred) {\n promises.push(assetDeferred.promise);\n\n kontra['load' + type](url).then(\n function loadAssetSuccess() {\n assetDeferred.resolve();\n deferred.notify({'loaded': ++numLoaded, 'total': numAssets});\n },\n function loadAssetError(error) {\n assetDeferred.reject(error);\n });\n })(q.defer());\n }\n\n q.all(promises).then(\n function loadAssetsSuccess() {\n deferred.resolve();\n },\n function loadAssetsError(error) {\n deferred.reject(error);\n });\n\n return deferred.promise;\n };\n\n /**\n * Load an Image file. Uses assetPaths.images to resolve URL.\n * @memberOf kontra\n *\n * @param {string} url - The URL to the Image file.\n *\n * @returns {Promise} A deferred promise. Promise resolves with the Image.\n *\n * @example\n * kontra.loadImage('car.png');\n * kontra.loadImage('autobots/truck.png');\n */\n kontra.loadImage = function(url) {\n var deferred = q.defer();\n var name = this.getAssetName(url);\n var image = new Image();\n\n url = this.assetPaths.images + url;\n\n image.onload = function loadImageOnLoad() {\n kontra.images[name] = kontra.images[url] = this;\n deferred.resolve(this);\n };\n\n image.onerror = function loadImageOnError() {\n deferred.reject('Unable to load image ' + url);\n };\n\n image.src = url;\n\n return deferred.promise;\n };\n\n /**\n * Load an Audio file. Supports loading multiple audio formats which will be resolved by\n * the browser in the order listed. Uses assetPaths.audios to resolve URL.\n * @memberOf kontra\n *\n * @param {string|string[]} url - The URL to the Audio file.\n *\n * @returns {Promise} A deferred promise. Promise resolves with the Audio.\n *\n * @example\n * kontra.loadAudio('sound_effects/laser.mp3');\n * kontra.loadAudio(['explosion.mp3', 'explosion.m4a', 'explosion.ogg']);\n *\n * There are two ways to load Audio in the web: HTML5 Audio or the Web Audio API.\n * HTML5 Audio has amazing browser support, including back to IE9\n * (http://caniuse.com/#feat=audio). However, the web Audio API isn't supported in\n * IE nor Android Browsers (http://caniuse.com/#search=Web%20Audio%20API).\n *\n * To support the most browsers we'll use HTML5 Audio. However, doing so means we'll\n * have to work around mobile device limitations as well as Audio implementation\n * limitations.\n *\n * Android browsers require playing Audio through user interaction whereas iOS 6+ can\n * play through normal JavaScript. Moreover, Android can only play one sound source at\n * a time whereas iOS 6+ can handle more than one. See this article for more details\n * (http://pupunzi.open-lab.com/2013/03/13/making-html5-audio-actually-work-on-mobile/)\n *\n * Both iOS and Android will download an Audio through JavaScript, but neither will play\n * it until user interaction. You can get around this issue by having a splash screen\n * that requires user interaction to start the game and using that event to play the audio.\n * (http://jsfiddle.net/straker/5dsm6jgt/)\n */\n kontra.loadAudio = function(url) {\n var deferred = q.defer();\n var source, name, playableSource, audio;\n\n if (!Array.isArray(url)) {\n url = [url];\n }\n\n // determine which audio format the browser can play\n for (var i = 0; source = url[i]; i++) {\n if ( this.canUse[this.getAssetExtension(source)] ) {\n playableSource = source;\n break;\n }\n }\n\n if (!playableSource) {\n deferred.reject('Browser cannot play any of the audio formats provided');\n }\n else {\n name = this.getAssetName(playableSource);\n audio = new Audio();\n\n source = this.assetPaths.audios + playableSource;\n\n audio.addEventListener('canplay', function loadAudioOnLoad() {\n kontra.audios[name] = kontra.audios[source] = this;\n deferred.resolve(this);\n });\n\n audio.onerror = function loadAudioOnError() {\n deferred.reject('Unable to load audio ' + source);\n };\n\n audio.src = source;\n audio.preload = 'auto';\n audio.load();\n }\n\n return deferred.promise;\n };\n\n\n /**\n * Load a data file (be it text or JSON). Uses assetPaths.data to resolve URL.\n * @memberOf kontra\n *\n * @param {string} url - The URL to the data file.\n *\n * @returns {Promise} A deferred promise. Resolves with the data or parsed JSON.\n *\n * @example\n * kontra.loadData('bio.json');\n * kontra.loadData('dialog.txt');\n */\n kontra.loadData = function(url) {\n var deferred = q.defer();\n var req = new XMLHttpRequest();\n var name = this.getAssetName(url);\n var dataUrl = this.assetPaths.data + url;\n\n req.addEventListener('load', function loadDataOnLoad() {\n if (req.status !== 200) {\n deferred.reject(req.responseText);\n return;\n }\n\n try {\n var json = JSON.parse(req.responseText);\n kontra.data[name] = kontra.data[dataUrl] = json;\n\n deferred.resolve(json);\n }\n catch(e) {\n var data = req.responseText;\n kontra.data[name] = kontra.data[dataUrl] = data;\n\n deferred.resolve(data);\n }\n });\n\n req.open('GET', dataUrl, true);\n req.send();\n\n return deferred.promise;\n };\n\n return kontra;\n})(kontra || {}, q);\n/*jshint -W084 */\n\nvar kontra = (function(kontra, q) {\n kontra.bundles = {};\n\n /**\n * Create a group of assets that can be loaded using kontra.loadBundle().\n * @memberOf kontra\n *\n * @param {string} bundle - The name of the bundle.\n * @param {string[]} assets - Assets to add to the bundle.\n *\n * @example\n * kontra.createBundle('myBundle', ['car.png', ['explosion.mp3', 'explosion.ogg']]);\n */\n kontra.createBundle = function createBundle(bundle, assets) {\n if (this.bundles[bundle]) {\n return;\n }\n\n this.bundles[bundle] = assets || [];\n };\n\n /**\n * Load all assets that are part of a bundle.\n * @memberOf kontra\n *\n * @param {string|string[]} - Comma separated list of bundles to load.\n *\n * @returns {Promise} A deferred promise.\n *\n * @example\n * kontra.loadBundles('myBundle');\n * kontra.loadBundles('myBundle', 'myOtherBundle');\n */\n kontra.loadBundles = function loadBundles() {\n var deferred = q.defer();\n var promises = [];\n var numLoaded = 0;\n var numAssets = 0;\n var assets;\n\n for (var i = 0, bundle; bundle = arguments[i]; i++) {\n if (!(assets = this.bundles[bundle])) {\n deferred.reject('Bundle \\'' + bundle + '\\' has not been created.');\n continue;\n }\n\n numAssets += assets.length;\n\n promises.push(this.loadAssets.apply(this, assets));\n }\n\n q.all(promises).then(\n function loadBundlesSuccess() {\n deferred.resolve();\n },\n function loadBundlesError(error) {\n deferred.reject(error);\n },\n function loadBundlesNofity() {\n deferred.notify({'loaded': ++numLoaded, 'total': numAssets});\n });\n\n return deferred.promise;\n };\n\n return kontra;\n})(kontra || {}, q);\n/*jshint -W084 */\n\nvar kontra = (function(kontra, q) {\n /**\n * Load an asset manifest file.\n * @memberOf kontra\n *\n * @param {string} url - The URL to the asset manifest file.\n *\n * @returns {Promise} A deferred promise.\n */\n kontra.loadManifest = function loadManifest(url) {\n var deferred = q.defer();\n var bundles;\n\n kontra.loadData(url).then(\n function loadManifestSuccess(manifest) {\n kontra.assetPaths.images = manifest.imagePath || '';\n kontra.assetPaths.audios = manifest.audioPath || '';\n kontra.assetPaths.data = manifest.dataPath || '';\n\n // create bundles and add assets\n for (var i = 0, bundle; bundle = manifest.bundles[i]; i++) {\n kontra.createBundle(bundle.name, bundle.assets);\n }\n\n if (!manifest.loadBundles) {\n deferred.resolve();\n return;\n }\n\n // load all bundles\n if (manifest.loadBundles === 'all') {\n bundles = Object.keys(kontra.bundles || {});\n }\n // load a single bundle\n else if (!Array.isArray(manifest.loadBundles)) {\n bundles = [manifest.loadBundles];\n }\n // load multiple bundles\n else {\n bundles = manifest.loadBundles;\n }\n\n kontra.loadBundles.apply(kontra, bundles).then(\n function loadBundlesSuccess() {\n deferred.resolve();\n },\n function loadBundlesError(error) {\n deferred.reject(error);\n },\n function loadBundlesNotify(progress) {\n deferred.notify(progress);\n });\n },\n function loadManifestError(error) {\n deferred.reject(error);\n });\n\n return deferred.promise;\n };\n\n return kontra;\n})(kontra || {}, q);","/* global console */\n\nvar kontra = (function(kontra, document) {\n 'use strict';\n\n /**\n * Set up the canvas.\n * @memberof kontra\n *\n * @param {object} properties - Properties for the game.\n * @param {string|Canvas} properties.canvas - Main canvas ID or Element for the game.\n */\n kontra.init = function init(properties) {\n properties = properties || {};\n\n if (kontra.isString(properties.canvas)) {\n this.canvas = document.getElementById(properties.canvas);\n }\n else if (kontra.isCanvas(properties.canvas)) {\n this.canvas = properties.canvas;\n }\n else {\n this.canvas = document.getElementsByTagName('canvas')[0];\n\n if (!this.canvas) {\n var error = new ReferenceError('No canvas element found.');\n kontra.logError(error, 'You must provide a canvas element for the game.');\n return;\n }\n }\n\n this.context = this.canvas.getContext('2d');\n this.game = {\n width: this.canvas.width,\n height: this.canvas.height\n };\n };\n\n /**\n * Throw an error message to the user with readable formating.\n * @memberof kontra\n *\n * @param {Error} error - Error object.\n * @param {string} message - Error message.\n */\n kontra.logError = function logError(error, message) {\n console.error('Kontra: ' + message + '\\n\\t' + error.stack);\n };\n\n /**\n * Noop function.\n * @memberof kontra\n */\n kontra.noop = function noop() {};\n\n /**\n * Determine if a value is an Array.\n * @memberof kontra\n *\n * @param {*} value - Value to test.\n *\n * @returns {boolean}\n */\n kontra.isArray = Array.isArray;\n\n /**\n * Determine if a value is a String.\n * @memberof kontra\n *\n * @param {*} value - Value to test.\n *\n * @returns {boolean}\n */\n kontra.isString = function isString(value) {\n return typeof value === 'string';\n };\n\n /**\n * Determine if a value is a Number.\n * @memberof kontra\n *\n * @param {*} value - Value to test.\n *\n * @returns {boolean}\n */\n kontra.isNumber = function isNumber(value) {\n return typeof value === 'number';\n };\n\n /**\n * Determine if a value is an Image.\n * @memberof kontra\n *\n * @param {*} value - Value to test.\n *\n * @returns {boolean}\n */\n kontra.isImage = function isImage(value) {\n return value instanceof HTMLImageElement;\n };\n\n /**\n * Determine if a value is a Canvas.\n * @memberof kontra\n *\n * @param {*} value - Value to test.\n *\n * @returns {boolean}\n */\n kontra.isCanvas = function isCanvas(value) {\n return value instanceof HTMLCanvasElement;\n };\n\n return kontra;\n})(kontra || {}, document);","var kontra = (function(kontra, window) {\n 'use strict';\n\n /**\n * Get the current time. Uses the User Timing API if it's available or defaults to using\n * Date().getTime()\n * @private\n *\n * @returns {number}\n */\n kontra.timestamp = (function() {\n if (window.performance && window.performance.now) {\n return function timestampPerformance() {\n return window.performance.now();\n };\n }\n else {\n return function timestampDate() {\n return new Date().getTime();\n };\n }\n })();\n\n /**\n * Game loop that updates and renders the game every frame.\n * @memberof kontra\n *\n * @see kontra.gameLoop.prototype.set for list of parameters.\n */\n kontra.gameLoop = function(properties) {\n var gameLoop = Object.create(kontra.gameLoop.prototype);\n gameLoop.set(properties);\n\n return gameLoop;\n };\n\n kontra.gameLoop.prototype = {\n /**\n * Set properties on the game loop.\n * @memberof kontra.gameLoop\n *\n * @param {object} properties - Configure the game loop.\n * @param {number} [properties.fps=60] - Desired frame rate.\n * @param {function} properties.update - Function called to update the game.\n * @param {function} properties.render - Function called to render the game.\n */\n set: function set(properties) {\n properties = properties || {};\n\n // check for required functions\n if (typeof properties.update !== 'function' || typeof properties.render !== 'function') {\n var error = new ReferenceError('Required functions not found');\n kontra.logError(error, 'You must provide update() and render() functions to create a game loop.');\n return;\n }\n\n this.isStopped = false;\n\n // animation variables\n this._accumulator = 0;\n this._delta = 1E3 / (properties.fps || 60);\n\n this.update = properties.update;\n this.render = properties.render;\n },\n\n /**\n * Called every frame of the game loop.\n * @memberof kontra.gameLoop\n */\n frame: function frame() {\n var _this = this;\n\n _this._rAF = requestAnimationFrame(_this.frame.bind(_this));\n\n _this._now = kontra.timestamp();\n _this._dt = _this._now - _this._last;\n _this._last = _this._now;\n\n // prevent updating the game with a very large dt if the game were to lose focus\n // and then regain focus later\n if (_this._dt > 1E3) {\n return;\n }\n\n _this._accumulator += _this._dt;\n\n while (_this._accumulator >= _this._delta) {\n _this.update(_this._delta / 1E3);\n\n _this._accumulator -= _this._delta;\n }\n\n _this.render();\n },\n\n /**\n * Start the game loop.\n * @memberof kontra.gameLoop\n */\n start: function start() {\n this._last = kontra.timestamp();\n this.isStopped = false;\n requestAnimationFrame(this.frame.bind(this));\n },\n\n /**\n * Stop the game loop.\n */\n stop: function stop() {\n this.isStopped = true;\n cancelAnimationFrame(this._rAF);\n }\n };\n\n return kontra;\n})(kontra || {}, window);","/*jshint -W084 */\n\nvar kontra = (function(kontra, window) {\n 'use strict';\n\n var callbacks = {};\n var pressedKeys = {};\n\n var keyMap = {\n // named keys\n 8: 'backspace',\n 9: 'tab',\n 13: 'enter',\n 16: 'shift',\n 17: 'ctrl',\n 18: 'alt',\n 20: 'capslock',\n 27: 'esc',\n 32: 'space',\n 33: 'pageup',\n 34: 'pagedown',\n 35: 'end',\n 36: 'home',\n 37: 'left',\n 38: 'up',\n 39: 'right',\n 40: 'down',\n 45: 'insert',\n 46: 'delete',\n 91: 'leftwindow',\n 92: 'rightwindow',\n 93: 'select',\n 144: 'numlock',\n 145: 'scrolllock',\n\n // special characters\n 106: '*',\n 107: '+',\n 109: '-',\n 110: '.',\n 111: '/',\n 186: ';',\n 187: '=',\n 188: ',',\n 189: '-',\n 190: '.',\n 191: '/',\n 192: '`',\n 219: '[',\n 220: '\\\\',\n 221: ']',\n 222: '\\''\n };\n\n // alpha keys\n for (var i = 0; i < 26; i++) {\n keyMap[65+i] = String.fromCharCode(65+i).toLowerCase();\n }\n // numeric keys\n for (i = 0; i < 10; i++) {\n keyMap[48+i] = ''+i;\n }\n // f keys\n for (i = 1; i < 20; i++) {\n keyMap[111+i] = 'f'+i;\n }\n // keypad\n for (i = 0; i < 10; i++) {\n keyMap[96+i] = 'numpad'+i;\n }\n\n // shift keys mapped to their non-shift equivalent\n var shiftKeys = {\n '~': '`',\n '!': '1',\n '@': '2',\n '#': '3',\n '$': '4',\n '%': '5',\n '^': '6',\n '&': '7',\n '*': '8',\n '(': '9',\n ')': '0',\n '_': '-',\n '+': '=',\n ':': ';',\n '\"': '\\'',\n '<': ',',\n '>': '.',\n '?': '/',\n '|': '\\\\',\n 'plus': '='\n };\n\n // aliases modifier keys to their actual key for keyup event\n var aliases = {\n 'leftwindow': 'meta', // mac\n 'select': 'meta' // mac\n };\n\n // modifier order for combinations\n var modifierOrder = ['meta', 'ctrl', 'alt', 'shift'];\n\n window.addEventListener('keydown', keydownEventHandler);\n window.addEventListener('keyup', keyupEventHandler);\n window.addEventListener('blur', blurEventHandler);\n\n /**\n * Object for using the keyboard.\n */\n kontra.keys = {};\n\n /**\n * Register a function to be called on a keyboard keys.\n * Please note that not all keyboard combinations can be executed due to ghosting.\n * @memberof kontra.keys\n *\n * @param {string|string[]} keys - keys combination string(s).\n *\n * @throws {SyntaxError} If callback is not a function.\n */\n kontra.keys.bind = function bindKey(keys, callback) {\n if (typeof callback !== 'function') {\n var error = new SyntaxError('Invalid function.');\n kontra.logError(error, 'You must provide a function as the second parameter.');\n return;\n }\n\n keys = (kontra.isArray(keys) ? keys : [keys]);\n\n for (var i = 0, key; key = keys[i]; i++) {\n var combination = normalizeKeys(key);\n\n callbacks[combination] = callback;\n }\n };\n\n /**\n * Remove the callback function for a key combination.\n * @memberof kontra.keys\n *\n * @param {string|string[]} keys - keys combination string.\n */\n kontra.keys.unbind = function unbindKey(keys) {\n keys = (kontra.isArray(keys) ? keys : [keys]);\n\n for (var i = 0, key; key = keys[i]; i++) {\n var combination = normalizeKeys(key);\n\n callbacks[combination] = undefined;\n }\n };\n\n /**\n * Returns whether a key is pressed.\n * @memberof kontra.keys\n *\n * @param {string} keys - Keys combination string.\n *\n * @returns {boolean}\n */\n kontra.keys.pressed = function keyPressed(keys) {\n var combination = normalizeKeys(keys);\n var pressed = true;\n\n // loop over each key in the combination and verify that it is pressed\n keys = combination.split('+');\n for (var i = 0, key; key = keys[i]; i++) {\n pressed = pressed && !!pressedKeys[key];\n }\n\n return pressed;\n };\n\n /**\n * Normalize the event keycode\n * @private\n *\n * @param {Event} e\n *\n * @returns {number}\n */\n function normalizeKeyCode(e) {\n return (typeof e.which === 'number' ? e.which : e.keyCode);\n }\n\n /**\n * Normalize keys combination order.\n * @private\n *\n * @param {string} keys - keys combination string.\n *\n * @returns {string} Normalized combination.\n *\n * @example\n * normalizeKeys('c+ctrl'); //=> 'ctrl+c'\n * normalizeKeys('shift+++meta+alt'); //=> 'meta+alt+shift+plus'\n */\n function normalizeKeys(keys) {\n var combination = [];\n\n // handle '++' combinations\n keys = keys.trim().replace('++', '+plus');\n\n // put modifiers in the correct order\n for (var i = 0, modifier; modifier = modifierOrder[i]; i++) {\n\n // check for the modifier\n if (keys.indexOf(modifier) !== -1) {\n combination.push(modifier);\n keys = keys.replace(modifier, '');\n }\n }\n\n // remove all '+'s to leave only the last key\n keys = keys.replace(/\\+/g, '').toLowerCase();\n\n // check for shift key\n if (shiftKeys[keys]) {\n combination.push('shift+'+shiftKeys[keys]);\n }\n else if (keys) {\n combination.push(keys);\n }\n\n return combination.join('+');\n }\n\n /**\n * Get the key combination from an event.\n * @private\n *\n * @param {Event} e\n *\n * @return {string} normalized combination.\n */\n function getKeyCombination(e) {\n var combination = [];\n\n // check for modifiers\n for (var i = 0, modifier; modifier = modifierOrder[i]; i++) {\n if (e[modifier+'Key']) {\n combination.push(modifier);\n }\n }\n\n var key = keyMap[normalizeKeyCode(e)];\n\n // prevent duplicate keys from being added to the combination\n // for example 'ctrl+ctrl' since ctrl is both a modifier and\n // a regular key\n if (combination.indexOf(key) === -1) {\n combination.push(key);\n }\n\n return combination.join('+');\n }\n\n /**\n * Execute a function that corresponds to a keyboard combination.\n * @private\n *\n * @param {Event} e\n */\n function keydownEventHandler(e) {\n var combination = getKeyCombination(e);\n\n // set pressed keys\n for (var i = 0, keys = combination.split('+'), key; key = keys[i]; i++) {\n pressedKeys[key] = true;\n }\n\n if (callbacks[combination]) {\n callbacks[combination](e, combination);\n e.preventDefault();\n }\n }\n\n /**\n * Set the released key to not being pressed.\n * @private\n *\n * @param {Event} e\n */\n function keyupEventHandler(e) {\n var key = keyMap[normalizeKeyCode(e)];\n pressedKeys[key] = false;\n\n if (aliases[key]) {\n pressedKeys[ aliases[key] ] = false;\n }\n }\n\n /**\n * Reset pressed keys.\n * @private\n *\n * @param {Event} e\n */\n function blurEventHandler(e) {\n pressedKeys = {};\n }\n\n return kontra;\n})(kontra || {}, window);","/*jshint -W084 */\n\nvar kontra = (function(kontra) {\n 'use strict';\n\n /**\n * Object pool. The pool will grow in size to accommodate as many objects as are needed.\n * Unused items are at the front of the pool and in use items are at the of the pool.\n * @memberof kontra\n *\n * @see kontra.pool.prototype.set for list of parameters.\n */\n kontra.pool = function(properties) {\n var pool = Object.create(kontra.pool.prototype);\n pool.set(properties);\n\n return pool;\n };\n\n kontra.pool.prototype = {\n /**\n * Set properties on the pool.\n * @memberof kontra.pool\n *\n * @param {object} properties - Properties of the pool.\n * @param {object} properties.create - Function that returns the object to use in the pool.\n * @param {object} properties.createProperties - Properties that will be passed to the create function.\n * @param {number} properties.maxSize - The maximum size that the pool will grow to.\n * @param {boolean} properties.fill - Fill the pool to max size instead of slowly growing.\n *\n * Objects inside the pool must implement render(), update(),\n * set(), and isAlive() functions.\n */\n set: function set(properties) {\n properties = properties || {};\n\n var error, obj;\n\n // check for the correct structure of the objects added to pools so we know that the\n // rest of the pool code will work without errors\n if (typeof properties.create !== 'function') {\n error = new SyntaxError('Required function not found.');\n kontra.logError(error, 'Parameter \\'create\\' must be a function that returns an object.');\n return;\n }\n\n // bind the create function to always use the create properties\n this.create = properties.create.bind(this, properties.createProperties || {});\n\n // ensure objects for the pool have required functions\n obj = this.create();\n\n if (!obj || typeof obj.render !== 'function' || typeof obj.update !== 'function' ||\n typeof obj.set !== 'function' || typeof obj.isAlive !== 'function') {\n error = new SyntaxError('Create object required functions not found.');\n kontra.logError(error, 'Objects to be pooled must implement render(), update(), set() and isAlive() functions.');\n return;\n }\n\n // start the pool with an object\n this.objects = [obj];\n this.size = 1;\n this.maxSize = properties.maxSize || Infinity;\n this.lastIndex = 0;\n this.inUse = 0;\n\n // fill the pool\n if (properties.fill) {\n if (properties.maxSize) {\n while (this.objects.length < this.maxSize) {\n this.objects.unshift(this.create());\n }\n\n this.size = this.maxSize;\n this.lastIndex = this.maxSize - 1;\n }\n else {\n error = new SyntaxError('Required property not found.');\n kontra.logError(error, 'Parameter \\'maxSize\\' must be set before you can fill a pool.');\n return;\n }\n }\n },\n\n /**\n * Get an object from the pool.\n * @memberof kontra.pool\n *\n * @param {object} properties - Properties to pass to object.set().\n */\n get: function get(properties) {\n properties = properties || {};\n\n var _this = this;\n\n // the pool is out of objects if the first object is in use and it can't grow\n if (_this.objects[0].isAlive()) {\n if (_this.size === _this.maxSize) {\n return;\n }\n // 'double' the size of the array by filling it with twice as many objects\n else {\n for (var x = 0; x < _this.size && _this.objects.length < _this.maxSize; x++) {\n _this.objects.unshift(_this.create());\n }\n\n _this.size = _this.objects.length;\n _this.lastIndex = _this.size - 1;\n }\n }\n\n // save off first object in pool to reassign to last object after unshift\n var obj = _this.objects[0];\n obj.set(properties);\n\n // unshift the array\n for (var i = 1; i < _this.size; i++) {\n _this.objects[i-1] = _this.objects[i];\n }\n\n _this.objects[_this.lastIndex] = obj;\n _this.inUse++;\n },\n\n /**\n * Return all objects that are alive from the pool.\n * @memberof kontra.pool\n *\n * @returns {object[]}\n */\n getAliveObjects: function getAliveObjects() {\n return this.objects.slice(this.objects.length - this.inUse);\n },\n\n /**\n * Clear the object pool.\n * @memberof kontra.pool\n */\n clear: function clear() {\n this.inUse = 0;\n this.size = 1;\n this.lastIndex = 0;\n this.objects.length = 0;\n this.objects.push(this.create());\n },\n\n /**\n * Update all alive pool objects.\n * @memberof kontra.pool\n *\n * @param {number} dt - Time since last update.\n */\n update: function update(dt) {\n var i = this.lastIndex;\n var obj;\n\n // If the user kills an object outside of the update cycle, the pool won't know of\n // the change until the next update and inUse won't be decremented. If the user then\n // gets an object when inUse is the same size as objects.length, inUse will increment\n // and this statement will evaluate to -1.\n //\n // I don't like having to go through the pool to kill an object as it forces you to\n // know which object came from which pool. Instead, we'll just prevent the index from\n // going below 0 and accept the fact that inUse may be out of sync for a frame.\n var index = Math.max(this.objects.length - this.inUse, 0);\n\n // only iterate over the objects that are alive\n while (i >= index) {\n obj = this.objects[i];\n\n obj.update(dt);\n\n // if the object is dead, move it to the front of the pool\n if (!obj.isAlive()) {\n\n // push an object from the middle of the pool to the front of the pool\n // without returning a new array through Array#splice to avoid garbage\n // collection of the old array\n // @see http://jsperf.com/object-pools-array-vs-loop\n for (var j = i; j > 0; j--) {\n this.objects[j] = this.objects[j-1];\n }\n\n this.objects[0] = obj;\n this.inUse--;\n index++;\n }\n else {\n i--;\n }\n }\n },\n\n /**\n * render all alive pool objects.\n * @memberof kontra.pool\n */\n render: function render() {\n var index = Math.max(this.objects.length - this.inUse, 0);\n\n for (var i = this.lastIndex; i >= index; i--) {\n this.objects[i].render();\n }\n }\n };\n\n return kontra;\n})(kontra || {});","/*jshint -W084 */\n\nvar kontra = (function(kontra, undefined) {\n 'use strict';\n\n /**\n * A quadtree for 2D collision checking. The quadtree acts like an object pool in that it\n * will create subnodes as objects are needed but it won't clean up the subnodes when it\n * collapses to avoid garbage collection.\n * @memberof kontra\n *\n * @see kontra.quadtree.prototype.set for list of parameters.\n *L\n * The quadrant indices are numbered as follows (following a z-order curve):\n * |\n * 0 | 1\n * ----+----\n * 2 | 3\n * |\n */\n kontra.quadtree = function(properties) {\n var quadtree = Object.create(kontra.quadtree.prototype);\n quadtree.set(properties);\n\n return quadtree;\n };\n\n kontra.quadtree.prototype = {\n /**\n * Set properties on the quadtree.\n * @memberof kontra.quadtree\n *\n * @param {number} [depth=0] - Current node depth.\n * @param {number} [maxDepth=3] - Maximum node depths the quadtree can have.\n * @param {number} [maxObjects=25] - Maximum number of objects a node can support before splitting.\n * @param {object} [parentNode] - The node that contains this node.\n * @param {object} [bounds] - The 2D space this node occupies.\n */\n set: function set(properties) {\n properties = properties || {};\n\n this.depth = properties.depth || 0;\n this.maxDepth = properties.maxDepth || 3;\n this.maxObjects = properties.maxObjects || 25;\n\n // since we won't clean up any subnodes, we need to keep track of which nodes are\n // currently the leaf node so we know which nodes to add objects to\n this.isBranchNode = false;\n\n this.parentNode = properties.parentNode;\n\n this.bounds = properties.bounds || {\n x: 0,\n y: 0,\n width: kontra.game.width,\n height: kontra.game.height\n };\n\n this.objects = [];\n this.subnodes = [];\n },\n\n /**\n * Clear the quadtree\n * @memberof kontra.quadtree\n */\n clear: function clear() {\n if (this.isBranchNode) {\n for (var i = 0; i < 4; i++) {\n this.subnodes[i].clear();\n }\n }\n\n this.isBranchNode = false;\n this.objects.length = 0;\n },\n\n /**\n * Find the leaf node the object belongs to and get all objects that are part of\n * that node.\n * @memberof kontra.quadtree\n *\n * @param {object} object - Object to use for finding the leaf node.\n *\n * @returns {object[]} A list of objects in the same leaf node as the object.\n */\n get: function get(object) {\n var node = this;\n var objects = [];\n var indices, index;\n\n // traverse the tree until we get to a leaf node\n while (node.subnodes.length && this.isBranchNode) {\n indices = this._getIndex(object);\n\n for (var i = 0, length = indices.length; i < length; i++) {\n index = indices[i];\n\n objects.push.apply(objects, this.subnodes[index].get(object));\n }\n\n return objects;\n }\n\n return node.objects;\n },\n\n /**\n * Add an object to the quadtree. Once the number of objects in the node exceeds\n * the maximum number of objects allowed, it will split and move all objects to their\n * corresponding subnodes.\n * @memberof kontra.quadtree\n */\n add: function add() {\n var _this = this;\n var i, object, obj, indices, index;\n\n for (var j = 0, length = arguments.length; j < length; j++) {\n object = arguments[j];\n\n // add a group of objects separately\n if (kontra.isArray(object)) {\n _this.add.apply(this, object);\n\n continue;\n }\n\n // current node has subnodes, so we need to add this object into a subnode\n if (_this.subnodes.length && _this.isBranchNode) {\n _this._addToSubnode(object);\n\n continue;\n }\n\n // this node is a leaf node so add the object to it\n _this.objects.push(object);\n\n // split the node if there are too many objects\n if (_this.objects.length > _this.maxObjects && _this.depth < _this.maxDepth) {\n _this._split();\n\n // move all objects to their corresponding subnodes\n for (i = 0; obj = _this.objects[i]; i++) {\n _this._addToSubnode(obj);\n }\n\n _this.objects.length = 0;\n }\n }\n },\n\n /**\n * Add an object to a subnode.\n * @memberof kontra.quadtree\n * @private\n *\n * @param {object} object - Object to add into a subnode\n */\n _addToSubnode: function _addToSubnode(object) {\n var indices = this._getIndex(object);\n\n // add the object to all subnodes it intersects\n for (var i = 0, length = indices.length; i < length; i++) {\n this.subnodes[ indices[i] ].add(object);\n }\n },\n\n /**\n * Determine which subnodes the object intersects with.\n * @memberof kontra.quadtree\n * @private\n *\n * @param {object} object - Object to check.\n *\n * @returns {number[]} List of all subnodes object intersects.\n */\n _getIndex: function getIndex(object) {\n var indices = [];\n\n var verticalMidpoint = this.bounds.x + this.bounds.width / 2;\n var horizontalMidpoint = this.bounds.y + this.bounds.height / 2;\n\n // handle non-kontra.sprite objects as well as kontra.sprite objects\n var x = (object.x !== undefined ? object.x : object.position.x);\n var y = (object.y !== undefined ? object.y : object.position.y);\n\n // save off quadrant checks for reuse\n var intersectsTopQuadrants = y < horizontalMidpoint && y + object.height >= this.bounds.y;\n var intersectsBottomQuadrants = y + object.height >= horizontalMidpoint && y < this.bounds.y + this.bounds.height;\n\n // object intersects with the left quadrants\n if (x < verticalMidpoint && x + object.width >= this.bounds.x) {\n if (intersectsTopQuadrants) { // top left\n indices.push(0);\n }\n\n if (intersectsBottomQuadrants) { // bottom left\n indices.push(2);\n }\n }\n\n // object intersects with the right quadrants\n if (x + object.width >= verticalMidpoint && x < this.bounds.x + this.bounds.width) { // top right\n if (intersectsTopQuadrants) {\n indices.push(1);\n }\n\n if (intersectsBottomQuadrants) { // bottom right\n indices.push(3);\n }\n }\n\n return indices;\n },\n\n /**\n * Split the node into four subnodes.\n * @memberof kontra.quadtree\n * @private\n */\n _split: function split() {\n this.isBranchNode = true;\n\n // only split if we haven't split before\n if (this.subnodes.length) {\n return;\n }\n\n var subWidth = this.bounds.width / 2 | 0;\n var subHeight = this.bounds.height / 2 | 0;\n var x = this.bounds.x;\n var y = this.bounds.y;\n\n for (var i = 0; i < 4; i++) {\n this.subnodes[i] = kontra.quadtree({\n bounds: {\n x: x + (i % 2 === 1 ? subWidth : 0), // nodes 1 and 3\n y: y + (i >= 2 ? subHeight : 0), // nodes 2 and 3\n width: subWidth,\n height: subHeight\n },\n depth: this.depth+1,\n maxDepth: this.maxDepth,\n maxObjects: this.maxObjects,\n parentNode: this\n });\n }\n },\n\n /**\n * Draw the quadtree. Useful for visual debugging.\n * @memberof kontra.quadtree\n */\n render: function() {\n // don't draw empty leaf nodes, always draw branch nodes and the first node\n if (this.objects.length || this.depth === 0 ||\n (this.parentNode && this.parentNode.isBranchNode)) {\n\n kontra.context.strokeStyle = 'red';\n kontra.context.strokeRect(this.bounds.x, this.bounds.y, this.bounds.width, this.bounds.height);\n\n if (this.subnodes.length) {\n for (var i = 0; i < 4; i++) {\n this.subnodes[i].render();\n }\n }\n }\n }\n };\n\n return kontra;\n})(kontra || {});","var kontra = (function(kontra, Math, undefined) {\n 'use strict';\n\n // prevent these properties from being set at the end of kontra.sprite.set()\n var excludedProperties = [\n 'x',\n 'y',\n 'dx',\n 'dy',\n 'ddx',\n 'ddy',\n 'timeToLive',\n 'context',\n 'image',\n 'animations',\n 'color',\n 'width',\n 'height'\n ];\n\n /**\n * A vector for 2D space.\n * @memberof kontra\n *\n * @see kontra.vector.prototype.set for list of parameters.\n */\n kontra.vector = function(x, y) {\n var vector = Object.create(kontra.vector.prototype);\n vector.set(x, y);\n\n return vector;\n };\n\n kontra.vector.prototype = {\n /**\n * Set the vectors x and y position.\n * @memberof kontra.vector\n *\n * @param {number} x=0 - Center x coordinate.\n * @param {number} y=0 - Center y coordinate.\n *\n * @returns {vector}\n */\n set: function set(x, y) {\n this.x = x || 0;\n this.y = y || 0;\n\n return this;\n },\n\n /**\n * Add a vector to this vector.\n * @memberof kontra.vector\n *\n * @param {vector} vector - Vector to add.\n * @param {number} dt=1 - Time since last update.\n */\n add: function add(vector, dt) {\n this.x += (vector.x || 0) * (dt || 1);\n this.y += (vector.y || 0) * (dt || 1);\n },\n\n /**\n * Clamp the vector between two points that form a rectangle.\n * Please note that clamping will only work if the add function is called.\n * @memberof kontra.vector\n *\n * @param {number} [xMin=-Infinity] - Min x value.\n * @param {number} [yMin=Infinity] - Min y value.\n * @param {number} [xMax=-Infinity] - Max x value.\n * @param {number} [yMax=Infinity] - Max y value.\n */\n clamp: function clamp(xMin, yMin, xMax, yMax) {\n this._xMin = (xMin !== undefined ? xMin : -Infinity);\n this._xMax = (xMax !== undefined ? xMax : Infinity);\n this._yMin = (yMin !== undefined ? yMin : -Infinity);\n this._yMax = (yMax !== undefined ? yMax : Infinity);\n\n // rename x and y so we can use them as getters and setters\n this._x = this.x;\n this._y = this.y;\n\n // define getters to return the renamed x and y and setters to clamp their value\n Object.defineProperties(this, {\n x: {\n get: function() {\n return this._x;\n },\n set: function(value) {\n this._x = Math.min( Math.max(value, this._xMin), this._xMax );\n }\n },\n y: {\n get: function() {\n return this._y;\n },\n set: function(value) {\n this._y = Math.min( Math.max(value, this._yMin), this._yMax );\n }\n }\n });\n }\n };\n\n\n\n\n\n /**\n * A sprite with a position, velocity, and acceleration.\n * @memberof kontra\n * @requires kontra.vector\n *\n * @see kontra.sprite._prot.set for list of parameters.\n */\n kontra.sprite = function(properties) {\n var sprite = Object.create(kontra.sprite.prototype);\n sprite.set(properties);\n\n return sprite;\n };\n\n kontra.sprite.prototype = {\n /**\n * Move the sprite by its velocity.\n * @memberof kontra.sprite\n *\n * @param {number} dt - Time since last update.\n */\n advanceSprite: function advanceSprite(dt) {\n this.velocity.add(this.acceleration, dt);\n this.position.add(this.velocity, dt);\n\n this.timeToLive--;\n },\n\n /**\n * Draw a simple rectangle. Useful for prototyping.\n * @memberof kontra.sprite\n */\n drawRect: function drawRect() {\n this.context.fillStyle = this.color;\n this.context.fillRect(this.position.x, this.position.y, this.width, this.height);\n },\n\n /**\n * Draw the sprite.\n * @memberof kontra.sprite\n */\n drawImage: function drawImage() {\n this.context.drawImage(this.image, this.position.x, this.position.y);\n },\n\n /**\n * Update the currently playing animation. Used when animations are passed to the sprite.\n * @memberof kontra.sprite\n *\n * @param {number} dt - Time since last update.\n */\n advanceAnimation: function advanceAnimation(dt) {\n this.advanceSprite(dt);\n\n this.currentAnimation.update(dt);\n },\n\n /**\n * Draw the currently playing animation. Used when animations are passed to the sprite.\n * @memberof kontra.sprite\n */\n drawAnimation: function drawAnimation() {\n this.currentAnimation.render({\n context: this.context,\n x: this.position.x,\n y: this.position.y\n });\n },\n\n /**\n * Play an animation.\n * @memberof kontra.sprite\n *\n * @param {string} name - Name of the animation to play.\n */\n playAnimation: function playAnimation(name) {\n this.currentAnimation = this.animations[name];\n },\n\n /**\n * Determine if the sprite is alive.\n * @memberof kontra.sprite\n *\n * @returns {boolean}\n */\n isAlive: function isAlive() {\n return this.timeToLive > 0;\n },\n\n /**\n * Set properties on the sprite.\n * @memberof kontra.sprite\n *\n * @param {object} properties - Properties to set on the sprite.\n * @param {number} properties.x - X coordinate of the sprite.\n * @param {number} properties.y - Y coordinate of the sprite.\n * @param {number} [properties.dx] - Change in X position.\n * @param {number} [properties.dy] - Change in Y position.\n * @param {number} [properties.ddx] - Change in X velocity.\n * @param {number} [properties.ddy] - Change in Y velocity.\n *\n * @param {number} [properties.timeToLive=0] - How may frames the sprite should be alive.\n * @param {Context} [properties.context=kontra.context] - Provide a context for the sprite to draw on.\n *\n * @param {Image|Canvas} [properties.image] - Image for the sprite.\n *\n * @param {object} [properties.animations] - Animations for the sprite instead of an image.\n *\n * @param {string} [properties.color] - If no image or animation is provided, use color to draw a rectangle for the sprite.\n * @param {number} [properties.width] - Width of the sprite for drawing a rectangle.\n * @param {number} [properties.height] - Height of the sprite for drawing a rectangle.\n *\n * @param {function} [properties.update] - Function to use to update the sprite.\n * @param {function} [properties.render] - Function to use to render the sprite.\n *\n * If you need the sprite to live forever, or just need it to stay on screen until you\n * decide when to kill it, you can set timeToLive to Infinity.\n * Just be sure to set timeToLive to 0 when you want the sprite to die.\n */\n set: function set(properties) {\n properties = properties || {};\n\n var _this = this;\n\n _this.position = (_this.position || kontra.vector()).set(properties.x, properties.y);\n _this.velocity = (_this.velocity || kontra.vector()).set(properties.dx, properties.dy);\n _this.acceleration = (_this.acceleration || kontra.vector()).set(properties.ddx, properties.ddy);\n\n _this.timeToLive = properties.timeToLive || 0;\n _this.context = properties.context || kontra.context;\n\n // image sprite\n if (kontra.isImage(properties.image) || kontra.isCanvas(properties.image)) {\n _this.image = properties.image;\n _this.width = properties.image.width;\n _this.height = properties.image.height;\n\n // change the advance and draw functions to work with images\n _this.advance = _this.advanceSprite;\n _this.draw = _this.drawImage;\n }\n // animation sprite\n else if (properties.animations) {\n _this.animations = properties.animations;\n\n // default the current animation to the first one in the list\n _this.currentAnimation = properties.animations[ Object.keys(properties.animations)[0] ];\n _this.width = _this.currentAnimation.width;\n _this.height = _this.currentAnimation.height;\n\n // change the advance and draw functions to work with animations\n _this.advance = _this.advanceAnimation;\n _this.draw = _this.drawAnimation;\n }\n // rectangle sprite\n else {\n _this.color = properties.color;\n _this.width = properties.width;\n _this.height = properties.height;\n\n // change the advance and draw functions to work with rectangles\n _this.advance = _this.advanceSprite;\n _this.draw = _this.drawRect;\n }\n\n // loop through all other properties an add them to the sprite\n for (var prop in properties) {\n if (properties.hasOwnProperty(prop) && excludedProperties.indexOf(prop) === -1) {\n _this[prop] = properties[prop];\n }\n }\n },\n\n // define getter and setter shortcut functions to make it easier to work work with the\n // position, velocity, and acceleration vectors.\n\n /**\n * Sprite position.x\n * @memberof kontra.sprite\n *\n * @property {number} x\n */\n get x() {\n return this.position.x;\n },\n\n /**\n * Sprite position.y\n * @memberof kontra.sprite\n *\n * @property {number} y\n */\n get y() {\n return this.position.y;\n },\n\n /**\n * Sprite velocity.x\n * @memberof kontra.sprite\n *\n * @property {number} dx\n */\n get dx() {\n return this.velocity.x;\n },\n\n /**\n * Sprite velocity.y\n * @memberof kontra.sprite\n *\n * @property {number} dy\n */\n get dy() {\n return this.velocity.y;\n },\n\n /**\n * Sprite acceleration.x\n * @memberof kontra.sprite\n *\n * @property {number} ddx\n */\n get ddx() {\n return this.acceleration.x;\n },\n\n /**\n * Sprite acceleration.y\n * @memberof kontra.sprite\n *\n * @property {number} ddy\n */\n get ddy() {\n return this.acceleration.y;\n },\n\n set x(value) {\n this.position.x = value;\n },\n set y(value) {\n this.position.y = value;\n },\n set dx(value) {\n this.velocity.x = value;\n },\n set dy(value) {\n this.velocity.y = value;\n },\n set ddx(value) {\n this.acceleration.x = value;\n },\n set ddy(value) {\n this.acceleration.y = value;\n },\n\n /**\n * Simple bounding box collision test.\n * @memberof kontra.sprite\n *\n * @param {object} object - Object to check collision against.\n *\n * @returns {boolean} True if the objects collide, false otherwise.\n */\n collidesWith: function collidesWith(object) {\n // handle non-kontra.sprite objects as well as kontra.sprite objects\n var x = (object.x !== undefined ? object.x : object.position.x);\n var y = (object.y !== undefined ? object.y : object.position.y);\n\n if (this.position.x < x + object.width &&\n this.position.x + this.width > x &&\n this.position.y < y + object.height &&\n this.position.y + this.height > y) {\n return true;\n }\n\n return false;\n },\n\n /**\n * Update the sprites velocity and position.\n * @memberof kontra.sprite\n * @abstract\n *\n * @param {number} dt - Time since last update.\n *\n * This function can be overridden on a per sprite basis if more functionality\n * is needed in the update step. Just call this.advance() when you need\n * the sprite to update its position.\n *\n * @example\n * sprite = kontra.sprite({\n * update: function update(dt) {\n * // do some logic\n *\n * this.advance(dt);\n * }\n * });\n */\n update: function update(dt) {\n this.advance(dt);\n },\n\n /**\n * Render the sprite.\n * @memberof kontra.sprite.\n * @abstract\n *\n * This function can be overridden on a per sprite basis if more functionality\n * is needed in the render step. Just call this.draw() when you need the\n * sprite to draw its image.\n *\n * @example\n * sprite = kontra.sprite({\n * render: function render() {\n * // do some logic\n *\n * this.draw();\n * }\n * });\n */\n render: function render() {\n this.draw();\n }\n };\n\n return kontra;\n})(kontra || {}, Math);","/*jshint -W084 */\n\nvar kontra = (function(kontra, undefined) {\n 'use strict';\n\n /**\n * Single animation from a sprite sheet.\n * @memberof kontra\n *\n * @see kontra.pool.prototype.set for list of parameters.\n */\n kontra.animation = function(properties) {\n var animation = Object.create(kontra.animation.prototype);\n animation.set(properties);\n\n return animation;\n };\n\n kontra.animation.prototype = {\n /**\n * Set properties on the animation.\n * @memberof kontra.animation\n *\n * @param {object} properties - Properties of the animation.\n * @param {spriteSheet} properties.spriteSheet - Sprite sheet for the animation.\n * @param {number[]} properties.frames - List of frames of the animation.\n * @param {number} properties.frameSpeed - Time to wait before transitioning the animation to the next frame.\n */\n set: function set(properties) {\n properties = properties || {};\n\n this.spriteSheet = properties.spriteSheet;\n this.frames = properties.frames;\n this.frameSpeed = properties.frameSpeed;\n\n this.width = properties.spriteSheet.frame.width;\n this.height = properties.spriteSheet.frame.height;\n\n this.currentFrame = 0;\n this._accumulator = 0;\n this.update = this.advance;\n this.render = this.draw;\n },\n\n /**\n * Update the animation. Used when the animation is not paused or stopped.\n * @memberof kontra.animation\n * @private\n *\n * @param {number} dt=1 - Time since last update.\n */\n advance: function advance(dt) {\n // normalize dt to work with milliseconds as a decimal or an integer\n dt = (dt < 1 ? dt * 1E3 : dt) || 1;\n\n this._accumulator += dt;\n\n // update to the next frame if it's time\n while (this._accumulator >= this.frameSpeed) {\n this.currentFrame = ++this.currentFrame % this.frames.length;\n\n this._accumulator -= this.frameSpeed;\n }\n },\n\n /**\n * Draw the current frame. Used when the animation is not stopped.\n * @memberof kontra.animation\n * @private\n *\n * @param {object} properties - How to draw the animation.\n * @param {number} properties.x - X position to draw.\n * @param {number} properties.y - Y position to draw.\n * @param {Context} [properties.context=kontra.context] - Provide a context for the sprite to draw on.\n */\n draw: function draw(properties) {\n properties = properties || {};\n\n var context = properties.context || kontra.context;\n\n // get the row and col of the frame\n var row = this.frames[this.currentFrame] / this.spriteSheet.framesPerRow | 0;\n var col = this.frames[this.currentFrame] % this.spriteSheet.framesPerRow | 0;\n\n context.drawImage(\n this.spriteSheet.image,\n col * this.spriteSheet.frame.width, row * this.spriteSheet.frame.height,\n this.spriteSheet.frame.width, this.spriteSheet.frame.height,\n properties.x, properties.y,\n this.spriteSheet.frame.width, this.spriteSheet.frame.height\n );\n },\n\n /**\n * Play the animation.\n * @memberof kontra.animation\n */\n play: function play() {\n // restore references to update and render functions only if overridden\n this.update = this.advance;\n this.render = this.draw;\n },\n\n /**\n * Stop the animation and prevent update and render.\n * @memberof kontra.animation\n */\n stop: function stop() {\n\n // instead of putting an if statement in both render/update functions that checks\n // a variable to determine whether to render or update, we can just reassign the\n // functions to noop and save processing time in the game loop.\n // @see http://jsperf.com/boolean-check-vs-noop\n this.update = kontra.noop;\n this.render = kontra.noop;\n },\n\n /**\n * Pause the animation and prevent update.\n * @memberof kontra.animation\n */\n pause: function pause() {\n this.update = kontra.noop;\n }\n };\n\n\n\n\n\n\n /**\n * Create a sprite sheet from an image.\n * @memberof kontra\n *\n * @see kontra.spriteSheet.prototype.set for list of parameters.\n */\n kontra.spriteSheet = function(properties) {\n var spriteSheet = Object.create(kontra.spriteSheet.prototype);\n spriteSheet.set(properties);\n\n return spriteSheet;\n };\n\n kontra.spriteSheet.prototype = {\n /**\n * Set properties on the spriteSheet.\n * @memberof kontra\n * @constructor\n *\n * @param {object} properties - Configure the sprite sheet.\n * @param {Image|Canvas} properties.image - Image for the sprite sheet.\n * @param {number} properties.frameWidth - Width (in px) of each frame.\n * @param {number} properties.frameHeight - Height (in px) of each frame.\n * @param {object} properties.animations - Animations to create from the sprite sheet.\n */\n set: function set(properties) {\n properties = properties || {};\n\n this.animations = {};\n\n if (kontra.isImage(properties.image) || kontra.isCanvas(properties.image)) {\n this.image = properties.image;\n this.frame = {\n width: properties.frameWidth,\n height: properties.frameHeight\n };\n\n this.framesPerRow = properties.image.width / properties.frameWidth | 0;\n }\n else {\n var error = new SyntaxError('Invalid image.');\n kontra.logError(error, 'You must provide an Image for the SpriteSheet.');\n return;\n }\n\n if (properties.animations) {\n this.createAnimations(properties.animations);\n }\n },\n\n /**\n * Create animations from the sprite sheet.\n * @memberof kontra.spriteSheet\n *\n * @param {object} animations - List of named animations to create from the Image.\n * @param {number|string|number[]|string[]} animations.animationName.frames - A single frame or list of frames for this animation.\n * @param {number} animations.animationName.frameSpeed=1 - Number of frames to wait before transitioning the animation to the next frame.\n *\n * @example\n * var sheet = kontra.spriteSheet({image: img, frameWidth: 16, frameHeight: 16});\n * sheet.createAnimations({\n * idle: {\n * frames: 1 // single frame animation\n * },\n * walk: {\n * frames: '2..6', // ascending consecutive frame animation (frames 2-6, inclusive)\n * frameSpeed: 4\n * },\n * moonWalk: {\n * frames: '6..2', // descending consecutive frame animation\n * frameSpeed: 4\n * },\n * jump: {\n * frames: [7, 12, 2], // non-consecutive frame animation\n * frameSpeed: 3\n * },\n * attack: {\n * frames: ['8..10', 13, '10..8'], // you can also mix and match, in this case frames [8,9,10,13,10,9,8]\n * frameSpeed: 2\n * }\n * });\n */\n createAnimations: function createAnimations(animations) {\n var error;\n\n if (!animations || Object.keys(animations).length === 0) {\n error = new ReferenceError('No animations found.');\n kontra.logError(error, 'You must provide at least one named animation to create an Animation.');\n return;\n }\n\n // create each animation by parsing the frames\n var animation, frames, frameSpeed, sequence;\n for (var name in animations) {\n if (!animations.hasOwnProperty(name)) {\n continue;\n }\n\n animation = animations[name];\n frames = animation.frames;\n frameSpeed = animation.frameSpeed;\n\n // array that holds the order of the animation\n sequence = [];\n\n if (frames === undefined) {\n error = new ReferenceError('No animation frames found.');\n kontra.logError(error, 'Animation ' + name + ' must provide a frames property.');\n return;\n }\n\n // single frame\n if (kontra.isNumber(frames)) {\n sequence.push(frames);\n }\n // consecutive frames\n else if (kontra.isString(frames)) {\n sequence = this._parseFrames(frames);\n }\n // non-consecutive frames\n else if (kontra.isArray(frames)) {\n for (var i = 0, frame; frame = frames[i]; i++) {\n\n // consecutive frames\n if (kontra.isString(frame)) {\n\n // add new frames to the end of the array\n sequence.push.apply(sequence, this._parseFrames(frame));\n }\n // single frame\n else {\n sequence.push(frame);\n }\n }\n }\n\n this.animations[name] = kontra.animation({\n spriteSheet: this,\n frames: sequence,\n frameSpeed: frameSpeed\n });\n }\n },\n\n /**\n * Parse a string of consecutive frames.\n * @memberof kontra.spriteSheet\n * @private\n *\n * @param {string} frames - Start and end frame.\n *\n * @returns {number[]} List of frames.\n */\n _parseFrames: function parseFrames(frames) {\n var sequence = [];\n var consecutiveFrames = frames.split('..').map(Number);\n\n // determine which direction to loop\n var direction = (consecutiveFrames[0] < consecutiveFrames[1] ? 1 : -1);\n var i;\n\n // ascending frame order\n if (direction === 1) {\n for (i = consecutiveFrames[0]; i <= consecutiveFrames[1]; i++) {\n sequence.push(i);\n }\n }\n // descending order\n else {\n for (i = consecutiveFrames[0]; i >= consecutiveFrames[1]; i--) {\n sequence.push(i);\n }\n }\n\n return sequence;\n }\n };\n\n return kontra;\n})(kontra || {});","/**\n * localStorage can be a bit of a pain to work with since it stores everything as strings:\n * localStorage.setItem('item', 1); //=> '1'\n * localStorage.setItem('item', false); //=> 'false'\n * localStorage.setItem('item', [1,2,3]); //=> '1,2,3'\n * localStorage.setItem('item', {a:'b'}); //=> '[object Object]'\n * localStorage.setItem('item', undefinedVariable); //=> 'undefined'\n *\n * @fileoverview A simple wrapper for localStorage to make it easier to work with.\n * Based on store.js {@see https://github.com/marcuswestin/store.js}\n */\nvar kontra = (function(kontra, window, localStorage, undefined) {\n 'use strict';\n\n // check if the browser can use localStorage\n kontra.canUse = kontra.canUse || {};\n kontra.canUse.localStorage = 'localStorage' in window && window.localStorage !== null;\n\n if (!kontra.canUse.localStorage) {\n return kontra;\n }\n\n /**\n * Object for using localStorage.\n */\n kontra.store = {};\n\n /**\n * Save an item to localStorage.\n * @memberof kontra.store\n *\n * @param {string} key - Name to store the item as.\n * @param {*} value - Item to store.\n */\n kontra.store.set = function setStoreItem(key, value) {\n if (value === undefined) {\n this.remove(key);\n }\n else {\n localStorage.setItem(key, JSON.stringify(value));\n }\n };\n\n /**\n * Retrieve an item from localStorage and convert it back to it's original type.\n * @memberof kontra.store\n *\n * @param {string} key - Name of the item.\n *\n * @returns {*}\n */\n kontra.store.get = function getStoreItem(key) {\n var value = localStorage.getItem(key);\n\n try {\n value = JSON.parse(value);\n }\n catch(e) {}\n\n return value;\n };\n\n /**\n * Remove an item from localStorage.\n * @memberof kontra.store\n *\n * @param {string} key - Name of the item.\n */\n kontra.store.remove = function removeStoreItem(key) {\n localStorage.removeItem(key);\n };\n\n /**\n * Clear all keys from localStorage.\n * @memberof kontra.store\n */\n kontra.store.clear = function clearStore() {\n localStorage.clear();\n };\n\n return kontra;\n})(kontra || {}, window, window.localStorage);","/*jshint -W084 */\n\nvar kontra = (function(kontra, Math, undefined) {\n 'use strict';\n\n /**\n * A tile engine for rendering tilesets. Works well with the tile engine program Tiled.\n * @memberof kontra\n *\n * @see kontra.tileEngine.prototype.set for list of parameters.\n */\n kontra.tileEngine = function(properties) {\n var tileEngine = Object.create(kontra.tileEngine.prototype);\n tileEngine.set(properties);\n\n return tileEngine;\n };\n\n kontra.tileEngine.prototype = {\n /**\n * Set properties on the tile engine.\n * @memberof kontra.tileEngine\n *\n * @param {object} properties - Properties of the tile engine.\n * @param {number} [properties.tileWidth=32] - Width of a tile.\n * @param {number} [properties.tileHeight=32] - Height of a tile.\n * @param {number} properties.width - Width of the map (in tiles).\n * @param {number} properties.height - Height of the map (in tiles).\n * @param {number} [properties.x=0] - X position to draw.\n * @param {number} [properties.y=0] - Y position to draw.\n * @param {number} [properties.sx=0] - X position to clip the tileset.\n * @param {number} [properties.sy=0] - Y position to clip the tileset.\n * @param {Context} [properties.context=kontra.context] - Provide a context for the tile engine to draw on.\n */\n set: function set(properties) {\n properties = properties || {};\n\n var _this = this;\n\n // size of the map (in tiles)\n if (!properties.width || !properties.height) {\n var error = new ReferenceError('Required parameters not found');\n kontra.logError(error, 'You must provide width and height of the map to create a tile engine.');\n return;\n }\n\n _this.width = properties.width;\n _this.height = properties.height;\n\n // size of the tiles. Most common tile size on opengameart.org seems to be 32x32,\n // followed by 16x16\n _this.tileWidth = properties.tileWidth || 32;\n _this.tileHeight = properties.tileHeight || 32;\n\n _this.context = properties.context || kontra.context;\n\n _this.canvasWidth = _this.context.canvas.width;\n _this.canvasHeight = _this.context.canvas.height;\n\n // create an off-screen canvas for pre-rendering the map\n // @see http://jsperf.com/render-vs-prerender\n _this._offscreenCanvas = document.createElement('canvas');\n _this._offscreenContext = _this._offscreenCanvas.getContext('2d');\n\n // make the off-screen canvas the full size of the map\n _this._offscreenCanvas.width = _this.mapWidth = _this.width * _this.tileWidth;\n _this._offscreenCanvas.height = _this.mapHeight = _this.height * _this.tileHeight;\n\n // when clipping an image, sx and sy must within the image region, otherwise\n // Firefox and Safari won't draw it.\n // @see http://stackoverflow.com/questions/19338032/canvas-indexsizeerror-index-or-size-is-negative-or-greater-than-the-allowed-a\n _this.sxMax = _this.mapWidth - _this.canvasWidth;\n _this.syMax = _this.mapHeight - _this.canvasHeight;\n\n _this.layers = {};\n\n // draw order of layers (by name)\n _this._layerOrder = [];\n\n // each tileset will hold the first and the last grid as well as the image for the tileset\n _this.tilesets = [];\n\n _this.x = properties.x || 0;\n _this.y = properties.y || 0;\n _this.sx = properties.sx || 0;\n _this.sy = properties.sy || 0;\n },\n\n /**\n * Add an tileset for the tile engine to use.\n * @memberof kontra.tileEngine\n *\n * @param {object} properties - Properties of the image to add.\n * @param {string|Image|Canvas} properties.image - Path to the image or Image object.\n * @param {number} properties.firstGrid - The first tile grid to start the image.\n */\n addTileset: function addTileset(properties) {\n properties = properties || {};\n\n if (kontra.isImage(properties.image) || kontra.isCanvas(properties.image)) {\n var image = properties.image;\n var firstGrid = properties.firstGrid;\n var numTiles = (image.width / this.tileWidth | 0) * (image.height / this.tileHeight | 0);\n\n if (!firstGrid) {\n // only calculate the first grid if the tile map has a tileset already\n if (this.tilesets.length > 0) {\n var lastTileset = this.tilesets[this.tilesets.length - 1];\n var tiles = (lastTileset.image.width / this.tileWidth | 0) *\n (lastTileset.image.height / this.tileHeight | 0);\n\n firstGrid = lastTileset.firstGrid + tiles - 1;\n }\n // otherwise this is the first tile added to the tile map\n else {\n firstGrid = 1;\n }\n }\n\n this.tilesets.push({\n firstGrid: firstGrid,\n lastGrid: firstGrid + numTiles - 1,\n image: image\n });\n\n // sort the tile map so we can perform a binary search when drawing\n this.tilesets.sort(function(a, b) {\n return a.firstGrid - b.firstGrid;\n });\n }\n else {\n var error = new SyntaxError('Invalid image.');\n kontra.logError(error, 'You must provide an Image for the tile engine.');\n return;\n }\n },\n\n /**\n * Add a layer to the tile engine.\n * @memberof kontra.tileEngine\n *\n * @param {object} properties - Properties of the layer to add.\n * @param {string} properties.name - Name of the layer.\n * @param {number[]} properties.data - Tile layer data.\n * @param {boolean} [properties.render=true] - If the layer should be drawn.\n * @param {number} properties.zIndex - Draw order for tile layer. Highest number is drawn last (i.e. on top of all other layers).\n */\n addLayer: function addLayer(properties) {\n properties = properties || {};\n properties.render = (properties.render === undefined ? true : properties.render);\n\n var _this = this;\n var data;\n\n // flatten a 2D array into a single array\n if (kontra.isArray(properties.data[0])) {\n data = [];\n\n for (var r = 0, row; row = properties.data[r]; r++) {\n for (var c = 0, length = row.length; c < length; c++) {\n data.push(row[c]);\n }\n }\n }\n else {\n data = properties.data;\n }\n\n this.layers[properties.name] = data;\n this.layers[properties.name].zIndex = properties.zIndex || 0;\n this.layers[properties.name].render = properties.render;\n\n // only add the layer to the layer order if it should be drawn\n if (properties.render) {\n this._layerOrder.push(properties.name);\n\n this._layerOrder.sort(function(a, b) {\n return _this.layers[a].zIndex - _this.layers[b].zIndex;\n });\n\n this._preRenderImage();\n }\n },\n\n /**\n * Simple bounding box collision test for layer tiles.\n * @memberof kontra.tileEngine\n *\n * @param {string} name - Name of the layer.\n * @param {object} object - Object to check collision against.\n * @param {number} object.x - X coordinate of the object.\n * @param {number} object.y - Y coordinate of the object.\n * @param {number} object.width - Width of the object.\n * @param {number} object.height - Height of the object.\n *\n * @returns {boolean} True if the object collides with a tile, false otherwise.\n */\n layerCollidesWith: function layerCollidesWith(name, object) {\n // handle non-kontra.sprite objects as well as kontra.sprite objects\n var x = (object.x !== undefined ? object.x : object.position.x);\n var y = (object.y !== undefined ? object.y : object.position.y);\n\n // calculate all tiles that the object can collide with\n var row = this._getRow(y);\n var col = this._getCol(x);\n\n var endRow = this._getRow(y + object.height);\n var endCol = this._getCol(x + object.width);\n\n // check all tiles\n var index;\n for (var r = row; r <= endRow; r++) {\n for (var c = col; c <= endCol; c++) {\n index = c + r * this.width;\n\n if (this.layers[name][index]) {\n return true;\n }\n }\n }\n\n return false;\n },\n\n /**\n * Get the tile from the specified layer at x, y.\n * @memberof kontra.tileEngine\n *\n * @param {string} name - Name of the layer.\n * @param {number} x - X coordinate of the tile.\n * @param {number} y - Y coordinate of the tile.\n *\n * @returns {number}\n */\n tileAtLayer: function tileAtLayer(name, x, y) {\n var row = this._getRow(y);\n var col = this._getCol(x);\n var index = col + row * this.width;\n\n return this.layers[name][index];\n },\n\n /**\n * Render the pre-rendered canvas.\n * @memberof kontra.tileEngine\n */\n render: function render() {\n var _this = this;\n\n // ensure sx and sy are within the image region\n _this.sx = Math.min( Math.max(_this.sx, 0), _this.sxMax );\n _this.sy = Math.min( Math.max(_this.sy, 0), _this.syMax );\n\n _this.context.drawImage(\n _this._offscreenCanvas,\n _this.sx, _this.sy, _this.canvasWidth, _this.canvasHeight,\n _this.x, _this.y, _this.canvasWidth, _this.canvasHeight\n );\n },\n\n /**\n * Render a specific layer.\n * @memberof kontra.tileEngine\n *\n * @param {string} name - Name of the layer to render.\n */\n renderLayer: function renderLayer(name) {\n var _this = this;\n\n var layer = _this.layers[name];\n\n // calculate the starting tile\n var row = _this._getRow();\n var col = _this._getCol();\n var index = col + row * _this.width;\n\n // calculate where to start drawing the tile relative to the drawing canvas\n var startX = col * _this.tileWidth - _this.sx;\n var startY = row * _this.tileHeight - _this.sy;\n\n // calculate how many tiles the drawing canvas can hold\n var viewWidth = Math.ceil(_this.canvasWidth / _this.tileWidth) + 1;\n var viewHeight = Math.ceil(_this.canvasHeight / _this.tileHeight) + 1;\n var numTiles = viewWidth * viewHeight;\n\n var count = 0;\n var x, y, tile, tileset, image, tileOffset, width, sx, sy;\n\n // draw just enough of the layer to fit inside the drawing canvas\n while (count < numTiles) {\n tile = layer[index];\n\n if (tile) {\n tileset = _this._getTileset(tile);\n image = tileset.image;\n\n x = startX + (count % viewWidth) * _this.tileWidth;\n y = startY + (count / viewWidth | 0) * _this.tileHeight;\n\n tileOffset = tile - tileset.firstGrid;\n width = image.width / _this.tileWidth;\n\n sx = (tileOffset % width) * _this.tileWidth;\n sy = (tileOffset / width | 0) * _this.tileHeight;\n\n _this.context.drawImage(\n image,\n sx, sy, _this.tileWidth, _this.tileHeight,\n x, y, _this.tileWidth, _this.tileHeight\n );\n }\n\n if (++count % viewWidth === 0) {\n index = col + (++row * _this.width);\n }\n else {\n index++;\n }\n }\n },\n\n /**\n * Get the row from the y coordinate.\n * @memberof kontra.tileEngine\n * @private\n *\n * @param {number} y - Y coordinate.\n *\n * @return {number}\n */\n _getRow: function getRow(y) {\n y = y || 0;\n\n return (this.sy + y) / this.tileHeight | 0;\n },\n\n /**\n * Get the col from the x coordinate.\n * @memberof kontra.tileEngine\n * @private\n *\n * @param {number} x - X coordinate.\n *\n * @return {number}\n */\n _getCol: function getCol(x) {\n x = x || 0;\n\n return (this.sx + x) / this.tileWidth | 0;\n },\n\n /**\n * Modified binary search that will return the tileset associated with the tile\n * @memberOf kontra.tileEngine\n * @private\n *\n * @param {number} tile - Tile grid.\n *\n * @return {object}\n */\n _getTileset: function getTileset(tile) {\n var min = 0;\n var max = this.tilesets.length - 1;\n var index;\n var currTile;\n\n while (min <= max) {\n index = (min + max) / 2 | 0;\n currTile = this.tilesets[index];\n\n if (tile >= currTile.firstGrid && tile <= currTile.lastGrid) {\n return currTile;\n }\n else if (currTile < tile) {\n min = index + 1;\n }\n else {\n max = index - 1;\n }\n }\n },\n\n /**\n * Pre-render the tiles to make drawing fast.\n */\n _preRenderImage: function preRenderImage() {\n var _this = this;\n var tile, tileset, image, x, y, sx, sy, tileOffset, width;\n\n // draw each layer in order\n for (var i = 0, layer; layer = _this.layers[_this._layerOrder[i]]; i++) {\n for (var j = 0, len = layer.length; j < len; j++) {\n tile = layer[j];\n\n // skip empty tiles (0)\n if (!tile) {\n continue;\n }\n\n tileset = _this._getTileset(tile);\n image = tileset.image;\n\n x = (j % _this.width) * _this.tileWidth;\n y = (j / _this.width | 0) * _this.tileHeight;\n\n tileOffset = tile - tileset.firstGrid;\n width = image.width / _this.tileWidth;\n\n sx = (tileOffset % width) * _this.tileWidth;\n sy = (tileOffset / width | 0) * _this.tileHeight;\n\n _this._offscreenContext.drawImage(\n image,\n sx, sy, _this.tileWidth, _this.tileHeight,\n x, y, _this.tileWidth, _this.tileHeight\n );\n }\n }\n }\n };\n\n return kontra;\n})(kontra || {}, Math);"],"sourceRoot":"/source/"} \ No newline at end of file +{"version":3,"sources":["kontraAssetLoader.js","core.js","gameLoop.js","keyboard.js","pool.js","quadtree.js","sprite.js","spriteSheet.js","store.js","tileEngine.js"],"names":["qFactory","nextTick","exceptionHandler","forEach","obj","iterator","context","key","isFunction","hasOwnProperty","call","isArray","length","defaultCallback","value","defaultErrback","reason","reject","all","promises","deferred","defer","counter","results","promise","ref","then","resolve","notify","toString","pending","val","callbacks","undefined","callback","i","ii","createInternalRejectedPromise","progress","errback","progressback","result","wrappedCallback","e","wrappedErrback","wrappedProgressback","push","catch","this","finally","makePromise","resolved","handleCallback","isResolved","callbackOutput","error","when","done","window","q","setTimeout","console","stack","kontra","isImage","isAudio","images","audios","data","assetPaths","audio","Audio","canUse","wav","mp3","canPlayType","replace","ogg","aac","m4a","getAssetExtension","url","substr","lastIndexOf","getAssetType","extension","match","getAssetName","loadAssets","type","numLoaded","numAssets","arguments","asset","Array","assetDeferred","loaded","total","loadImage","name","image","Image","onload","onerror","src","loadAudio","source","playableSource","addEventListener","preload","load","loadData","req","XMLHttpRequest","dataUrl","status","responseText","json","JSON","parse","open","send","bundles","createBundle","bundle","assets","loadBundles","apply","loadManifest","manifest","imagePath","audioPath","dataPath","Object","keys","document","init","properties","isString","canvas","getElementById","isCanvas","getElementsByTagName","ReferenceError","logError","getContext","game","width","height","message","noop","isNumber","HTMLImageElement","HTMLCanvasElement","timestamp","performance","now","Date","getTime","gameLoop","create","prototype","update","render","isStopped","_accumulator","_delta","fps","start","_last","requestAnimationFrame","_frame","bind","stop","cancelAnimationFrame","_rAF","_this","_now","_dt","normalizeKeyCode","which","keyCode","normalizeKeys","combination","trim","modifier","modifierOrder","indexOf","toLowerCase","shiftKeys","join","getKeyCombination","keyMap","keydownEventHandler","split","pressedKeys","preventDefault","keyupEventHandler","aliases","blurEventHandler",8,9,13,16,17,18,20,27,32,33,34,35,36,37,38,39,40,45,46,91,92,93,144,145,106,107,109,110,111,186,187,188,189,190,191,192,219,220,221,222,"String","fromCharCode","~","!","@","#","$","%","^","&","*","(",")","_","+",":","\"","<",">","?","|","plus","leftwindow","select","SyntaxError","unbind","pressed","pool","createProperties","isAlive","objects","size","maxSize","Infinity","lastIndex","inUse","fill","unshift","get","x","getAliveObjects","slice","clear","dt","index","Math","max","j","quadtree","depth","maxDepth","maxObjects","isBranchNode","parentNode","bounds","y","subnodes","object","indices","node","_getIndex","add","_addToSubnode","_split","verticalMidpoint","horizontalMidpoint","position","intersectsTopQuadrants","intersectsBottomQuadrants","subWidth","subHeight","strokeStyle","strokeRect","excludedProperties","vector","clamp","xMin","yMin","xMax","yMax","_xMin","_xMax","_yMin","_yMax","_x","_y","defineProperties","set","min","sprite","advanceSprite","velocity","acceleration","timeToLive","drawRect","fillStyle","color","fillRect","drawImage","advanceAnimation","currentAnimation","drawAnimation","playAnimation","animations","dx","dy","ddx","ddy","advance","draw","prop",{"end":{"file":"?","comments_before":[],"nlb":false,"endpos":60138,"endcol":9,"endline":2146,"pos":60137,"col":8,"line":2146,"value":"x","type":"name"},"start":{"file":"?","comments_before":[],"nlb":false,"endpos":60138,"endcol":9,"endline":2146,"pos":60137,"col":8,"line":2146,"value":"x","type":"name"},"name":"x"},"collidesWith","animation","spriteSheet","frames","frameSpeed","frame","currentFrame","row","framesPerRow","col","play","pause","frameWidth","frameHeight","createAnimations","sequence","_parseFrames","consecutiveFrames","map","Number","direction","localStorage","store","remove","setItem","stringify","getItem","removeItem","tileEngine","tileWidth","tileHeight","canvasWidth","canvasHeight","_offscreenCanvas","createElement","_offscreenContext","mapWidth","mapHeight","sxMax","syMax","layers","_layerOrder","tilesets","sx","sy","addTileset","firstGrid","numTiles","lastTileset","tiles","lastGrid","sort","a","b","addLayer","r","c","zIndex","_preRenderImage","layerCollidesWith","_getRow","_getCol","endRow","endCol","tileAtLayer","renderLayer","tile","tileset","tileOffset","layer","startX","startY","viewWidth","ceil","viewHeight","count","_getTileset","currTile","len"],"mappings":"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","file":"kontra.min.js","sourcesContent":["/**\n * The MIT License\n *\n * Copyright (c) 2010-2012 Google, Inc. http://angularjs.org\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\nwindow.q = qFactory(function(callback) {\n setTimeout(function() {\n callback();\n }, 0);\n}, function(e) {\n console.error('qLite: ' + e.stack);\n});\n\n/**\n * Constructs a promise manager.\n *\n * @param {function(Function)} nextTick Function for executing functions in the next turn.\n * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for\n * debugging purposes.\n * @returns {object} Promise manager.\n */\nfunction qFactory(nextTick, exceptionHandler) {\n var toString = ({}).toString;\n var isFunction = function isFunction(value){return typeof value == 'function';};\n var isArray = function isArray(value) {return toString.call(value) === '[object Array]';};\n\n function forEach(obj, iterator, context) {\n var key;\n if (obj) {\n if (isFunction(obj)) {\n for (key in obj) {\n // Need to check if hasOwnProperty exists,\n // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function\n if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {\n iterator.call(context, obj[key], key);\n }\n }\n } else if (obj.forEach && obj.forEach !== forEach) {\n obj.forEach(iterator, context);\n } else if (isArray(obj)) {\n for (key = 0; key < obj.length; key++)\n iterator.call(context, obj[key], key);\n } else {\n for (key in obj) {\n if (obj.hasOwnProperty(key)) {\n iterator.call(context, obj[key], key);\n }\n }\n }\n }\n return obj;\n }\n\n /**\n * @ngdoc method\n * @name $q#defer\n * @function\n *\n * @description\n * Creates a `Deferred` object which represents a task which will finish in the future.\n *\n * @returns {Deferred} Returns a new instance of deferred.\n */\n var defer = function() {\n var pending = [],\n value, deferred;\n\n deferred = {\n\n resolve: function(val) {\n if (pending) {\n var callbacks = pending;\n pending = undefined;\n value = ref(val);\n\n if (callbacks.length) {\n nextTick(function() {\n var callback;\n for (var i = 0, ii = callbacks.length; i < ii; i++) {\n callback = callbacks[i];\n value.then(callback[0], callback[1], callback[2]);\n }\n });\n }\n }\n },\n\n\n reject: function(reason) {\n deferred.resolve(createInternalRejectedPromise(reason));\n },\n\n\n notify: function(progress) {\n if (pending) {\n var callbacks = pending;\n\n if (pending.length) {\n nextTick(function() {\n var callback;\n for (var i = 0, ii = callbacks.length; i < ii; i++) {\n callback = callbacks[i];\n callback[2](progress);\n }\n });\n }\n }\n },\n\n\n promise: {\n then: function(callback, errback, progressback) {\n var result = defer();\n\n var wrappedCallback = function(value) {\n try {\n result.resolve((isFunction(callback) ? callback : defaultCallback)(value));\n } catch(e) {\n result.reject(e);\n exceptionHandler(e);\n }\n };\n\n var wrappedErrback = function(reason) {\n try {\n result.resolve((isFunction(errback) ? errback : defaultErrback)(reason));\n } catch(e) {\n result.reject(e);\n exceptionHandler(e);\n }\n };\n\n var wrappedProgressback = function(progress) {\n try {\n result.notify((isFunction(progressback) ? progressback : defaultCallback)(progress));\n } catch(e) {\n exceptionHandler(e);\n }\n };\n\n if (pending) {\n pending.push([wrappedCallback, wrappedErrback, wrappedProgressback]);\n } else {\n value.then(wrappedCallback, wrappedErrback, wrappedProgressback);\n }\n\n return result.promise;\n },\n\n \"catch\": function(callback) {\n return this.then(null, callback);\n },\n\n \"finally\": function(callback) {\n\n function makePromise(value, resolved) {\n var result = defer();\n if (resolved) {\n result.resolve(value);\n } else {\n result.reject(value);\n }\n return result.promise;\n }\n\n function handleCallback(value, isResolved) {\n var callbackOutput = null;\n try {\n callbackOutput = (callback ||defaultCallback)();\n } catch(e) {\n return makePromise(e, false);\n }\n if (callbackOutput && isFunction(callbackOutput.then)) {\n return callbackOutput.then(function() {\n return makePromise(value, isResolved);\n }, function(error) {\n return makePromise(error, false);\n });\n } else {\n return makePromise(value, isResolved);\n }\n }\n\n return this.then(function(value) {\n return handleCallback(value, true);\n }, function(error) {\n return handleCallback(error, false);\n });\n }\n }\n };\n\n return deferred;\n };\n\n\n var ref = function(value) {\n if (value && isFunction(value.then)) return value;\n return {\n then: function(callback) {\n var result = defer();\n nextTick(function() {\n result.resolve(callback(value));\n });\n return result.promise;\n }\n };\n };\n\n\n /**\n * @ngdoc method\n * @name $q#reject\n * @function\n *\n * @description\n * Creates a promise that is resolved as rejected with the specified `reason`. This api should be\n * used to forward rejection in a chain of promises. If you are dealing with the last promise in\n * a promise chain, you don't need to worry about it.\n *\n * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of\n * `reject` as the `throw` keyword in JavaScript. This also means that if you \"catch\" an error via\n * a promise error callback and you want to forward the error to the promise derived from the\n * current promise, you have to \"rethrow\" the error by returning a rejection constructed via\n * `reject`.\n *\n * ```js\n * promiseB = promiseA.then(function(result) {\n * // success: do something and resolve promiseB\n * // with the old or a new result\n * return result;\n * }, function(reason) {\n * // error: handle the error if possible and\n * // resolve promiseB with newPromiseOrValue,\n * // otherwise forward the rejection to promiseB\n * if (canHandle(reason)) {\n * // handle the error and recover\n * return newPromiseOrValue;\n * }\n * return $q.reject(reason);\n * });\n * ```\n *\n * @param {*} reason Constant, message, exception or an object representing the rejection reason.\n * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.\n */\n var reject = function(reason) {\n var result = defer();\n result.reject(reason);\n return result.promise;\n };\n\n var createInternalRejectedPromise = function(reason) {\n return {\n then: function(callback, errback) {\n var result = defer();\n nextTick(function() {\n try {\n result.resolve((isFunction(errback) ? errback : defaultErrback)(reason));\n } catch(e) {\n result.reject(e);\n exceptionHandler(e);\n }\n });\n return result.promise;\n }\n };\n };\n\n\n /**\n * @ngdoc method\n * @name $q#when\n * @function\n *\n * @description\n * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.\n * This is useful when you are dealing with an object that might or might not be a promise, or if\n * the promise comes from a source that can't be trusted.\n *\n * @param {*} value Value or a promise\n * @returns {Promise} Returns a promise of the passed value or promise\n */\n var when = function(value, callback, errback, progressback) {\n var result = defer(),\n done;\n\n var wrappedCallback = function(value) {\n try {\n return (isFunction(callback) ? callback : defaultCallback)(value);\n } catch (e) {\n exceptionHandler(e);\n return reject(e);\n }\n };\n\n var wrappedErrback = function(reason) {\n try {\n return (isFunction(errback) ? errback : defaultErrback)(reason);\n } catch (e) {\n exceptionHandler(e);\n return reject(e);\n }\n };\n\n var wrappedProgressback = function(progress) {\n try {\n return (isFunction(progressback) ? progressback : defaultCallback)(progress);\n } catch (e) {\n exceptionHandler(e);\n }\n };\n\n nextTick(function() {\n ref(value).then(function(value) {\n if (done) return;\n done = true;\n result.resolve(ref(value).then(wrappedCallback, wrappedErrback, wrappedProgressback));\n }, function(reason) {\n if (done) return;\n done = true;\n result.resolve(wrappedErrback(reason));\n }, function(progress) {\n if (done) return;\n result.notify(wrappedProgressback(progress));\n });\n });\n\n return result.promise;\n };\n\n\n function defaultCallback(value) {\n return value;\n }\n\n\n function defaultErrback(reason) {\n return reject(reason);\n }\n\n\n /**\n * @ngdoc method\n * @name $q#all\n * @function\n *\n * @description\n * Combines multiple promises into a single promise that is resolved when all of the input\n * promises are resolved.\n *\n * @param {Array.|Object.} promises An array or hash of promises.\n * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,\n * each value corresponding to the promise at the same index/key in the `promises` array/hash.\n * If any of the promises is resolved with a rejection, this resulting promise will be rejected\n * with the same rejection value.\n */\n function all(promises) {\n var deferred = defer(),\n counter = 0,\n results = isArray(promises) ? [] : {};\n\n forEach(promises, function(promise, key) {\n counter++;\n ref(promise).then(function(value) {\n if (results.hasOwnProperty(key)) return;\n results[key] = value;\n if (!(--counter)) deferred.resolve(results);\n }, function(reason) {\n if (results.hasOwnProperty(key)) return;\n deferred.reject(reason);\n }, function(reason) {\n if (results.hasOwnProperty(key)) return;\n deferred.notify(reason);\n });\n });\n\n if (counter === 0) {\n deferred.resolve(results);\n }\n\n return deferred.promise;\n }\n\n return {\n defer: defer,\n reject: reject,\n when: when,\n all: all\n };\n}\nvar kontra = (function(kontra) {\n var isImage = /(jpeg|jpg|gif|png)$/;\n var isAudio = /(wav|mp3|ogg|aac|m4a)$/;\n var folderSeparator = /(\\\\|\\/)/g;\n\n // all assets are stored by name as well as by URL\n kontra.images = {};\n kontra.audios = {};\n kontra.data = {};\n\n // base asset path for determining asset URLs\n kontra.assetPaths = {\n images: '',\n audios: '',\n data: '',\n };\n\n // audio playability\n // @see https://github.com/Modernizr/Modernizr/blob/master/feature-detects/audio.js\n var audio = new Audio();\n kontra.canUse = kontra.canUse || {};\n kontra.canUse.wav = '';\n kontra.canUse.mp3 = audio.canPlayType('audio/mpeg;').replace(/^no$/,'');\n kontra.canUse.ogg = audio.canPlayType('audio/ogg; codecs=\"vorbis\"').replace(/^no$/,'');\n kontra.canUse.aac = audio.canPlayType('audio/aac;').replace(/^no$/,'');\n kontra.canUse.m4a = (audio.canPlayType('audio/x-m4a;') || kontra.canUse.aac).replace(/^no$/,'');\n\n /**\n * Get the extension of an asset.\n * @see http://jsperf.com/extract-file-extension\n * @memberOf kontra\n *\n * @param {string} url - The URL to the asset.\n *\n * @returns {string}\n */\n kontra.getAssetExtension = function getAssetExtension(url) {\n return url.substr((~-url.lastIndexOf(\".\") >>> 0) + 2);\n };\n\n /**\n * Get the type of asset based on its extension.\n * @memberOf kontra\n *\n * @param {string} url - The URL to the asset.\n *\n * @returns {string} Image, Audio, Data.\n */\n kontra.getAssetType = function getAssetType(url) {\n var extension = this.getAssetExtension(url);\n\n if (extension.match(isImage)) {\n return 'Image';\n }\n else if (extension.match(isAudio)) {\n return 'Audio';\n }\n else {\n return 'Data';\n }\n };\n\n /**\n * Get the name of an asset.\n * @memberOf kontra\n *\n * @param {string} url - The URL to the asset.\n *\n * @returns {string}\n */\n kontra.getAssetName = function getAssetName(url) {\n return url.replace(/\\.[^/.]+$/, \"\");\n };\n\n return kontra;\n})(kontra || {});\n/*jshint -W084 */\n\nvar kontra = (function(kontra, q) {\n /**\n * Load an Image, Audio, or data file.\n * @memberOf kontra\n *\n * @param {string|string[]} - Comma separated list of assets to load.\n *\n * @returns {Promise} A deferred promise.\n *\n * @example\n * kontra.loadAsset('car.png');\n * kontra.loadAsset(['explosion.mp3', 'explosion.ogg']);\n * kontra.loadAsset('bio.json');\n * kontra.loadAsset('car.png', ['explosion.mp3', 'explosion.ogg'], 'bio.json');\n */\n kontra.loadAssets = function loadAsset() {\n var deferred = q.defer();\n var promises = [];\n var numLoaded = 0;\n var numAssets = arguments.length;\n var type, name, url;\n\n if (!arguments.length) {\n deferred.resolve();\n }\n\n for (var i = 0, asset; asset = arguments[i]; i++) {\n if (!Array.isArray(asset)) {\n url = asset;\n }\n else {\n url = asset[0];\n }\n\n type = this.getAssetType(url);\n\n // create a closure for event binding\n (function(assetDeferred) {\n promises.push(assetDeferred.promise);\n\n kontra['load' + type](url).then(\n function loadAssetSuccess() {\n assetDeferred.resolve();\n deferred.notify({'loaded': ++numLoaded, 'total': numAssets});\n },\n function loadAssetError(error) {\n assetDeferred.reject(error);\n });\n })(q.defer());\n }\n\n q.all(promises).then(\n function loadAssetsSuccess() {\n deferred.resolve();\n },\n function loadAssetsError(error) {\n deferred.reject(error);\n });\n\n return deferred.promise;\n };\n\n /**\n * Load an Image file. Uses assetPaths.images to resolve URL.\n * @memberOf kontra\n *\n * @param {string} url - The URL to the Image file.\n *\n * @returns {Promise} A deferred promise. Promise resolves with the Image.\n *\n * @example\n * kontra.loadImage('car.png');\n * kontra.loadImage('autobots/truck.png');\n */\n kontra.loadImage = function(url) {\n var deferred = q.defer();\n var name = this.getAssetName(url);\n var image = new Image();\n\n url = this.assetPaths.images + url;\n\n image.onload = function loadImageOnLoad() {\n kontra.images[name] = kontra.images[url] = this;\n deferred.resolve(this);\n };\n\n image.onerror = function loadImageOnError() {\n deferred.reject('Unable to load image ' + url);\n };\n\n image.src = url;\n\n return deferred.promise;\n };\n\n /**\n * Load an Audio file. Supports loading multiple audio formats which will be resolved by\n * the browser in the order listed. Uses assetPaths.audios to resolve URL.\n * @memberOf kontra\n *\n * @param {string|string[]} url - The URL to the Audio file.\n *\n * @returns {Promise} A deferred promise. Promise resolves with the Audio.\n *\n * @example\n * kontra.loadAudio('sound_effects/laser.mp3');\n * kontra.loadAudio(['explosion.mp3', 'explosion.m4a', 'explosion.ogg']);\n *\n * There are two ways to load Audio in the web: HTML5 Audio or the Web Audio API.\n * HTML5 Audio has amazing browser support, including back to IE9\n * (http://caniuse.com/#feat=audio). However, the web Audio API isn't supported in\n * IE nor Android Browsers (http://caniuse.com/#search=Web%20Audio%20API).\n *\n * To support the most browsers we'll use HTML5 Audio. However, doing so means we'll\n * have to work around mobile device limitations as well as Audio implementation\n * limitations.\n *\n * Android browsers require playing Audio through user interaction whereas iOS 6+ can\n * play through normal JavaScript. Moreover, Android can only play one sound source at\n * a time whereas iOS 6+ can handle more than one. See this article for more details\n * (http://pupunzi.open-lab.com/2013/03/13/making-html5-audio-actually-work-on-mobile/)\n *\n * Both iOS and Android will download an Audio through JavaScript, but neither will play\n * it until user interaction. You can get around this issue by having a splash screen\n * that requires user interaction to start the game and using that event to play the audio.\n * (http://jsfiddle.net/straker/5dsm6jgt/)\n */\n kontra.loadAudio = function(url) {\n var deferred = q.defer();\n var source, name, playableSource, audio;\n\n if (!Array.isArray(url)) {\n url = [url];\n }\n\n // determine which audio format the browser can play\n for (var i = 0; source = url[i]; i++) {\n if ( this.canUse[this.getAssetExtension(source)] ) {\n playableSource = source;\n break;\n }\n }\n\n if (!playableSource) {\n deferred.reject('Browser cannot play any of the audio formats provided');\n }\n else {\n name = this.getAssetName(playableSource);\n audio = new Audio();\n\n source = this.assetPaths.audios + playableSource;\n\n audio.addEventListener('canplay', function loadAudioOnLoad() {\n kontra.audios[name] = kontra.audios[source] = this;\n deferred.resolve(this);\n });\n\n audio.onerror = function loadAudioOnError() {\n deferred.reject('Unable to load audio ' + source);\n };\n\n audio.src = source;\n audio.preload = 'auto';\n audio.load();\n }\n\n return deferred.promise;\n };\n\n\n /**\n * Load a data file (be it text or JSON). Uses assetPaths.data to resolve URL.\n * @memberOf kontra\n *\n * @param {string} url - The URL to the data file.\n *\n * @returns {Promise} A deferred promise. Resolves with the data or parsed JSON.\n *\n * @example\n * kontra.loadData('bio.json');\n * kontra.loadData('dialog.txt');\n */\n kontra.loadData = function(url) {\n var deferred = q.defer();\n var req = new XMLHttpRequest();\n var name = this.getAssetName(url);\n var dataUrl = this.assetPaths.data + url;\n\n req.addEventListener('load', function loadDataOnLoad() {\n if (req.status !== 200) {\n deferred.reject(req.responseText);\n return;\n }\n\n try {\n var json = JSON.parse(req.responseText);\n kontra.data[name] = kontra.data[dataUrl] = json;\n\n deferred.resolve(json);\n }\n catch(e) {\n var data = req.responseText;\n kontra.data[name] = kontra.data[dataUrl] = data;\n\n deferred.resolve(data);\n }\n });\n\n req.open('GET', dataUrl, true);\n req.send();\n\n return deferred.promise;\n };\n\n return kontra;\n})(kontra || {}, q);\n/*jshint -W084 */\n\nvar kontra = (function(kontra, q) {\n kontra.bundles = {};\n\n /**\n * Create a group of assets that can be loaded using kontra.loadBundle().\n * @memberOf kontra\n *\n * @param {string} bundle - The name of the bundle.\n * @param {string[]} assets - Assets to add to the bundle.\n *\n * @example\n * kontra.createBundle('myBundle', ['car.png', ['explosion.mp3', 'explosion.ogg']]);\n */\n kontra.createBundle = function createBundle(bundle, assets) {\n if (this.bundles[bundle]) {\n return;\n }\n\n this.bundles[bundle] = assets || [];\n };\n\n /**\n * Load all assets that are part of a bundle.\n * @memberOf kontra\n *\n * @param {string|string[]} - Comma separated list of bundles to load.\n *\n * @returns {Promise} A deferred promise.\n *\n * @example\n * kontra.loadBundles('myBundle');\n * kontra.loadBundles('myBundle', 'myOtherBundle');\n */\n kontra.loadBundles = function loadBundles() {\n var deferred = q.defer();\n var promises = [];\n var numLoaded = 0;\n var numAssets = 0;\n var assets;\n\n for (var i = 0, bundle; bundle = arguments[i]; i++) {\n if (!(assets = this.bundles[bundle])) {\n deferred.reject('Bundle \\'' + bundle + '\\' has not been created.');\n continue;\n }\n\n numAssets += assets.length;\n\n promises.push(this.loadAssets.apply(this, assets));\n }\n\n q.all(promises).then(\n function loadBundlesSuccess() {\n deferred.resolve();\n },\n function loadBundlesError(error) {\n deferred.reject(error);\n },\n function loadBundlesNofity() {\n deferred.notify({'loaded': ++numLoaded, 'total': numAssets});\n });\n\n return deferred.promise;\n };\n\n return kontra;\n})(kontra || {}, q);\n/*jshint -W084 */\n\nvar kontra = (function(kontra, q) {\n /**\n * Load an asset manifest file.\n * @memberOf kontra\n *\n * @param {string} url - The URL to the asset manifest file.\n *\n * @returns {Promise} A deferred promise.\n */\n kontra.loadManifest = function loadManifest(url) {\n var deferred = q.defer();\n var bundles;\n\n kontra.loadData(url).then(\n function loadManifestSuccess(manifest) {\n kontra.assetPaths.images = manifest.imagePath || '';\n kontra.assetPaths.audios = manifest.audioPath || '';\n kontra.assetPaths.data = manifest.dataPath || '';\n\n // create bundles and add assets\n for (var i = 0, bundle; bundle = manifest.bundles[i]; i++) {\n kontra.createBundle(bundle.name, bundle.assets);\n }\n\n if (!manifest.loadBundles) {\n deferred.resolve();\n return;\n }\n\n // load all bundles\n if (manifest.loadBundles === 'all') {\n bundles = Object.keys(kontra.bundles || {});\n }\n // load a single bundle\n else if (!Array.isArray(manifest.loadBundles)) {\n bundles = [manifest.loadBundles];\n }\n // load multiple bundles\n else {\n bundles = manifest.loadBundles;\n }\n\n kontra.loadBundles.apply(kontra, bundles).then(\n function loadBundlesSuccess() {\n deferred.resolve();\n },\n function loadBundlesError(error) {\n deferred.reject(error);\n },\n function loadBundlesNotify(progress) {\n deferred.notify(progress);\n });\n },\n function loadManifestError(error) {\n deferred.reject(error);\n });\n\n return deferred.promise;\n };\n\n return kontra;\n})(kontra || {}, q);","/* global console */\n\nvar kontra = (function(kontra, document) {\n 'use strict';\n\n /**\n * Initialize the canvas.\n * @memberof kontra\n *\n * @param {object} properties - Properties for the game.\n * @param {string|Canvas} properties.canvas - Main canvas ID or Element for the game.\n */\n kontra.init = function init(properties) {\n properties = properties || {};\n\n if (kontra.isString(properties.canvas)) {\n this.canvas = document.getElementById(properties.canvas);\n }\n else if (kontra.isCanvas(properties.canvas)) {\n this.canvas = properties.canvas;\n }\n else {\n this.canvas = document.getElementsByTagName('canvas')[0];\n\n if (!this.canvas) {\n var error = new ReferenceError('No canvas element found.');\n kontra.logError(error, 'You must provide a canvas element for the game.');\n return;\n }\n }\n\n this.context = this.canvas.getContext('2d');\n this.game = {\n width: this.canvas.width,\n height: this.canvas.height\n };\n };\n\n /**\n * Throw an error message to the user with readable formating.\n * @memberof kontra\n *\n * @param {Error} error - Error object.\n * @param {string} message - Error message.\n */\n kontra.logError = function logError(error, message) {\n console.error('Kontra: ' + message + '\\n\\t' + error.stack);\n };\n\n /**\n * Noop function.\n * @memberof kontra\n */\n kontra.noop = function noop() {};\n\n /**\n * Determine if a value is an Array.\n * @memberof kontra\n *\n * @param {*} value - Value to test.\n *\n * @returns {boolean}\n */\n kontra.isArray = Array.isArray;\n\n /**\n * Determine if a value is a String.\n * @memberof kontra\n *\n * @param {*} value - Value to test.\n *\n * @returns {boolean}\n */\n kontra.isString = function isString(value) {\n return typeof value === 'string';\n };\n\n /**\n * Determine if a value is a Number.\n * @memberof kontra\n *\n * @param {*} value - Value to test.\n *\n * @returns {boolean}\n */\n kontra.isNumber = function isNumber(value) {\n return typeof value === 'number';\n };\n\n /**\n * Determine if a value is an Image.\n * @memberof kontra\n *\n * @param {*} value - Value to test.\n *\n * @returns {boolean}\n */\n kontra.isImage = function isImage(value) {\n return value instanceof HTMLImageElement;\n };\n\n /**\n * Determine if a value is a Canvas.\n * @memberof kontra\n *\n * @param {*} value - Value to test.\n *\n * @returns {boolean}\n */\n kontra.isCanvas = function isCanvas(value) {\n return value instanceof HTMLCanvasElement;\n };\n\n return kontra;\n})(kontra || {}, document);","var kontra = (function(kontra, window) {\n 'use strict';\n\n /**\n * Get the current time. Uses the User Timing API if it's available or defaults to using\n * Date().getTime()\n * @memberof kontra\n *\n * @returns {number}\n */\n kontra.timestamp = (function() {\n if (window.performance && window.performance.now) {\n return function timestampPerformance() {\n return window.performance.now();\n };\n }\n else {\n return function timestampDate() {\n return new Date().getTime();\n };\n }\n })();\n\n /**\n * Game loop that updates and renders the game every frame.\n * @memberof kontra\n *\n * @see kontra.gameLoop.prototype.init for list of parameters.\n */\n kontra.gameLoop = function(properties) {\n var gameLoop = Object.create(kontra.gameLoop.prototype);\n gameLoop.init(properties);\n\n return gameLoop;\n };\n\n kontra.gameLoop.prototype = {\n /**\n * Initialize properties on the game loop.\n * @memberof kontra.gameLoop\n *\n * @param {object} properties - Configure the game loop.\n * @param {number} [properties.fps=60] - Desired frame rate.\n * @param {function} properties.update - Function called to update the game.\n * @param {function} properties.render - Function called to render the game.\n */\n init: function init(properties) {\n properties = properties || {};\n\n // check for required functions\n if (typeof properties.update !== 'function' || typeof properties.render !== 'function') {\n var error = new ReferenceError('Required functions not found');\n kontra.logError(error, 'You must provide update() and render() functions to create a game loop.');\n return;\n }\n\n this.isStopped = false;\n\n // animation variables\n this._accumulator = 0;\n this._delta = 1E3 / (properties.fps || 60);\n\n this.update = properties.update;\n this.render = properties.render;\n },\n\n /**\n * Start the game loop.\n * @memberof kontra.gameLoop\n */\n start: function start() {\n this._last = kontra.timestamp();\n this.isStopped = false;\n requestAnimationFrame(this._frame.bind(this));\n },\n\n /**\n * Stop the game loop.\n */\n stop: function stop() {\n this.isStopped = true;\n cancelAnimationFrame(this._rAF);\n },\n\n /**\n * Called every frame of the game loop.\n * @memberof kontra.gameLoop\n * @private\n */\n _frame: function frame() {\n var _this = this;\n\n _this._rAF = requestAnimationFrame(_this._frame.bind(_this));\n\n _this._now = kontra.timestamp();\n _this._dt = _this._now - _this._last;\n _this._last = _this._now;\n\n // prevent updating the game with a very large dt if the game were to lose focus\n // and then regain focus later\n if (_this._dt > 1E3) {\n return;\n }\n\n _this._accumulator += _this._dt;\n\n while (_this._accumulator >= _this._delta) {\n _this.update(_this._delta / 1E3);\n\n _this._accumulator -= _this._delta;\n }\n\n _this.render();\n }\n };\n\n return kontra;\n})(kontra || {}, window);","/*jshint -W084 */\n\nvar kontra = (function(kontra, window) {\n 'use strict';\n\n var callbacks = {};\n var pressedKeys = {};\n\n var keyMap = {\n // named keys\n 8: 'backspace',\n 9: 'tab',\n 13: 'enter',\n 16: 'shift',\n 17: 'ctrl',\n 18: 'alt',\n 20: 'capslock',\n 27: 'esc',\n 32: 'space',\n 33: 'pageup',\n 34: 'pagedown',\n 35: 'end',\n 36: 'home',\n 37: 'left',\n 38: 'up',\n 39: 'right',\n 40: 'down',\n 45: 'insert',\n 46: 'delete',\n 91: 'leftwindow',\n 92: 'rightwindow',\n 93: 'select',\n 144: 'numlock',\n 145: 'scrolllock',\n\n // special characters\n 106: '*',\n 107: '+',\n 109: '-',\n 110: '.',\n 111: '/',\n 186: ';',\n 187: '=',\n 188: ',',\n 189: '-',\n 190: '.',\n 191: '/',\n 192: '`',\n 219: '[',\n 220: '\\\\',\n 221: ']',\n 222: '\\''\n };\n\n // alpha keys\n for (var i = 0; i < 26; i++) {\n keyMap[65+i] = String.fromCharCode(65+i).toLowerCase();\n }\n // numeric keys\n for (i = 0; i < 10; i++) {\n keyMap[48+i] = ''+i;\n }\n // f keys\n for (i = 1; i < 20; i++) {\n keyMap[111+i] = 'f'+i;\n }\n // keypad\n for (i = 0; i < 10; i++) {\n keyMap[96+i] = 'numpad'+i;\n }\n\n // shift keys mapped to their non-shift equivalent\n var shiftKeys = {\n '~': '`',\n '!': '1',\n '@': '2',\n '#': '3',\n '$': '4',\n '%': '5',\n '^': '6',\n '&': '7',\n '*': '8',\n '(': '9',\n ')': '0',\n '_': '-',\n '+': '=',\n ':': ';',\n '\"': '\\'',\n '<': ',',\n '>': '.',\n '?': '/',\n '|': '\\\\',\n 'plus': '='\n };\n\n // aliases modifier keys to their actual key for keyup event\n var aliases = {\n 'leftwindow': 'meta', // mac\n 'select': 'meta' // mac\n };\n\n // modifier order for combinations\n var modifierOrder = ['meta', 'ctrl', 'alt', 'shift'];\n\n window.addEventListener('keydown', keydownEventHandler);\n window.addEventListener('keyup', keyupEventHandler);\n window.addEventListener('blur', blurEventHandler);\n\n /**\n * Object for using the keyboard.\n */\n kontra.keys = {};\n\n /**\n * Register a function to be called on a keyboard keys.\n * Please note that not all keyboard combinations can be executed due to ghosting.\n * @memberof kontra.keys\n *\n * @param {string|string[]} keys - keys combination string(s).\n *\n * @throws {SyntaxError} If callback is not a function.\n */\n kontra.keys.bind = function bindKey(keys, callback) {\n if (typeof callback !== 'function') {\n var error = new SyntaxError('Invalid function.');\n kontra.logError(error, 'You must provide a function as the second parameter.');\n return;\n }\n\n keys = (kontra.isArray(keys) ? keys : [keys]);\n\n for (var i = 0, key; key = keys[i]; i++) {\n var combination = normalizeKeys(key);\n\n callbacks[combination] = callback;\n }\n };\n\n /**\n * Remove the callback function for a key combination.\n * @memberof kontra.keys\n *\n * @param {string|string[]} keys - keys combination string.\n */\n kontra.keys.unbind = function unbindKey(keys) {\n keys = (kontra.isArray(keys) ? keys : [keys]);\n\n for (var i = 0, key; key = keys[i]; i++) {\n var combination = normalizeKeys(key);\n\n callbacks[combination] = undefined;\n }\n };\n\n /**\n * Returns whether a key is pressed.\n * @memberof kontra.keys\n *\n * @param {string} keys - Keys combination string.\n *\n * @returns {boolean}\n */\n kontra.keys.pressed = function keyPressed(keys) {\n var combination = normalizeKeys(keys);\n var pressed = true;\n\n // loop over each key in the combination and verify that it is pressed\n keys = combination.split('+');\n for (var i = 0, key; key = keys[i]; i++) {\n pressed = pressed && !!pressedKeys[key];\n }\n\n return pressed;\n };\n\n /**\n * Normalize the event keycode\n * @private\n *\n * @param {Event} e\n *\n * @returns {number}\n */\n function normalizeKeyCode(e) {\n return (typeof e.which === 'number' ? e.which : e.keyCode);\n }\n\n /**\n * Normalize keys combination order.\n * @private\n *\n * @param {string} keys - keys combination string.\n *\n * @returns {string} Normalized combination.\n *\n * @example\n * normalizeKeys('c+ctrl'); //=> 'ctrl+c'\n * normalizeKeys('shift+++meta+alt'); //=> 'meta+alt+shift+plus'\n */\n function normalizeKeys(keys) {\n var combination = [];\n\n // handle '++' combinations\n keys = keys.trim().replace('++', '+plus');\n\n // put modifiers in the correct order\n for (var i = 0, modifier; modifier = modifierOrder[i]; i++) {\n\n // check for the modifier\n if (keys.indexOf(modifier) !== -1) {\n combination.push(modifier);\n keys = keys.replace(modifier, '');\n }\n }\n\n // remove all '+'s to leave only the last key\n keys = keys.replace(/\\+/g, '').toLowerCase();\n\n // check for shift key\n if (shiftKeys[keys]) {\n combination.push('shift+'+shiftKeys[keys]);\n }\n else if (keys) {\n combination.push(keys);\n }\n\n return combination.join('+');\n }\n\n /**\n * Get the key combination from an event.\n * @private\n *\n * @param {Event} e\n *\n * @return {string} normalized combination.\n */\n function getKeyCombination(e) {\n var combination = [];\n\n // check for modifiers\n for (var i = 0, modifier; modifier = modifierOrder[i]; i++) {\n if (e[modifier+'Key']) {\n combination.push(modifier);\n }\n }\n\n var key = keyMap[normalizeKeyCode(e)];\n\n // prevent duplicate keys from being added to the combination\n // for example 'ctrl+ctrl' since ctrl is both a modifier and\n // a regular key\n if (combination.indexOf(key) === -1) {\n combination.push(key);\n }\n\n return combination.join('+');\n }\n\n /**\n * Execute a function that corresponds to a keyboard combination.\n * @private\n *\n * @param {Event} e\n */\n function keydownEventHandler(e) {\n var combination = getKeyCombination(e);\n\n // set pressed keys\n for (var i = 0, keys = combination.split('+'), key; key = keys[i]; i++) {\n pressedKeys[key] = true;\n }\n\n if (callbacks[combination]) {\n callbacks[combination](e, combination);\n e.preventDefault();\n }\n }\n\n /**\n * Set the released key to not being pressed.\n * @private\n *\n * @param {Event} e\n */\n function keyupEventHandler(e) {\n var key = keyMap[normalizeKeyCode(e)];\n pressedKeys[key] = false;\n\n if (aliases[key]) {\n pressedKeys[ aliases[key] ] = false;\n }\n }\n\n /**\n * Reset pressed keys.\n * @private\n *\n * @param {Event} e\n */\n function blurEventHandler(e) {\n pressedKeys = {};\n }\n\n return kontra;\n})(kontra || {}, window);","/*jshint -W084 */\n\nvar kontra = (function(kontra) {\n 'use strict';\n\n /**\n * Object pool. The pool will grow in size to accommodate as many objects as are needed.\n * Unused items are at the front of the pool and in use items are at the of the pool.\n * @memberof kontra\n *\n * @see kontra.pool.prototype.init for list of parameters.\n */\n kontra.pool = function(properties) {\n var pool = Object.create(kontra.pool.prototype);\n pool.init(properties);\n\n return pool;\n };\n\n kontra.pool.prototype = {\n /**\n * Initialize properties on the pool.\n * @memberof kontra.pool\n *\n * @param {object} properties - Properties of the pool.\n * @param {object} properties.create - Function that returns the object to use in the pool.\n * @param {object} properties.createProperties - Properties that will be passed to the create function.\n * @param {number} properties.maxSize - The maximum size that the pool will grow to.\n * @param {boolean} properties.fill - Fill the pool to max size instead of slowly growing.\n *\n * Objects inside the pool must implement render(), update(),\n * init(), and isAlive() functions.\n */\n init: function init(properties) {\n properties = properties || {};\n\n var error, obj;\n\n // check for the correct structure of the objects added to pools so we know that the\n // rest of the pool code will work without errors\n if (typeof properties.create !== 'function') {\n error = new SyntaxError('Required function not found.');\n kontra.logError(error, 'Parameter \\'create\\' must be a function that returns an object.');\n return;\n }\n\n // bind the create function to always use the create properties\n this.create = properties.create.bind(this, properties.createProperties || {});\n\n // ensure objects for the pool have required functions\n obj = this.create();\n\n if (!obj || typeof obj.render !== 'function' || typeof obj.update !== 'function' ||\n typeof obj.init !== 'function' || typeof obj.isAlive !== 'function') {\n error = new SyntaxError('Create object required functions not found.');\n kontra.logError(error, 'Objects to be pooled must implement render(), update(), init() and isAlive() functions.');\n return;\n }\n\n // start the pool with an object\n this.objects = [obj];\n this.size = 1;\n this.maxSize = properties.maxSize || Infinity;\n this.lastIndex = 0;\n this.inUse = 0;\n\n // fill the pool\n if (properties.fill) {\n if (properties.maxSize) {\n while (this.objects.length < this.maxSize) {\n this.objects.unshift(this.create());\n }\n\n this.size = this.maxSize;\n this.lastIndex = this.maxSize - 1;\n }\n else {\n error = new SyntaxError('Required property not found.');\n kontra.logError(error, 'Parameter \\'maxSize\\' must be set before you can fill a pool.');\n return;\n }\n }\n },\n\n /**\n * Get an object from the pool.\n * @memberof kontra.pool\n *\n * @param {object} properties - Properties to pass to object.init().\n */\n get: function get(properties) {\n properties = properties || {};\n\n var _this = this;\n\n // the pool is out of objects if the first object is in use and it can't grow\n if (_this.objects[0].isAlive()) {\n if (_this.size === _this.maxSize) {\n return;\n }\n // 'double' the size of the array by filling it with twice as many objects\n else {\n for (var x = 0; x < _this.size && _this.objects.length < _this.maxSize; x++) {\n _this.objects.unshift(_this.create());\n }\n\n _this.size = _this.objects.length;\n _this.lastIndex = _this.size - 1;\n }\n }\n\n // save off first object in pool to reassign to last object after unshift\n var obj = _this.objects[0];\n obj.init(properties);\n\n // unshift the array\n for (var i = 1; i < _this.size; i++) {\n _this.objects[i-1] = _this.objects[i];\n }\n\n _this.objects[_this.lastIndex] = obj;\n _this.inUse++;\n },\n\n /**\n * Return all objects that are alive from the pool.\n * @memberof kontra.pool\n *\n * @returns {object[]}\n */\n getAliveObjects: function getAliveObjects() {\n return this.objects.slice(this.objects.length - this.inUse);\n },\n\n /**\n * Clear the object pool.\n * @memberof kontra.pool\n */\n clear: function clear() {\n this.inUse = 0;\n this.size = 1;\n this.lastIndex = 0;\n this.objects.length = 0;\n this.objects.push(this.create());\n },\n\n /**\n * Update all alive pool objects.\n * @memberof kontra.pool\n *\n * @param {number} dt - Time since last update.\n */\n update: function update(dt) {\n var i = this.lastIndex;\n var obj;\n\n // If the user kills an object outside of the update cycle, the pool won't know of\n // the change until the next update and inUse won't be decremented. If the user then\n // gets an object when inUse is the same size as objects.length, inUse will increment\n // and this statement will evaluate to -1.\n //\n // I don't like having to go through the pool to kill an object as it forces you to\n // know which object came from which pool. Instead, we'll just prevent the index from\n // going below 0 and accept the fact that inUse may be out of sync for a frame.\n var index = Math.max(this.objects.length - this.inUse, 0);\n\n // only iterate over the objects that are alive\n while (i >= index) {\n obj = this.objects[i];\n\n obj.update(dt);\n\n // if the object is dead, move it to the front of the pool\n if (!obj.isAlive()) {\n\n // push an object from the middle of the pool to the front of the pool\n // without returning a new array through Array#splice to avoid garbage\n // collection of the old array\n // @see http://jsperf.com/object-pools-array-vs-loop\n for (var j = i; j > 0; j--) {\n this.objects[j] = this.objects[j-1];\n }\n\n this.objects[0] = obj;\n this.inUse--;\n index++;\n }\n else {\n i--;\n }\n }\n },\n\n /**\n * render all alive pool objects.\n * @memberof kontra.pool\n */\n render: function render() {\n var index = Math.max(this.objects.length - this.inUse, 0);\n\n for (var i = this.lastIndex; i >= index; i--) {\n this.objects[i].render();\n }\n }\n };\n\n return kontra;\n})(kontra || {});","/*jshint -W084 */\n\nvar kontra = (function(kontra, undefined) {\n 'use strict';\n\n /**\n * A quadtree for 2D collision checking. The quadtree acts like an object pool in that it\n * will create subnodes as objects are needed but it won't clean up the subnodes when it\n * collapses to avoid garbage collection.\n * @memberof kontra\n *\n * @see kontra.quadtree.prototype.init for list of parameters.\n *L\n * The quadrant indices are numbered as follows (following a z-order curve):\n * |\n * 0 | 1\n * ----+----\n * 2 | 3\n * |\n */\n kontra.quadtree = function(properties) {\n var quadtree = Object.create(kontra.quadtree.prototype);\n quadtree.init(properties);\n\n return quadtree;\n };\n\n kontra.quadtree.prototype = {\n /**\n * Initialize properties on the quadtree.\n * @memberof kontra.quadtree\n *\n * @param {number} [depth=0] - Current node depth.\n * @param {number} [maxDepth=3] - Maximum node depths the quadtree can have.\n * @param {number} [maxObjects=25] - Maximum number of objects a node can support before splitting.\n * @param {object} [parentNode] - The node that contains this node.\n * @param {object} [bounds] - The 2D space this node occupies.\n */\n init: function init(properties) {\n properties = properties || {};\n\n this.depth = properties.depth || 0;\n this.maxDepth = properties.maxDepth || 3;\n this.maxObjects = properties.maxObjects || 25;\n\n // since we won't clean up any subnodes, we need to keep track of which nodes are\n // currently the leaf node so we know which nodes to add objects to\n this.isBranchNode = false;\n\n this.parentNode = properties.parentNode;\n\n this.bounds = properties.bounds || {\n x: 0,\n y: 0,\n width: kontra.game.width,\n height: kontra.game.height\n };\n\n this.objects = [];\n this.subnodes = [];\n },\n\n /**\n * Clear the quadtree\n * @memberof kontra.quadtree\n */\n clear: function clear() {\n if (this.isBranchNode) {\n for (var i = 0; i < 4; i++) {\n this.subnodes[i].clear();\n }\n }\n\n this.isBranchNode = false;\n this.objects.length = 0;\n },\n\n /**\n * Find the leaf node the object belongs to and get all objects that are part of\n * that node.\n * @memberof kontra.quadtree\n *\n * @param {object} object - Object to use for finding the leaf node.\n *\n * @returns {object[]} A list of objects in the same leaf node as the object.\n */\n get: function get(object) {\n var node = this;\n var objects = [];\n var indices, index;\n\n // traverse the tree until we get to a leaf node\n while (node.subnodes.length && this.isBranchNode) {\n indices = this._getIndex(object);\n\n for (var i = 0, length = indices.length; i < length; i++) {\n index = indices[i];\n\n objects.push.apply(objects, this.subnodes[index].get(object));\n }\n\n return objects;\n }\n\n return node.objects;\n },\n\n /**\n * Add an object to the quadtree. Once the number of objects in the node exceeds\n * the maximum number of objects allowed, it will split and move all objects to their\n * corresponding subnodes.\n * @memberof kontra.quadtree\n */\n add: function add() {\n var _this = this;\n var i, object, obj, indices, index;\n\n for (var j = 0, length = arguments.length; j < length; j++) {\n object = arguments[j];\n\n // add a group of objects separately\n if (kontra.isArray(object)) {\n _this.add.apply(this, object);\n\n continue;\n }\n\n // current node has subnodes, so we need to add this object into a subnode\n if (_this.subnodes.length && _this.isBranchNode) {\n _this._addToSubnode(object);\n\n continue;\n }\n\n // this node is a leaf node so add the object to it\n _this.objects.push(object);\n\n // split the node if there are too many objects\n if (_this.objects.length > _this.maxObjects && _this.depth < _this.maxDepth) {\n _this._split();\n\n // move all objects to their corresponding subnodes\n for (i = 0; obj = _this.objects[i]; i++) {\n _this._addToSubnode(obj);\n }\n\n _this.objects.length = 0;\n }\n }\n },\n\n /**\n * Add an object to a subnode.\n * @memberof kontra.quadtree\n * @private\n *\n * @param {object} object - Object to add into a subnode\n */\n _addToSubnode: function _addToSubnode(object) {\n var indices = this._getIndex(object);\n\n // add the object to all subnodes it intersects\n for (var i = 0, length = indices.length; i < length; i++) {\n this.subnodes[ indices[i] ].add(object);\n }\n },\n\n /**\n * Determine which subnodes the object intersects with.\n * @memberof kontra.quadtree\n * @private\n *\n * @param {object} object - Object to check.\n *\n * @returns {number[]} List of all subnodes object intersects.\n */\n _getIndex: function getIndex(object) {\n var indices = [];\n\n var verticalMidpoint = this.bounds.x + this.bounds.width / 2;\n var horizontalMidpoint = this.bounds.y + this.bounds.height / 2;\n\n // handle non-kontra.sprite objects as well as kontra.sprite objects\n var x = (object.x !== undefined ? object.x : object.position.x);\n var y = (object.y !== undefined ? object.y : object.position.y);\n\n // save off quadrant checks for reuse\n var intersectsTopQuadrants = y < horizontalMidpoint && y + object.height >= this.bounds.y;\n var intersectsBottomQuadrants = y + object.height >= horizontalMidpoint && y < this.bounds.y + this.bounds.height;\n\n // object intersects with the left quadrants\n if (x < verticalMidpoint && x + object.width >= this.bounds.x) {\n if (intersectsTopQuadrants) { // top left\n indices.push(0);\n }\n\n if (intersectsBottomQuadrants) { // bottom left\n indices.push(2);\n }\n }\n\n // object intersects with the right quadrants\n if (x + object.width >= verticalMidpoint && x < this.bounds.x + this.bounds.width) { // top right\n if (intersectsTopQuadrants) {\n indices.push(1);\n }\n\n if (intersectsBottomQuadrants) { // bottom right\n indices.push(3);\n }\n }\n\n return indices;\n },\n\n /**\n * Split the node into four subnodes.\n * @memberof kontra.quadtree\n * @private\n */\n _split: function split() {\n this.isBranchNode = true;\n\n // only split if we haven't split before\n if (this.subnodes.length) {\n return;\n }\n\n var subWidth = this.bounds.width / 2 | 0;\n var subHeight = this.bounds.height / 2 | 0;\n var x = this.bounds.x;\n var y = this.bounds.y;\n\n for (var i = 0; i < 4; i++) {\n this.subnodes[i] = kontra.quadtree({\n bounds: {\n x: x + (i % 2 === 1 ? subWidth : 0), // nodes 1 and 3\n y: y + (i >= 2 ? subHeight : 0), // nodes 2 and 3\n width: subWidth,\n height: subHeight\n },\n depth: this.depth+1,\n maxDepth: this.maxDepth,\n maxObjects: this.maxObjects,\n parentNode: this\n });\n }\n },\n\n /**\n * Draw the quadtree. Useful for visual debugging.\n * @memberof kontra.quadtree\n */\n render: function() {\n // don't draw empty leaf nodes, always draw branch nodes and the first node\n if (this.objects.length || this.depth === 0 ||\n (this.parentNode && this.parentNode.isBranchNode)) {\n\n kontra.context.strokeStyle = 'red';\n kontra.context.strokeRect(this.bounds.x, this.bounds.y, this.bounds.width, this.bounds.height);\n\n if (this.subnodes.length) {\n for (var i = 0; i < 4; i++) {\n this.subnodes[i].render();\n }\n }\n }\n }\n };\n\n return kontra;\n})(kontra || {});","var kontra = (function(kontra, Math, undefined) {\n 'use strict';\n\n // prevent these properties from being set at the end of kontra.sprite.init()\n var excludedProperties = [\n 'x',\n 'y',\n 'dx',\n 'dy',\n 'ddx',\n 'ddy',\n 'timeToLive',\n 'context',\n 'image',\n 'animations',\n 'color',\n 'width',\n 'height'\n ];\n\n /**\n * A vector for 2D space.\n * @memberof kontra\n *\n * @see kontra.vector.prototype.init for list of parameters.\n */\n kontra.vector = function(x, y) {\n var vector = Object.create(kontra.vector.prototype);\n vector.init(x, y);\n\n return vector;\n };\n\n kontra.vector.prototype = {\n /**\n * Initialize the vectors x and y position.\n * @memberof kontra.vector\n *\n * @param {number} x=0 - Center x coordinate.\n * @param {number} y=0 - Center y coordinate.\n *\n * @returns {vector}\n */\n init: function init(x, y) {\n this.x = x || 0;\n this.y = y || 0;\n\n return this;\n },\n\n /**\n * Add a vector to this vector.\n * @memberof kontra.vector\n *\n * @param {vector} vector - Vector to add.\n * @param {number} dt=1 - Time since last update.\n */\n add: function add(vector, dt) {\n this.x += (vector.x || 0) * (dt || 1);\n this.y += (vector.y || 0) * (dt || 1);\n },\n\n /**\n * Clamp the vector between two points that form a rectangle.\n * Please note that clamping will only work if the add function is called.\n * @memberof kontra.vector\n *\n * @param {number} [xMin=-Infinity] - Min x value.\n * @param {number} [yMin=Infinity] - Min y value.\n * @param {number} [xMax=-Infinity] - Max x value.\n * @param {number} [yMax=Infinity] - Max y value.\n */\n clamp: function clamp(xMin, yMin, xMax, yMax) {\n this._xMin = (xMin !== undefined ? xMin : -Infinity);\n this._xMax = (xMax !== undefined ? xMax : Infinity);\n this._yMin = (yMin !== undefined ? yMin : -Infinity);\n this._yMax = (yMax !== undefined ? yMax : Infinity);\n\n // rename x and y so we can use them as getters and setters\n this._x = this.x;\n this._y = this.y;\n\n // define getters to return the renamed x and y and setters to clamp their value\n Object.defineProperties(this, {\n x: {\n get: function() {\n return this._x;\n },\n set: function(value) {\n this._x = Math.min( Math.max(value, this._xMin), this._xMax );\n }\n },\n y: {\n get: function() {\n return this._y;\n },\n set: function(value) {\n this._y = Math.min( Math.max(value, this._yMin), this._yMax );\n }\n }\n });\n }\n };\n\n\n\n\n\n /**\n * A sprite with a position, velocity, and acceleration.\n * @memberof kontra\n * @requires kontra.vector\n *\n * @see kontra.sprite.prototype.init for list of parameters.\n */\n kontra.sprite = function(properties) {\n var sprite = Object.create(kontra.sprite.prototype);\n sprite.init(properties);\n\n return sprite;\n };\n\n kontra.sprite.prototype = {\n /**\n * Move the sprite by its velocity.\n * @memberof kontra.sprite\n *\n * @param {number} dt - Time since last update.\n */\n advanceSprite: function advanceSprite(dt) {\n this.velocity.add(this.acceleration, dt);\n this.position.add(this.velocity, dt);\n\n this.timeToLive--;\n },\n\n /**\n * Draw a simple rectangle. Useful for prototyping.\n * @memberof kontra.sprite\n */\n drawRect: function drawRect() {\n this.context.fillStyle = this.color;\n this.context.fillRect(this.position.x, this.position.y, this.width, this.height);\n },\n\n /**\n * Draw the sprite.\n * @memberof kontra.sprite\n */\n drawImage: function drawImage() {\n this.context.drawImage(this.image, this.position.x, this.position.y);\n },\n\n /**\n * Update the currently playing animation. Used when animations are passed to the sprite.\n * @memberof kontra.sprite\n *\n * @param {number} dt - Time since last update.\n */\n advanceAnimation: function advanceAnimation(dt) {\n this.advanceSprite(dt);\n\n this.currentAnimation.update(dt);\n },\n\n /**\n * Draw the currently playing animation. Used when animations are passed to the sprite.\n * @memberof kontra.sprite\n */\n drawAnimation: function drawAnimation() {\n this.currentAnimation.render({\n context: this.context,\n x: this.position.x,\n y: this.position.y\n });\n },\n\n /**\n * Play an animation.\n * @memberof kontra.sprite\n *\n * @param {string} name - Name of the animation to play.\n */\n playAnimation: function playAnimation(name) {\n this.currentAnimation = this.animations[name];\n },\n\n /**\n * Determine if the sprite is alive.\n * @memberof kontra.sprite\n *\n * @returns {boolean}\n */\n isAlive: function isAlive() {\n return this.timeToLive > 0;\n },\n\n /**\n * Initialize properties on the sprite.\n * @memberof kontra.sprite\n *\n * @param {object} properties - Properties of the sprite.\n * @param {number} properties.x - X coordinate of the sprite.\n * @param {number} properties.y - Y coordinate of the sprite.\n * @param {number} [properties.dx] - Change in X position.\n * @param {number} [properties.dy] - Change in Y position.\n * @param {number} [properties.ddx] - Change in X velocity.\n * @param {number} [properties.ddy] - Change in Y velocity.\n *\n * @param {number} [properties.timeToLive=0] - How may frames the sprite should be alive.\n * @param {Context} [properties.context=kontra.context] - Provide a context for the sprite to draw on.\n *\n * @param {Image|Canvas} [properties.image] - Image for the sprite.\n *\n * @param {object} [properties.animations] - Animations for the sprite instead of an image.\n *\n * @param {string} [properties.color] - If no image or animation is provided, use color to draw a rectangle for the sprite.\n * @param {number} [properties.width] - Width of the sprite for drawing a rectangle.\n * @param {number} [properties.height] - Height of the sprite for drawing a rectangle.\n *\n * @param {function} [properties.update] - Function to use to update the sprite.\n * @param {function} [properties.render] - Function to use to render the sprite.\n *\n * If you need the sprite to live forever, or just need it to stay on screen until you\n * decide when to kill it, you can set timeToLive to Infinity.\n * Just be sure to set timeToLive to 0 when you want the sprite to die.\n */\n init: function init(properties) {\n properties = properties || {};\n\n var _this = this;\n\n _this.position = (_this.position || kontra.vector()).init(properties.x, properties.y);\n _this.velocity = (_this.velocity || kontra.vector()).init(properties.dx, properties.dy);\n _this.acceleration = (_this.acceleration || kontra.vector()).init(properties.ddx, properties.ddy);\n\n _this.timeToLive = properties.timeToLive || 0;\n _this.context = properties.context || kontra.context;\n\n // image sprite\n if (kontra.isImage(properties.image) || kontra.isCanvas(properties.image)) {\n _this.image = properties.image;\n _this.width = properties.image.width;\n _this.height = properties.image.height;\n\n // change the advance and draw functions to work with images\n _this.advance = _this.advanceSprite;\n _this.draw = _this.drawImage;\n }\n // animation sprite\n else if (properties.animations) {\n _this.animations = properties.animations;\n\n // default the current animation to the first one in the list\n _this.currentAnimation = properties.animations[ Object.keys(properties.animations)[0] ];\n _this.width = _this.currentAnimation.width;\n _this.height = _this.currentAnimation.height;\n\n // change the advance and draw functions to work with animations\n _this.advance = _this.advanceAnimation;\n _this.draw = _this.drawAnimation;\n }\n // rectangle sprite\n else {\n _this.color = properties.color;\n _this.width = properties.width;\n _this.height = properties.height;\n\n // change the advance and draw functions to work with rectangles\n _this.advance = _this.advanceSprite;\n _this.draw = _this.drawRect;\n }\n\n // loop through all other properties an add them to the sprite\n for (var prop in properties) {\n if (properties.hasOwnProperty(prop) && excludedProperties.indexOf(prop) === -1) {\n _this[prop] = properties[prop];\n }\n }\n },\n\n // define getter and setter shortcut functions to make it easier to work work with the\n // position, velocity, and acceleration vectors.\n\n /**\n * Sprite position.x\n * @memberof kontra.sprite\n *\n * @property {number} x\n */\n get x() {\n return this.position.x;\n },\n\n /**\n * Sprite position.y\n * @memberof kontra.sprite\n *\n * @property {number} y\n */\n get y() {\n return this.position.y;\n },\n\n /**\n * Sprite velocity.x\n * @memberof kontra.sprite\n *\n * @property {number} dx\n */\n get dx() {\n return this.velocity.x;\n },\n\n /**\n * Sprite velocity.y\n * @memberof kontra.sprite\n *\n * @property {number} dy\n */\n get dy() {\n return this.velocity.y;\n },\n\n /**\n * Sprite acceleration.x\n * @memberof kontra.sprite\n *\n * @property {number} ddx\n */\n get ddx() {\n return this.acceleration.x;\n },\n\n /**\n * Sprite acceleration.y\n * @memberof kontra.sprite\n *\n * @property {number} ddy\n */\n get ddy() {\n return this.acceleration.y;\n },\n\n set x(value) {\n this.position.x = value;\n },\n set y(value) {\n this.position.y = value;\n },\n set dx(value) {\n this.velocity.x = value;\n },\n set dy(value) {\n this.velocity.y = value;\n },\n set ddx(value) {\n this.acceleration.x = value;\n },\n set ddy(value) {\n this.acceleration.y = value;\n },\n\n /**\n * Simple bounding box collision test.\n * @memberof kontra.sprite\n *\n * @param {object} object - Object to check collision against.\n *\n * @returns {boolean} True if the objects collide, false otherwise.\n */\n collidesWith: function collidesWith(object) {\n // handle non-kontra.sprite objects as well as kontra.sprite objects\n var x = (object.x !== undefined ? object.x : object.position.x);\n var y = (object.y !== undefined ? object.y : object.position.y);\n\n if (this.position.x < x + object.width &&\n this.position.x + this.width > x &&\n this.position.y < y + object.height &&\n this.position.y + this.height > y) {\n return true;\n }\n\n return false;\n },\n\n /**\n * Update the sprites velocity and position.\n * @memberof kontra.sprite\n * @abstract\n *\n * @param {number} dt - Time since last update.\n *\n * This function can be overridden on a per sprite basis if more functionality\n * is needed in the update step. Just call this.advance() when you need\n * the sprite to update its position.\n *\n * @example\n * sprite = kontra.sprite({\n * update: function update(dt) {\n * // do some logic\n *\n * this.advance(dt);\n * }\n * });\n */\n update: function update(dt) {\n this.advance(dt);\n },\n\n /**\n * Render the sprite.\n * @memberof kontra.sprite.\n * @abstract\n *\n * This function can be overridden on a per sprite basis if more functionality\n * is needed in the render step. Just call this.draw() when you need the\n * sprite to draw its image.\n *\n * @example\n * sprite = kontra.sprite({\n * render: function render() {\n * // do some logic\n *\n * this.draw();\n * }\n * });\n */\n render: function render() {\n this.draw();\n }\n };\n\n return kontra;\n})(kontra || {}, Math);","/*jshint -W084 */\n\nvar kontra = (function(kontra, undefined) {\n 'use strict';\n\n /**\n * Single animation from a sprite sheet.\n * @memberof kontra\n *\n * @see kontra.pool.prototype.init for list of parameters.\n */\n kontra.animation = function(properties) {\n var animation = Object.create(kontra.animation.prototype);\n animation.init(properties);\n\n return animation;\n };\n\n kontra.animation.prototype = {\n /**\n * Initialize properties on the animation.\n * @memberof kontra.animation\n *\n * @param {object} properties - Properties of the animation.\n * @param {spriteSheet} properties.spriteSheet - Sprite sheet for the animation.\n * @param {number[]} properties.frames - List of frames of the animation.\n * @param {number} properties.frameSpeed - Time to wait before transitioning the animation to the next frame.\n */\n init: function init(properties) {\n properties = properties || {};\n\n this.spriteSheet = properties.spriteSheet;\n this.frames = properties.frames;\n this.frameSpeed = properties.frameSpeed;\n\n this.width = properties.spriteSheet.frame.width;\n this.height = properties.spriteSheet.frame.height;\n\n this.currentFrame = 0;\n this._accumulator = 0;\n this.update = this.advance;\n this.render = this.draw;\n },\n\n /**\n * Update the animation. Used when the animation is not paused or stopped.\n * @memberof kontra.animation\n * @private\n *\n * @param {number} dt=1 - Time since last update.\n */\n advance: function advance(dt) {\n // normalize dt to work with milliseconds as a decimal or an integer\n dt = (dt < 1 ? dt * 1E3 : dt) || 1;\n\n this._accumulator += dt;\n\n // update to the next frame if it's time\n while (this._accumulator >= this.frameSpeed) {\n this.currentFrame = ++this.currentFrame % this.frames.length;\n\n this._accumulator -= this.frameSpeed;\n }\n },\n\n /**\n * Draw the current frame. Used when the animation is not stopped.\n * @memberof kontra.animation\n * @private\n *\n * @param {object} properties - How to draw the animation.\n * @param {number} properties.x - X position to draw.\n * @param {number} properties.y - Y position to draw.\n * @param {Context} [properties.context=kontra.context] - Provide a context for the sprite to draw on.\n */\n draw: function draw(properties) {\n properties = properties || {};\n\n var context = properties.context || kontra.context;\n\n // get the row and col of the frame\n var row = this.frames[this.currentFrame] / this.spriteSheet.framesPerRow | 0;\n var col = this.frames[this.currentFrame] % this.spriteSheet.framesPerRow | 0;\n\n context.drawImage(\n this.spriteSheet.image,\n col * this.spriteSheet.frame.width, row * this.spriteSheet.frame.height,\n this.spriteSheet.frame.width, this.spriteSheet.frame.height,\n properties.x, properties.y,\n this.spriteSheet.frame.width, this.spriteSheet.frame.height\n );\n },\n\n /**\n * Play the animation.\n * @memberof kontra.animation\n */\n play: function play() {\n // restore references to update and render functions only if overridden\n this.update = this.advance;\n this.render = this.draw;\n },\n\n /**\n * Stop the animation and prevent update and render.\n * @memberof kontra.animation\n */\n stop: function stop() {\n\n // instead of putting an if statement in both render/update functions that checks\n // a variable to determine whether to render or update, we can just reassign the\n // functions to noop and save processing time in the game loop.\n // @see http://jsperf.com/boolean-check-vs-noop\n this.update = kontra.noop;\n this.render = kontra.noop;\n },\n\n /**\n * Pause the animation and prevent update.\n * @memberof kontra.animation\n */\n pause: function pause() {\n this.update = kontra.noop;\n }\n };\n\n\n\n\n\n\n /**\n * Create a sprite sheet from an image.\n * @memberof kontra\n *\n * @see kontra.spriteSheet.prototype.init for list of parameters.\n */\n kontra.spriteSheet = function(properties) {\n var spriteSheet = Object.create(kontra.spriteSheet.prototype);\n spriteSheet.init(properties);\n\n return spriteSheet;\n };\n\n kontra.spriteSheet.prototype = {\n /**\n * Initialize properties on the spriteSheet.\n * @memberof kontra\n * @constructor\n *\n * @param {object} properties - Configure the sprite sheet.\n * @param {Image|Canvas} properties.image - Image for the sprite sheet.\n * @param {number} properties.frameWidth - Width (in px) of each frame.\n * @param {number} properties.frameHeight - Height (in px) of each frame.\n * @param {object} properties.animations - Animations to create from the sprite sheet.\n */\n init: function init(properties) {\n properties = properties || {};\n\n this.animations = {};\n\n if (kontra.isImage(properties.image) || kontra.isCanvas(properties.image)) {\n this.image = properties.image;\n this.frame = {\n width: properties.frameWidth,\n height: properties.frameHeight\n };\n\n this.framesPerRow = properties.image.width / properties.frameWidth | 0;\n }\n else {\n var error = new SyntaxError('Invalid image.');\n kontra.logError(error, 'You must provide an Image for the SpriteSheet.');\n return;\n }\n\n if (properties.animations) {\n this.createAnimations(properties.animations);\n }\n },\n\n /**\n * Create animations from the sprite sheet.\n * @memberof kontra.spriteSheet\n *\n * @param {object} animations - List of named animations to create from the Image.\n * @param {number|string|number[]|string[]} animations.animationName.frames - A single frame or list of frames for this animation.\n * @param {number} animations.animationName.frameSpeed=1 - Number of frames to wait before transitioning the animation to the next frame.\n *\n * @example\n * var sheet = kontra.spriteSheet({image: img, frameWidth: 16, frameHeight: 16});\n * sheet.createAnimations({\n * idle: {\n * frames: 1 // single frame animation\n * },\n * walk: {\n * frames: '2..6', // ascending consecutive frame animation (frames 2-6, inclusive)\n * frameSpeed: 4\n * },\n * moonWalk: {\n * frames: '6..2', // descending consecutive frame animation\n * frameSpeed: 4\n * },\n * jump: {\n * frames: [7, 12, 2], // non-consecutive frame animation\n * frameSpeed: 3\n * },\n * attack: {\n * frames: ['8..10', 13, '10..8'], // you can also mix and match, in this case frames [8,9,10,13,10,9,8]\n * frameSpeed: 2\n * }\n * });\n */\n createAnimations: function createAnimations(animations) {\n var error;\n\n if (!animations || Object.keys(animations).length === 0) {\n error = new ReferenceError('No animations found.');\n kontra.logError(error, 'You must provide at least one named animation to create an Animation.');\n return;\n }\n\n // create each animation by parsing the frames\n var animation, frames, frameSpeed, sequence;\n for (var name in animations) {\n if (!animations.hasOwnProperty(name)) {\n continue;\n }\n\n animation = animations[name];\n frames = animation.frames;\n frameSpeed = animation.frameSpeed;\n\n // array that holds the order of the animation\n sequence = [];\n\n if (frames === undefined) {\n error = new ReferenceError('No animation frames found.');\n kontra.logError(error, 'Animation ' + name + ' must provide a frames property.');\n return;\n }\n\n // single frame\n if (kontra.isNumber(frames)) {\n sequence.push(frames);\n }\n // consecutive frames\n else if (kontra.isString(frames)) {\n sequence = this._parseFrames(frames);\n }\n // non-consecutive frames\n else if (kontra.isArray(frames)) {\n for (var i = 0, frame; frame = frames[i]; i++) {\n\n // consecutive frames\n if (kontra.isString(frame)) {\n\n // add new frames to the end of the array\n sequence.push.apply(sequence, this._parseFrames(frame));\n }\n // single frame\n else {\n sequence.push(frame);\n }\n }\n }\n\n this.animations[name] = kontra.animation({\n spriteSheet: this,\n frames: sequence,\n frameSpeed: frameSpeed\n });\n }\n },\n\n /**\n * Parse a string of consecutive frames.\n * @memberof kontra.spriteSheet\n * @private\n *\n * @param {string} frames - Start and end frame.\n *\n * @returns {number[]} List of frames.\n */\n _parseFrames: function parseFrames(frames) {\n var sequence = [];\n var consecutiveFrames = frames.split('..').map(Number);\n\n // determine which direction to loop\n var direction = (consecutiveFrames[0] < consecutiveFrames[1] ? 1 : -1);\n var i;\n\n // ascending frame order\n if (direction === 1) {\n for (i = consecutiveFrames[0]; i <= consecutiveFrames[1]; i++) {\n sequence.push(i);\n }\n }\n // descending order\n else {\n for (i = consecutiveFrames[0]; i >= consecutiveFrames[1]; i--) {\n sequence.push(i);\n }\n }\n\n return sequence;\n }\n };\n\n return kontra;\n})(kontra || {});","/**\n * localStorage can be a bit of a pain to work with since it stores everything as strings:\n * localStorage.setItem('item', 1); //=> '1'\n * localStorage.setItem('item', false); //=> 'false'\n * localStorage.setItem('item', [1,2,3]); //=> '1,2,3'\n * localStorage.setItem('item', {a:'b'}); //=> '[object Object]'\n * localStorage.setItem('item', undefinedVariable); //=> 'undefined'\n *\n * @fileoverview A simple wrapper for localStorage to make it easier to work with.\n * Based on store.js {@see https://github.com/marcuswestin/store.js}\n */\nvar kontra = (function(kontra, window, localStorage, undefined) {\n 'use strict';\n\n // check if the browser can use localStorage\n kontra.canUse = kontra.canUse || {};\n kontra.canUse.localStorage = 'localStorage' in window && window.localStorage !== null;\n\n if (!kontra.canUse.localStorage) {\n return kontra;\n }\n\n /**\n * Object for using localStorage.\n */\n kontra.store = {};\n\n /**\n * Save an item to localStorage.\n * @memberof kontra.store\n *\n * @param {string} key - Name to store the item as.\n * @param {*} value - Item to store.\n */\n kontra.store.set = function setStoreItem(key, value) {\n if (value === undefined) {\n this.remove(key);\n }\n else {\n localStorage.setItem(key, JSON.stringify(value));\n }\n };\n\n /**\n * Retrieve an item from localStorage and convert it back to it's original type.\n * @memberof kontra.store\n *\n * @param {string} key - Name of the item.\n *\n * @returns {*}\n */\n kontra.store.get = function getStoreItem(key) {\n var value = localStorage.getItem(key);\n\n try {\n value = JSON.parse(value);\n }\n catch(e) {}\n\n return value;\n };\n\n /**\n * Remove an item from localStorage.\n * @memberof kontra.store\n *\n * @param {string} key - Name of the item.\n */\n kontra.store.remove = function removeStoreItem(key) {\n localStorage.removeItem(key);\n };\n\n /**\n * Clear all keys from localStorage.\n * @memberof kontra.store\n */\n kontra.store.clear = function clearStore() {\n localStorage.clear();\n };\n\n return kontra;\n})(kontra || {}, window, window.localStorage);","/*jshint -W084 */\n\nvar kontra = (function(kontra, Math, undefined) {\n 'use strict';\n\n /**\n * A tile engine for rendering tilesets. Works well with the tile engine program Tiled.\n * @memberof kontra\n *\n * @see kontra.tileEngine.prototype.init for list of parameters.\n */\n kontra.tileEngine = function(properties) {\n var tileEngine = Object.create(kontra.tileEngine.prototype);\n tileEngine.init(properties);\n\n return tileEngine;\n };\n\n kontra.tileEngine.prototype = {\n /**\n * Initialize properties on the tile engine.\n * @memberof kontra.tileEngine\n *\n * @param {object} properties - Properties of the tile engine.\n * @param {number} [properties.tileWidth=32] - Width of a tile.\n * @param {number} [properties.tileHeight=32] - Height of a tile.\n * @param {number} properties.width - Width of the map (in tiles).\n * @param {number} properties.height - Height of the map (in tiles).\n * @param {number} [properties.x=0] - X position to draw.\n * @param {number} [properties.y=0] - Y position to draw.\n * @param {number} [properties.sx=0] - X position to clip the tileset.\n * @param {number} [properties.sy=0] - Y position to clip the tileset.\n * @param {Context} [properties.context=kontra.context] - Provide a context for the tile engine to draw on.\n */\n init: function init(properties) {\n properties = properties || {};\n\n var _this = this;\n\n // size of the map (in tiles)\n if (!properties.width || !properties.height) {\n var error = new ReferenceError('Required parameters not found');\n kontra.logError(error, 'You must provide width and height of the map to create a tile engine.');\n return;\n }\n\n _this.width = properties.width;\n _this.height = properties.height;\n\n // size of the tiles. Most common tile size on opengameart.org seems to be 32x32,\n // followed by 16x16\n _this.tileWidth = properties.tileWidth || 32;\n _this.tileHeight = properties.tileHeight || 32;\n\n _this.context = properties.context || kontra.context;\n\n _this.canvasWidth = _this.context.canvas.width;\n _this.canvasHeight = _this.context.canvas.height;\n\n // create an off-screen canvas for pre-rendering the map\n // @see http://jsperf.com/render-vs-prerender\n _this._offscreenCanvas = document.createElement('canvas');\n _this._offscreenContext = _this._offscreenCanvas.getContext('2d');\n\n // make the off-screen canvas the full size of the map\n _this._offscreenCanvas.width = _this.mapWidth = _this.width * _this.tileWidth;\n _this._offscreenCanvas.height = _this.mapHeight = _this.height * _this.tileHeight;\n\n // when clipping an image, sx and sy must within the image region, otherwise\n // Firefox and Safari won't draw it.\n // @see http://stackoverflow.com/questions/19338032/canvas-indexsizeerror-index-or-size-is-negative-or-greater-than-the-allowed-a\n _this.sxMax = _this.mapWidth - _this.canvasWidth;\n _this.syMax = _this.mapHeight - _this.canvasHeight;\n\n _this.layers = {};\n\n // draw order of layers (by name)\n _this._layerOrder = [];\n\n // each tileset will hold the first and the last grid as well as the image for the tileset\n _this.tilesets = [];\n\n _this.x = properties.x || 0;\n _this.y = properties.y || 0;\n _this.sx = properties.sx || 0;\n _this.sy = properties.sy || 0;\n },\n\n /**\n * Add an tileset for the tile engine to use.\n * @memberof kontra.tileEngine\n *\n * @param {object} properties - Properties of the image to add.\n * @param {string|Image|Canvas} properties.image - Path to the image or Image object.\n * @param {number} properties.firstGrid - The first tile grid to start the image.\n */\n addTileset: function addTileset(properties) {\n properties = properties || {};\n\n if (kontra.isImage(properties.image) || kontra.isCanvas(properties.image)) {\n var image = properties.image;\n var firstGrid = properties.firstGrid;\n var numTiles = (image.width / this.tileWidth | 0) * (image.height / this.tileHeight | 0);\n\n if (!firstGrid) {\n // only calculate the first grid if the tile map has a tileset already\n if (this.tilesets.length > 0) {\n var lastTileset = this.tilesets[this.tilesets.length - 1];\n var tiles = (lastTileset.image.width / this.tileWidth | 0) *\n (lastTileset.image.height / this.tileHeight | 0);\n\n firstGrid = lastTileset.firstGrid + tiles - 1;\n }\n // otherwise this is the first tile added to the tile map\n else {\n firstGrid = 1;\n }\n }\n\n this.tilesets.push({\n firstGrid: firstGrid,\n lastGrid: firstGrid + numTiles - 1,\n image: image\n });\n\n // sort the tile map so we can perform a binary search when drawing\n this.tilesets.sort(function(a, b) {\n return a.firstGrid - b.firstGrid;\n });\n }\n else {\n var error = new SyntaxError('Invalid image.');\n kontra.logError(error, 'You must provide an Image for the tile engine.');\n return;\n }\n },\n\n /**\n * Add a layer to the tile engine.\n * @memberof kontra.tileEngine\n *\n * @param {object} properties - Properties of the layer to add.\n * @param {string} properties.name - Name of the layer.\n * @param {number[]} properties.data - Tile layer data.\n * @param {boolean} [properties.render=true] - If the layer should be drawn.\n * @param {number} properties.zIndex - Draw order for tile layer. Highest number is drawn last (i.e. on top of all other layers).\n */\n addLayer: function addLayer(properties) {\n properties = properties || {};\n properties.render = (properties.render === undefined ? true : properties.render);\n\n var _this = this;\n var data;\n\n // flatten a 2D array into a single array\n if (kontra.isArray(properties.data[0])) {\n data = [];\n\n for (var r = 0, row; row = properties.data[r]; r++) {\n for (var c = 0, length = row.length; c < length; c++) {\n data.push(row[c]);\n }\n }\n }\n else {\n data = properties.data;\n }\n\n this.layers[properties.name] = data;\n this.layers[properties.name].zIndex = properties.zIndex || 0;\n this.layers[properties.name].render = properties.render;\n\n // only add the layer to the layer order if it should be drawn\n if (properties.render) {\n this._layerOrder.push(properties.name);\n\n this._layerOrder.sort(function(a, b) {\n return _this.layers[a].zIndex - _this.layers[b].zIndex;\n });\n\n this._preRenderImage();\n }\n },\n\n /**\n * Simple bounding box collision test for layer tiles.\n * @memberof kontra.tileEngine\n *\n * @param {string} name - Name of the layer.\n * @param {object} object - Object to check collision against.\n * @param {number} object.x - X coordinate of the object.\n * @param {number} object.y - Y coordinate of the object.\n * @param {number} object.width - Width of the object.\n * @param {number} object.height - Height of the object.\n *\n * @returns {boolean} True if the object collides with a tile, false otherwise.\n */\n layerCollidesWith: function layerCollidesWith(name, object) {\n // handle non-kontra.sprite objects as well as kontra.sprite objects\n var x = (object.x !== undefined ? object.x : object.position.x);\n var y = (object.y !== undefined ? object.y : object.position.y);\n\n // calculate all tiles that the object can collide with\n var row = this._getRow(y);\n var col = this._getCol(x);\n\n var endRow = this._getRow(y + object.height);\n var endCol = this._getCol(x + object.width);\n\n // check all tiles\n var index;\n for (var r = row; r <= endRow; r++) {\n for (var c = col; c <= endCol; c++) {\n index = c + r * this.width;\n\n if (this.layers[name][index]) {\n return true;\n }\n }\n }\n\n return false;\n },\n\n /**\n * Get the tile from the specified layer at x, y.\n * @memberof kontra.tileEngine\n *\n * @param {string} name - Name of the layer.\n * @param {number} x - X coordinate of the tile.\n * @param {number} y - Y coordinate of the tile.\n *\n * @returns {number}\n */\n tileAtLayer: function tileAtLayer(name, x, y) {\n var row = this._getRow(y);\n var col = this._getCol(x);\n var index = col + row * this.width;\n\n return this.layers[name][index];\n },\n\n /**\n * Render the pre-rendered canvas.\n * @memberof kontra.tileEngine\n */\n render: function render() {\n var _this = this;\n\n // ensure sx and sy are within the image region\n _this.sx = Math.min( Math.max(_this.sx, 0), _this.sxMax );\n _this.sy = Math.min( Math.max(_this.sy, 0), _this.syMax );\n\n _this.context.drawImage(\n _this._offscreenCanvas,\n _this.sx, _this.sy, _this.canvasWidth, _this.canvasHeight,\n _this.x, _this.y, _this.canvasWidth, _this.canvasHeight\n );\n },\n\n /**\n * Render a specific layer.\n * @memberof kontra.tileEngine\n *\n * @param {string} name - Name of the layer to render.\n */\n renderLayer: function renderLayer(name) {\n var _this = this;\n\n var layer = _this.layers[name];\n\n // calculate the starting tile\n var row = _this._getRow();\n var col = _this._getCol();\n var index = col + row * _this.width;\n\n // calculate where to start drawing the tile relative to the drawing canvas\n var startX = col * _this.tileWidth - _this.sx;\n var startY = row * _this.tileHeight - _this.sy;\n\n // calculate how many tiles the drawing canvas can hold\n var viewWidth = Math.ceil(_this.canvasWidth / _this.tileWidth) + 1;\n var viewHeight = Math.ceil(_this.canvasHeight / _this.tileHeight) + 1;\n var numTiles = viewWidth * viewHeight;\n\n var count = 0;\n var x, y, tile, tileset, image, tileOffset, width, sx, sy;\n\n // draw just enough of the layer to fit inside the drawing canvas\n while (count < numTiles) {\n tile = layer[index];\n\n if (tile) {\n tileset = _this._getTileset(tile);\n image = tileset.image;\n\n x = startX + (count % viewWidth) * _this.tileWidth;\n y = startY + (count / viewWidth | 0) * _this.tileHeight;\n\n tileOffset = tile - tileset.firstGrid;\n width = image.width / _this.tileWidth;\n\n sx = (tileOffset % width) * _this.tileWidth;\n sy = (tileOffset / width | 0) * _this.tileHeight;\n\n _this.context.drawImage(\n image,\n sx, sy, _this.tileWidth, _this.tileHeight,\n x, y, _this.tileWidth, _this.tileHeight\n );\n }\n\n if (++count % viewWidth === 0) {\n index = col + (++row * _this.width);\n }\n else {\n index++;\n }\n }\n },\n\n /**\n * Get the row from the y coordinate.\n * @memberof kontra.tileEngine\n * @private\n *\n * @param {number} y - Y coordinate.\n *\n * @return {number}\n */\n _getRow: function getRow(y) {\n y = y || 0;\n\n return (this.sy + y) / this.tileHeight | 0;\n },\n\n /**\n * Get the col from the x coordinate.\n * @memberof kontra.tileEngine\n * @private\n *\n * @param {number} x - X coordinate.\n *\n * @return {number}\n */\n _getCol: function getCol(x) {\n x = x || 0;\n\n return (this.sx + x) / this.tileWidth | 0;\n },\n\n /**\n * Modified binary search that will return the tileset associated with the tile\n * @memberof kontra.tileEngine\n * @private\n *\n * @param {number} tile - Tile grid.\n *\n * @return {object}\n */\n _getTileset: function getTileset(tile) {\n var min = 0;\n var max = this.tilesets.length - 1;\n var index;\n var currTile;\n\n while (min <= max) {\n index = (min + max) / 2 | 0;\n currTile = this.tilesets[index];\n\n if (tile >= currTile.firstGrid && tile <= currTile.lastGrid) {\n return currTile;\n }\n else if (currTile < tile) {\n min = index + 1;\n }\n else {\n max = index - 1;\n }\n }\n },\n\n /**\n * Pre-render the tiles to make drawing fast.\n * @memberof kontra.tileEngine\n * @private\n */\n _preRenderImage: function preRenderImage() {\n var _this = this;\n var tile, tileset, image, x, y, sx, sy, tileOffset, width;\n\n // draw each layer in order\n for (var i = 0, layer; layer = _this.layers[_this._layerOrder[i]]; i++) {\n for (var j = 0, len = layer.length; j < len; j++) {\n tile = layer[j];\n\n // skip empty tiles (0)\n if (!tile) {\n continue;\n }\n\n tileset = _this._getTileset(tile);\n image = tileset.image;\n\n x = (j % _this.width) * _this.tileWidth;\n y = (j / _this.width | 0) * _this.tileHeight;\n\n tileOffset = tile - tileset.firstGrid;\n width = image.width / _this.tileWidth;\n\n sx = (tileOffset % width) * _this.tileWidth;\n sy = (tileOffset / width | 0) * _this.tileHeight;\n\n _this._offscreenContext.drawImage(\n image,\n sx, sy, _this.tileWidth, _this.tileHeight,\n x, y, _this.tileWidth, _this.tileHeight\n );\n }\n }\n }\n };\n\n return kontra;\n})(kontra || {}, Math);"],"sourceRoot":"/source/"} \ No newline at end of file diff --git a/src/core.js b/src/core.js index b847fd42..b4da7342 100644 --- a/src/core.js +++ b/src/core.js @@ -4,7 +4,7 @@ var kontra = (function(kontra, document) { 'use strict'; /** - * Set up the canvas. + * Initialize the canvas. * @memberof kontra * * @param {object} properties - Properties for the game. diff --git a/src/gameLoop.js b/src/gameLoop.js index 03a05d05..e2128a12 100644 --- a/src/gameLoop.js +++ b/src/gameLoop.js @@ -4,7 +4,7 @@ var kontra = (function(kontra, window) { /** * Get the current time. Uses the User Timing API if it's available or defaults to using * Date().getTime() - * @private + * @memberof kontra * * @returns {number} */ @@ -25,18 +25,18 @@ var kontra = (function(kontra, window) { * Game loop that updates and renders the game every frame. * @memberof kontra * - * @see kontra.gameLoop.prototype.set for list of parameters. + * @see kontra.gameLoop.prototype.init for list of parameters. */ kontra.gameLoop = function(properties) { var gameLoop = Object.create(kontra.gameLoop.prototype); - gameLoop.set(properties); + gameLoop.init(properties); return gameLoop; }; kontra.gameLoop.prototype = { /** - * Set properties on the game loop. + * Initialize properties on the game loop. * @memberof kontra.gameLoop * * @param {object} properties - Configure the game loop. @@ -44,7 +44,7 @@ var kontra = (function(kontra, window) { * @param {function} properties.update - Function called to update the game. * @param {function} properties.render - Function called to render the game. */ - set: function set(properties) { + init: function init(properties) { properties = properties || {}; // check for required functions @@ -64,14 +64,33 @@ var kontra = (function(kontra, window) { this.render = properties.render; }, + /** + * Start the game loop. + * @memberof kontra.gameLoop + */ + start: function start() { + this._last = kontra.timestamp(); + this.isStopped = false; + requestAnimationFrame(this._frame.bind(this)); + }, + + /** + * Stop the game loop. + */ + stop: function stop() { + this.isStopped = true; + cancelAnimationFrame(this._rAF); + }, + /** * Called every frame of the game loop. * @memberof kontra.gameLoop + * @private */ - frame: function frame() { + _frame: function frame() { var _this = this; - _this._rAF = requestAnimationFrame(_this.frame.bind(_this)); + _this._rAF = requestAnimationFrame(_this._frame.bind(_this)); _this._now = kontra.timestamp(); _this._dt = _this._now - _this._last; @@ -92,24 +111,6 @@ var kontra = (function(kontra, window) { } _this.render(); - }, - - /** - * Start the game loop. - * @memberof kontra.gameLoop - */ - start: function start() { - this._last = kontra.timestamp(); - this.isStopped = false; - requestAnimationFrame(this.frame.bind(this)); - }, - - /** - * Stop the game loop. - */ - stop: function stop() { - this.isStopped = true; - cancelAnimationFrame(this._rAF); } }; diff --git a/src/pool.js b/src/pool.js index fc8474e6..bd5435f1 100644 --- a/src/pool.js +++ b/src/pool.js @@ -8,18 +8,18 @@ var kontra = (function(kontra) { * Unused items are at the front of the pool and in use items are at the of the pool. * @memberof kontra * - * @see kontra.pool.prototype.set for list of parameters. + * @see kontra.pool.prototype.init for list of parameters. */ kontra.pool = function(properties) { var pool = Object.create(kontra.pool.prototype); - pool.set(properties); + pool.init(properties); return pool; }; kontra.pool.prototype = { /** - * Set properties on the pool. + * Initialize properties on the pool. * @memberof kontra.pool * * @param {object} properties - Properties of the pool. @@ -29,9 +29,9 @@ var kontra = (function(kontra) { * @param {boolean} properties.fill - Fill the pool to max size instead of slowly growing. * * Objects inside the pool must implement render(), update(), - * set(), and isAlive() functions. + * init(), and isAlive() functions. */ - set: function set(properties) { + init: function init(properties) { properties = properties || {}; var error, obj; @@ -51,9 +51,9 @@ var kontra = (function(kontra) { obj = this.create(); if (!obj || typeof obj.render !== 'function' || typeof obj.update !== 'function' || - typeof obj.set !== 'function' || typeof obj.isAlive !== 'function') { + typeof obj.init !== 'function' || typeof obj.isAlive !== 'function') { error = new SyntaxError('Create object required functions not found.'); - kontra.logError(error, 'Objects to be pooled must implement render(), update(), set() and isAlive() functions.'); + kontra.logError(error, 'Objects to be pooled must implement render(), update(), init() and isAlive() functions.'); return; } @@ -86,7 +86,7 @@ var kontra = (function(kontra) { * Get an object from the pool. * @memberof kontra.pool * - * @param {object} properties - Properties to pass to object.set(). + * @param {object} properties - Properties to pass to object.init(). */ get: function get(properties) { properties = properties || {}; @@ -111,7 +111,7 @@ var kontra = (function(kontra) { // save off first object in pool to reassign to last object after unshift var obj = _this.objects[0]; - obj.set(properties); + obj.init(properties); // unshift the array for (var i = 1; i < _this.size; i++) { diff --git a/src/quadtree.js b/src/quadtree.js index a1941201..3a46a1bf 100644 --- a/src/quadtree.js +++ b/src/quadtree.js @@ -9,7 +9,7 @@ var kontra = (function(kontra, undefined) { * collapses to avoid garbage collection. * @memberof kontra * - * @see kontra.quadtree.prototype.set for list of parameters. + * @see kontra.quadtree.prototype.init for list of parameters. *L * The quadrant indices are numbered as follows (following a z-order curve): * | @@ -20,14 +20,14 @@ var kontra = (function(kontra, undefined) { */ kontra.quadtree = function(properties) { var quadtree = Object.create(kontra.quadtree.prototype); - quadtree.set(properties); + quadtree.init(properties); return quadtree; }; kontra.quadtree.prototype = { /** - * Set properties on the quadtree. + * Initialize properties on the quadtree. * @memberof kontra.quadtree * * @param {number} [depth=0] - Current node depth. @@ -36,7 +36,7 @@ var kontra = (function(kontra, undefined) { * @param {object} [parentNode] - The node that contains this node. * @param {object} [bounds] - The 2D space this node occupies. */ - set: function set(properties) { + init: function init(properties) { properties = properties || {}; this.depth = properties.depth || 0; diff --git a/src/sprite.js b/src/sprite.js index 86cc30ce..784b36a9 100644 --- a/src/sprite.js +++ b/src/sprite.js @@ -1,7 +1,7 @@ var kontra = (function(kontra, Math, undefined) { 'use strict'; - // prevent these properties from being set at the end of kontra.sprite.set() + // prevent these properties from being set at the end of kontra.sprite.init() var excludedProperties = [ 'x', 'y', @@ -22,18 +22,18 @@ var kontra = (function(kontra, Math, undefined) { * A vector for 2D space. * @memberof kontra * - * @see kontra.vector.prototype.set for list of parameters. + * @see kontra.vector.prototype.init for list of parameters. */ kontra.vector = function(x, y) { var vector = Object.create(kontra.vector.prototype); - vector.set(x, y); + vector.init(x, y); return vector; }; kontra.vector.prototype = { /** - * Set the vectors x and y position. + * Initialize the vectors x and y position. * @memberof kontra.vector * * @param {number} x=0 - Center x coordinate. @@ -41,7 +41,7 @@ var kontra = (function(kontra, Math, undefined) { * * @returns {vector} */ - set: function set(x, y) { + init: function init(x, y) { this.x = x || 0; this.y = y || 0; @@ -111,11 +111,11 @@ var kontra = (function(kontra, Math, undefined) { * @memberof kontra * @requires kontra.vector * - * @see kontra.sprite._prot.set for list of parameters. + * @see kontra.sprite.prototype.init for list of parameters. */ kontra.sprite = function(properties) { var sprite = Object.create(kontra.sprite.prototype); - sprite.set(properties); + sprite.init(properties); return sprite; }; @@ -196,10 +196,10 @@ var kontra = (function(kontra, Math, undefined) { }, /** - * Set properties on the sprite. + * Initialize properties on the sprite. * @memberof kontra.sprite * - * @param {object} properties - Properties to set on the sprite. + * @param {object} properties - Properties of the sprite. * @param {number} properties.x - X coordinate of the sprite. * @param {number} properties.y - Y coordinate of the sprite. * @param {number} [properties.dx] - Change in X position. @@ -225,14 +225,14 @@ var kontra = (function(kontra, Math, undefined) { * decide when to kill it, you can set timeToLive to Infinity. * Just be sure to set timeToLive to 0 when you want the sprite to die. */ - set: function set(properties) { + init: function init(properties) { properties = properties || {}; var _this = this; - _this.position = (_this.position || kontra.vector()).set(properties.x, properties.y); - _this.velocity = (_this.velocity || kontra.vector()).set(properties.dx, properties.dy); - _this.acceleration = (_this.acceleration || kontra.vector()).set(properties.ddx, properties.ddy); + _this.position = (_this.position || kontra.vector()).init(properties.x, properties.y); + _this.velocity = (_this.velocity || kontra.vector()).init(properties.dx, properties.dy); + _this.acceleration = (_this.acceleration || kontra.vector()).init(properties.ddx, properties.ddy); _this.timeToLive = properties.timeToLive || 0; _this.context = properties.context || kontra.context; diff --git a/src/spriteSheet.js b/src/spriteSheet.js index f2a9aab6..615cdd3c 100644 --- a/src/spriteSheet.js +++ b/src/spriteSheet.js @@ -7,18 +7,18 @@ var kontra = (function(kontra, undefined) { * Single animation from a sprite sheet. * @memberof kontra * - * @see kontra.pool.prototype.set for list of parameters. + * @see kontra.pool.prototype.init for list of parameters. */ kontra.animation = function(properties) { var animation = Object.create(kontra.animation.prototype); - animation.set(properties); + animation.init(properties); return animation; }; kontra.animation.prototype = { /** - * Set properties on the animation. + * Initialize properties on the animation. * @memberof kontra.animation * * @param {object} properties - Properties of the animation. @@ -26,7 +26,7 @@ var kontra = (function(kontra, undefined) { * @param {number[]} properties.frames - List of frames of the animation. * @param {number} properties.frameSpeed - Time to wait before transitioning the animation to the next frame. */ - set: function set(properties) { + init: function init(properties) { properties = properties || {}; this.spriteSheet = properties.spriteSheet; @@ -133,18 +133,18 @@ var kontra = (function(kontra, undefined) { * Create a sprite sheet from an image. * @memberof kontra * - * @see kontra.spriteSheet.prototype.set for list of parameters. + * @see kontra.spriteSheet.prototype.init for list of parameters. */ kontra.spriteSheet = function(properties) { var spriteSheet = Object.create(kontra.spriteSheet.prototype); - spriteSheet.set(properties); + spriteSheet.init(properties); return spriteSheet; }; kontra.spriteSheet.prototype = { /** - * Set properties on the spriteSheet. + * Initialize properties on the spriteSheet. * @memberof kontra * @constructor * @@ -154,7 +154,7 @@ var kontra = (function(kontra, undefined) { * @param {number} properties.frameHeight - Height (in px) of each frame. * @param {object} properties.animations - Animations to create from the sprite sheet. */ - set: function set(properties) { + init: function init(properties) { properties = properties || {}; this.animations = {}; diff --git a/src/tileEngine.js b/src/tileEngine.js index 52873a0a..5225c6f3 100644 --- a/src/tileEngine.js +++ b/src/tileEngine.js @@ -7,18 +7,18 @@ var kontra = (function(kontra, Math, undefined) { * A tile engine for rendering tilesets. Works well with the tile engine program Tiled. * @memberof kontra * - * @see kontra.tileEngine.prototype.set for list of parameters. + * @see kontra.tileEngine.prototype.init for list of parameters. */ kontra.tileEngine = function(properties) { var tileEngine = Object.create(kontra.tileEngine.prototype); - tileEngine.set(properties); + tileEngine.init(properties); return tileEngine; }; kontra.tileEngine.prototype = { /** - * Set properties on the tile engine. + * Initialize properties on the tile engine. * @memberof kontra.tileEngine * * @param {object} properties - Properties of the tile engine. @@ -32,7 +32,7 @@ var kontra = (function(kontra, Math, undefined) { * @param {number} [properties.sy=0] - Y position to clip the tileset. * @param {Context} [properties.context=kontra.context] - Provide a context for the tile engine to draw on. */ - set: function set(properties) { + init: function init(properties) { properties = properties || {}; var _this = this; @@ -351,7 +351,7 @@ var kontra = (function(kontra, Math, undefined) { /** * Modified binary search that will return the tileset associated with the tile - * @memberOf kontra.tileEngine + * @memberof kontra.tileEngine * @private * * @param {number} tile - Tile grid. @@ -382,6 +382,8 @@ var kontra = (function(kontra, Math, undefined) { /** * Pre-render the tiles to make drawing fast. + * @memberof kontra.tileEngine + * @private */ _preRenderImage: function preRenderImage() { var _this = this; diff --git a/test/gameLoop.spec.js b/test/gameLoop.spec.js new file mode 100644 index 00000000..14fd4490 --- /dev/null +++ b/test/gameLoop.spec.js @@ -0,0 +1,190 @@ +// -------------------------------------------------- +// kontra.timestamp +// -------------------------------------------------- +describe('kontra.timestamp', function() { + + it('should return a timestamp function that returns a number', function() { + expect(kontra.timestamp).to.exist + expect(typeof kontra.timestamp).to.equal('function'); + expect(typeof kontra.timestamp()).to.equal('number'); + }); + +}); + + + + + +// -------------------------------------------------- +// kontra.gameLoop.init +// -------------------------------------------------- +describe('kontra.gameLoop.init', function() { + + it('should log an error if not passed required functions', function() { + sinon.stub(kontra, 'logError', kontra.noop); + + kontra.gameLoop(); + + expect(kontra.logError.called).to.be.ok; + + kontra.logError.restore(); + }); + +}); + + + + + +// -------------------------------------------------- +// kontra.gameLoop.start +// -------------------------------------------------- +describe('kontra.gameLoop.start', function() { + var requestAnimFrame; + + it('should call requestAnimationFrame', function() { + requestAnimFrame = sinon.stub(window, 'requestAnimationFrame', kontra.noop); + + var loop = kontra.gameLoop({ + update: kontra.noop, + render: kontra.noop + }) + + loop.start(); + + expect(window.requestAnimationFrame.called).to.be.ok; + + window.requestAnimationFrame.restore(); + }); + +}); + + + + + +// -------------------------------------------------- +// kontra.gameLoop.stop +// -------------------------------------------------- +describe('kontra.gameLoop.stop', function() { + var requestAnimFrame; + + it('should call cancelAnimationFrame', function() { + requestAnimFrame = sinon.stub(window, 'cancelAnimationFrame', kontra.noop); + + var loop = kontra.gameLoop({ + update: kontra.noop, + render: kontra.noop + }) + + loop.stop(); + + expect(window.cancelAnimationFrame.called).to.be.ok; + + window.cancelAnimationFrame.restore(); + }); + +}); + + + + + +// -------------------------------------------------- +// kontra.gameLoop._frame +// -------------------------------------------------- +describe('kontra.gameLoop._frame', function() { + + it('should call the update function and pass it dt', function() { + requestAnimFrame = sinon.stub(window, 'requestAnimationFrame', kontra.noop); + + var count = 0; + var dt = 0; + + var loop = kontra.gameLoop({ + update: function(time) { + count++; + dt = time; + }, + render: kontra.noop + }); + + // fake the first call to requestAnimationFrame, waiting 1 frame + loop.start(); + loop._last -= loop._delta; + loop._frame(); + + expect(count).to.equal(1); + expect(dt).to.be.above(0); + + window.requestAnimationFrame.restore(); + }); + + it('should call the render function', function() { + requestAnimFrame = sinon.stub(window, 'requestAnimationFrame', kontra.noop); + + var count = 0; + + var loop = kontra.gameLoop({ + update: kontra.noop, + render: function() { + count++; + } + }); + + // fake the first call to requestAnimationFrame + loop.start(); + loop._frame(); + + expect(count).to.equal(1); + + window.requestAnimationFrame.restore(); + }); + + it('should exit early if time elapsed is greater than 1000ms', function() { + requestAnimFrame = sinon.stub(window, 'requestAnimationFrame', kontra.noop); + + var count = 0; + + var loop = kontra.gameLoop({ + update: function(time) { + count++; + }, + render: function() { + count++; + } + }); + + // fake the first call to requestAnimationFrame + loop.start(); + loop._last -= 1500; + loop._frame(); + + expect(count).to.equal(0); + + window.requestAnimationFrame.restore(); + }); + + it('should make multiple calls to the update function if enough time has elapsed', function() { + requestAnimFrame = sinon.stub(window, 'requestAnimationFrame', kontra.noop); + + var count = 0; + + var loop = kontra.gameLoop({ + update: function(time) { + count++; + }, + render: kontra.noop + }); + + // fake the first call to requestAnimationFrame + loop.start(); + loop._last -= loop._delta * 2.5; + loop._frame(); + + expect(count).to.equal(2); + + window.requestAnimationFrame.restore(); + }); + +}); \ No newline at end of file diff --git a/test/phantom.polyfill.js b/test/phantom.polyfill.js index e21a07ab..dd581888 100644 --- a/test/phantom.polyfill.js +++ b/test/phantom.polyfill.js @@ -91,4 +91,28 @@ if (typeof Function.prototype.bind != 'function') { }; window.Audio = Audio; +})(); + +/** + * RequestAnimationFrame polyfill for PhantomJS. + */ +(function() { + // only set if requestAnimationFrame is not defined + if (!window.requestAnimationFrame) { + window.requestAnimationFrame = function(callback, element) { + var currTime = new Date().getTime(); + var timeToCall = Math.max(0, 16 - (currTime - lastTime)); + var id = window.setTimeout(function() { callback(currTime + timeToCall); }, + timeToCall); + lastTime = currTime + timeToCall; + return id; + }; + } + + // only set if cancelAnimationFrame is not defined + if (!window.cancelAnimationFrame) { + window.cancelAnimationFrame = function(id) { + clearTimeout(id); + }; + } })(); \ No newline at end of file diff --git a/test/pool.spec.js b/test/pool.spec.js index 739c8788..12aa2c0a 100644 --- a/test/pool.spec.js +++ b/test/pool.spec.js @@ -9,7 +9,7 @@ describe('', function() { update: function() { this.timeToLive--; }, - set: function(properties) { + init: function(properties) { this.alive = properties.alive; for(prop in properties) { @@ -22,37 +22,38 @@ describe('', function() { }; } - // stub the logError function so we can test errors being logged - beforeEach(function() { - sinon.stub(kontra, 'logError', kontra.noop); - }); - - afterEach(function() { - kontra.logError.restore(); - }); - // -------------------------------------------------- - // kontra.pool.set + // kontra.pool.init // -------------------------------------------------- - describe('kontra.pool.set', function() { + describe('kontra.pool.init', function() { it('should log an error if the create function is not passed', function() { + sinon.stub(kontra, 'logError', kontra.noop); + kontra.pool({}); expect(kontra.logError.called).to.be.ok; + + kontra.logError.restore(); }); it('should log an error if the create function did not return an object', function() { + sinon.stub(kontra, 'logError', kontra.noop); + kontra.pool({create:kontra.noop}); expect(kontra.logError.called).to.be.ok; + + kontra.logError.restore(); }); it('should log an error if the create function returned an object with missing functions', function() { + sinon.stub(kontra, 'logError', kontra.noop); + kontra.pool({create: function() { return { render: kontra.noop @@ -60,6 +61,8 @@ describe('', function() { }}); expect(kontra.logError.called).to.be.ok; + + kontra.logError.restore(); }); it('should fill the pool when passed the fill property', function() { @@ -75,15 +78,21 @@ describe('', function() { }); it('should log an error if you try to fill the pool with a default maxSize', function() { + sinon.stub(kontra, 'logError', kontra.noop); + kontra.pool({ create: sprite, fill: true }); expect(kontra.logError.called).to.be.ok; + + kontra.logError.restore(); }); it('should call the create function and pass it createProperties', function() { + sinon.stub(kontra, 'logError', kontra.noop); + var spy = sinon.spy(); var props = {foo: 'bar'}; @@ -93,6 +102,8 @@ describe('', function() { }); expect(spy.calledWith(props)).to.be.ok; + + kontra.logError.restore(); }); }); @@ -106,12 +117,12 @@ describe('', function() { // -------------------------------------------------- describe('kontra.pool.get', function() { - it('should call the objects set function', function() { + it('should call the objects init function', function() { var pool = kontra.pool({ create: sprite }); - var spy = sinon.spy(pool.objects[0], 'set'); + var spy = sinon.spy(pool.objects[0], 'init'); pool.get(); @@ -268,7 +279,7 @@ describe('', function() { update: function() { count++; }, - set: function(properties) { this.alive = properties.alive; }, + init: function(properties) { this.alive = properties.alive; }, isAlive: function() { return this.alive; }, } }, @@ -340,7 +351,7 @@ describe('', function() { render: function() { count++; }, - set: function(properties) { this.alive = properties.alive; }, + init: function(properties) { this.alive = properties.alive; }, isAlive: function() { return this.alive; }, } },