diff --git a/examples/extendingSprite.html b/examples/extendingSprite.html new file mode 100644 index 00000000..e0df21c3 --- /dev/null +++ b/examples/extendingSprite.html @@ -0,0 +1,101 @@ + + +
+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 && value.nodeName.toLowerCase() === 'img';\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 && value.nodeName.toLowerCase() === 'canvas';\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._proto.set for list of parameters.\n */\n kontra.gameLoop = function(properties) {\n var gameLoop = Object.create(kontra.gameLoop._proto);\n gameLoop.set(properties);\n\n return gameLoop;\n };\n\n kontra.gameLoop._proto = {\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._proto.set for list of parameters.\n */\n kontra.pool = function(properties) {\n var pool = Object.create(kontra.pool._proto);\n pool.set(properties);\n\n return pool;\n };\n\n kontra.pool._proto = {\n /**\n * Set properties on the 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 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 ReferenceError('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 while (this.objects.length < this.maxSize) {\n this.objects.unshift(this.create());\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 update: function update() {\n var i = this.lastIndex;\n var obj;\n\n // only iterate over the objects that are alive\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 know\n // which object came from which pool. Instead, we'll just prevent the index from going below\n // 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 while (i >= index) {\n obj = this.objects[i];\n\n obj.update();\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._proto.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._proto);\n quadtree.set(properties);\n\n return quadtree;\n };\n\n kontra.quadtree._proto = {\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 /**\n * A vector for 2D space.\n * @memberof kontra\n *\n * @see kontra.vector._proto.set for list of parameters.\n */\n kontra.vector = function(x, y) {\n var vector = Object.create(kontra.vector._proto);\n vector.set(x, y);\n\n return vector;\n };\n\n kontra.vector._proto = {\n /**\n * Set the vector's 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 set: function set(x, y) {\n this.x = x || 0;\n this.y = y || 0;\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 - Min x value.\n * @param {number} yMin - Min y value.\n * @param {number} xMax - Max x value.\n * @param {number} yMax - Max y value.\n */\n clamp: function clamp(xMin, yMin, xMax, yMax) {\n\n // overwrite add function to clamp the final values.\n this.add = function clampAdd(vector, dt) {\n var x = this.x + (vector.x || 0) * (dt || 1);\n var y = this.y + (vector.y || 0) * (dt || 1);\n\n this.x = Math.min( Math.max(x, xMin), xMax );\n this.y = Math.min( Math.max(y, yMin), yMax );\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._proto);\n sprite.position = kontra.vector();\n sprite.velocity = kontra.vector();\n sprite.acceleration = kontra.vector();\n sprite.set(properties);\n\n return sprite;\n };\n\n kontra.sprite._proto = {\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 {object} [properties.properties] - Additional properties to set on the sprite.\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.set(properties.x, properties.y);\n _this.velocity.set(properties.dx, properties.dy);\n _this.acceleration.set(properties.ddx, properties.ddy);\n _this.timeToLive = properties.timeToLive || 0;\n\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 if (properties.update) {\n _this.update = properties.update;\n }\n\n if (properties.render) {\n _this.render = properties.render;\n }\n\n // loop through all additional properties and add them to the sprite\n for (var prop in properties.properties) {\n if (properties.properties.hasOwnProperty(prop)) {\n _this[prop] = properties.properties[prop];\n }\n }\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._proto.set for list of parameters.\n */\n kontra.animation = function(properties) {\n var animation = Object.create(kontra.animation._proto);\n animation.set(properties);\n\n return animation;\n };\n\n kontra.animation._proto = {\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 {integer} properties.x - X position to draw\n * @param {integer} 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._proto.set for list of parameters.\n */\n kontra.spriteSheet = function(properties) {\n var spriteSheet = Object.create(kontra.spriteSheet._proto);\n spriteSheet.set(properties);\n\n return spriteSheet;\n };\n\n kontra.spriteSheet._proto = {\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);"],"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"],"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","nodeName","toLowerCase","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","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","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","vector","dt","clamp","xMin","yMin","xMax","yMax","min","sprite","advanceSprite","velocity","acceleration","timeToLive","drawRect","fillStyle","color","fillRect","drawImage","advanceAnimation","currentAnimation","drawAnimation","playAnimation","animations","dx","dy","ddx","ddy","advance","draw","prop","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"],"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.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 && value.nodeName.toLowerCase() === 'img';\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 && value.nodeName.toLowerCase() === 'canvas';\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 *\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 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 ReferenceError('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 while (this.objects.length < this.maxSize) {\n this.objects.unshift(this.create());\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 update: function update() {\n var i = this.lastIndex;\n var obj;\n\n // only iterate over the objects that are alive\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 know\n // which object came from which pool. Instead, we'll just prevent the index from going below\n // 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 while (i >= index) {\n obj = this.objects[i];\n\n obj.update();\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 /**\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 vector's 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 - Min x value.\n * @param {number} yMin - Min y value.\n * @param {number} xMax - Max x value.\n * @param {number} yMax - Max y value.\n */\n clamp: function clamp(xMin, yMin, xMax, yMax) {\n\n // overwrite add function to clamp the final values.\n this.add = function clampAdd(vector, dt) {\n var x = this.x + (vector.x || 0) * (dt || 1);\n var y = this.y + (vector.y || 0) * (dt || 1);\n\n this.x = Math.min( Math.max(x, xMin), xMax );\n this.y = Math.min( Math.max(y, yMin), yMax );\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 {object} [properties.properties] - Additional properties to set on the sprite.\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 if (properties.update) {\n _this.update = properties.update;\n }\n\n if (properties.render) {\n _this.render = properties.render;\n }\n\n // loop through all additional properties and add them to the sprite\n for (var prop in properties.properties) {\n if (properties.properties.hasOwnProperty(prop)) {\n _this[prop] = properties.properties[prop];\n }\n }\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 {integer} properties.x - X position to draw\n * @param {integer} 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);"],"sourceRoot":"/source/"}
\ No newline at end of file
diff --git a/src/gameLoop.js b/src/gameLoop.js
index 0043baaf..03a05d05 100644
--- a/src/gameLoop.js
+++ b/src/gameLoop.js
@@ -25,16 +25,16 @@ var kontra = (function(kontra, window) {
* Game loop that updates and renders the game every frame.
* @memberof kontra
*
- * @see kontra.gameLoop._proto.set for list of parameters.
+ * @see kontra.gameLoop.prototype.set for list of parameters.
*/
kontra.gameLoop = function(properties) {
- var gameLoop = Object.create(kontra.gameLoop._proto);
+ var gameLoop = Object.create(kontra.gameLoop.prototype);
gameLoop.set(properties);
return gameLoop;
};
- kontra.gameLoop._proto = {
+ kontra.gameLoop.prototype = {
/**
* Set properties on the game loop.
* @memberof kontra.gameLoop
diff --git a/src/pool.js b/src/pool.js
index 8248a2f1..756dd374 100644
--- a/src/pool.js
+++ b/src/pool.js
@@ -8,16 +8,16 @@ 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._proto.set for list of parameters.
+ * @see kontra.pool.prototype.set for list of parameters.
*/
kontra.pool = function(properties) {
- var pool = Object.create(kontra.pool._proto);
+ var pool = Object.create(kontra.pool.prototype);
pool.set(properties);
return pool;
};
- kontra.pool._proto = {
+ kontra.pool.prototype = {
/**
* Set properties on the pool.
*
diff --git a/src/quadtree.js b/src/quadtree.js
index 4e96a098..a1941201 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._proto.set for list of parameters.
+ * @see kontra.quadtree.prototype.set for list of parameters.
*L
* The quadrant indices are numbered as follows (following a z-order curve):
* |
@@ -19,13 +19,13 @@ var kontra = (function(kontra, undefined) {
* |
*/
kontra.quadtree = function(properties) {
- var quadtree = Object.create(kontra.quadtree._proto);
+ var quadtree = Object.create(kontra.quadtree.prototype);
quadtree.set(properties);
return quadtree;
};
- kontra.quadtree._proto = {
+ kontra.quadtree.prototype = {
/**
* Set properties on the quadtree.
* @memberof kontra.quadtree
diff --git a/src/sprite.js b/src/sprite.js
index a6832d26..41ba7b17 100644
--- a/src/sprite.js
+++ b/src/sprite.js
@@ -5,26 +5,30 @@ var kontra = (function(kontra, Math, undefined) {
* A vector for 2D space.
* @memberof kontra
*
- * @see kontra.vector._proto.set for list of parameters.
+ * @see kontra.vector.prototype.set for list of parameters.
*/
kontra.vector = function(x, y) {
- var vector = Object.create(kontra.vector._proto);
+ var vector = Object.create(kontra.vector.prototype);
vector.set(x, y);
return vector;
};
- kontra.vector._proto = {
+ kontra.vector.prototype = {
/**
* Set the vector's x and y position.
* @memberof kontra.vector
*
* @param {number} x=0 - Center x coordinate.
* @param {number} y=0 - Center y coordinate.
+ *
+ * @returns {vector}
*/
set: function set(x, y) {
this.x = x || 0;
this.y = y || 0;
+
+ return this;
},
/**
@@ -74,16 +78,13 @@ var kontra = (function(kontra, Math, undefined) {
* @see kontra.sprite._prot.set for list of parameters.
*/
kontra.sprite = function(properties) {
- var sprite = Object.create(kontra.sprite._proto);
- sprite.position = kontra.vector();
- sprite.velocity = kontra.vector();
- sprite.acceleration = kontra.vector();
+ var sprite = Object.create(kontra.sprite.prototype);
sprite.set(properties);
return sprite;
};
- kontra.sprite._proto = {
+ kontra.sprite.prototype = {
/**
* Move the sprite by its velocity.
* @memberof kontra.sprite
@@ -194,11 +195,11 @@ var kontra = (function(kontra, Math, undefined) {
var _this = this;
- _this.position.set(properties.x, properties.y);
- _this.velocity.set(properties.dx, properties.dy);
- _this.acceleration.set(properties.ddx, properties.ddy);
- _this.timeToLive = properties.timeToLive || 0;
+ _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.timeToLive = properties.timeToLive || 0;
_this.context = properties.context || kontra.context;
// image sprite
diff --git a/src/spriteSheet.js b/src/spriteSheet.js
index 37de4013..cfd3f72c 100644
--- a/src/spriteSheet.js
+++ b/src/spriteSheet.js
@@ -7,16 +7,16 @@ var kontra = (function(kontra, undefined) {
* Single animation from a sprite sheet.
* @memberof kontra
*
- * @see kontra.pool._proto.set for list of parameters.
+ * @see kontra.pool.prototype.set for list of parameters.
*/
kontra.animation = function(properties) {
- var animation = Object.create(kontra.animation._proto);
+ var animation = Object.create(kontra.animation.prototype);
animation.set(properties);
return animation;
};
- kontra.animation._proto = {
+ kontra.animation.prototype = {
/**
* Set properties on the animation.
* @memberof kontra.animation
@@ -133,16 +133,16 @@ var kontra = (function(kontra, undefined) {
* Create a sprite sheet from an image.
* @memberof kontra
*
- * @see kontra.spriteSheet._proto.set for list of parameters.
+ * @see kontra.spriteSheet.prototype.set for list of parameters.
*/
kontra.spriteSheet = function(properties) {
- var spriteSheet = Object.create(kontra.spriteSheet._proto);
+ var spriteSheet = Object.create(kontra.spriteSheet.prototype);
spriteSheet.set(properties);
return spriteSheet;
};
- kontra.spriteSheet._proto = {
+ kontra.spriteSheet.prototype = {
/**
* Set properties on the spriteSheet.
* @memberof kontra