+ * Returns the x, y, w, h properties as a rect object
+ * (a rect object is just an object with the keys _x, _y, _w, _h).
+ *
+ * The keys have an underscore prefix. This is due to the x, y, w, h
+ * properties being merely setters and getters that wrap the properties with an underscore (_x, _y, _w, _h).
+ */
+ pos: function () {
+ return {
+ _x: (this._x),
+ _y: (this._y),
+ _w: (this._w),
+ _h: (this._h)
+ };
+ },
+
+ /**@
+ * #.mbr
+ * @comp 2D
+ * @sign public Object .mbr()
+ * Returns the minimum bounding rectangle. If there is no rotation
+ * on the entity it will return the rect.
+ */
+ mbr: function () {
+ if (!this._mbr) return this.pos();
+ return {
+ _x: (this._mbr._x),
+ _y: (this._mbr._y),
+ _w: (this._mbr._w),
+ _h: (this._mbr._h)
+ };
+ },
+
+ /**@
+ * #.isAt
+ * @comp 2D
+ * @sign public Boolean .isAt(Number x, Number y)
+ * @param x - X position of the point
+ * @param y - Y position of the point
+ * Determines whether a point is contained by the entity. Unlike other methods,
+ * an object can't be passed. The arguments require the x and y value.
+ *
+ * The given point is tested against the first of the following that exists: a mapArea associated with "Mouse", the hitarea associated with "Collision", or the object's MBR.
+ */
+ isAt: function (x, y) {
+ if (this.mapArea) {
+ return this.mapArea.containsPoint(x, y);
+ } else if (this.map) {
+ return this.map.containsPoint(x, y);
+ }
+ var mbr = this._mbr || this;
+ return mbr._x <= x && mbr._x + mbr._w >= x &&
+ mbr._y <= y && mbr._y + mbr._h >= y;
+ },
+
+ /**@
+ * #.move
+ * @comp 2D
+ * @sign public this .move(String dir, Number by)
+ * @param dir - Direction to move (n,s,e,w,ne,nw,se,sw)
+ * @param by - Amount to move in the specified direction
+ * Quick method to move the entity in a direction (n, s, e, w, ne, nw, se, sw) by an amount of pixels.
+ * Adding this component to an entity with a Collision component will cause its collision polygon to be drawn to the debug canvas as an outline
+ *
+ * The methods of DebugCanvas can be used to control this component's appearance.
+ * @see DebugPolygon, DebugCanvas
+ */
+Crafty.c("WiredHitBox", {
+ init: function () {
+ this.requires("DebugPolygon")
+ .debugStroke("red")
+ .matchHitBox();
+ this.bind("NewHitbox", this.matchHitBox);
+ },
+ matchHitBox: function () {
+ this.debugPolygon(this.map);
+ }
+});
+
+/**@
+ * #SolidHitBox
+ * @category Debug
+ *
+ * Adding this component to an entity with a Collision component will cause its collision polygon to be drawn to the debug canvas, with a default alpha level of 0.7.
+ *
+ * The methods of DebugCanvas can be used to control this component's appearance.
+ * @see DebugPolygon, DebugCanvas
+ */
+Crafty.c("SolidHitBox", {
+ init: function () {
+ this.requires("Collision, DebugPolygon")
+ .debugFill("orange").debugAlpha(0.7)
+ .matchHitBox();
+ this.bind("NewHitbox", this.matchHitBox);
+ },
+ matchHitBox: function () {
+ this.debugPolygon(this.map);
+ }
+});
+
+Crafty.DebugCanvas = {
+ context: null,
+ entities: [],
+ onetimeEntities: [],
+ add: function (ent) {
+ this.entities.push(ent);
+ },
+
+ remove: function (ent) {
+ var list = this.entities;
+ for (var i = list.length - 1; i >= 0; i--)
+ if (list[i] == ent)
+ list.splice(i, 1);
+
+ },
+
+ // Mostly copied from canvas.init()
+ // Called the first time a "DebugCanvas" component is added to an entity
+ // We should consider how to abstract the idea of multiple canvases
+ init: function () {
+ if (!Crafty.DebugCanvas.context) {
+ //check if canvas is supported
+ if (!Crafty.support.canvas) {
+ Crafty.trigger("NoCanvas");
+ Crafty.stop();
+ return;
+ }
+
+ //create an empty canvas element
+ var c;
+ c = document.createElement("canvas");
+ c.width = Crafty.viewport.width;
+ c.height = Crafty.viewport.height;
+ c.style.position = 'absolute';
+ c.style.left = "0px";
+ c.style.top = "0px";
+ c.id = "debug-canvas";
+ // The debug canvas should be on the very top; the highest a regular zindex can get is ~10000
+ * @param cellsize - the cell size. If omitted, `cellsize` is 64.
+ *
+ * Set `cellsize`.
+ * And create `this.map`.
+ */
+ var cellsize,
+
+ HashMap = function (cell) {
+ cellsize = cell || 64;
+ this.map = {};
+ },
+
+ SPACE = " ",
+ keyHolder = {};
+
+ HashMap.prototype = {
+ /**@
+ * #Crafty.map.insert
+ * @comp Crafty.map
+ * @sign public Object Crafty.map.insert(Object obj)
+ * @param obj - An entity to be inserted.
+ *
+ * `obj` is inserted in '.map' of the corresponding broad phase cells. An object of the following fields is returned.
+ * ~~~
+ * - the object that keep track of cells (keys)
+ * - `obj`
+ * - the HashMap object
+ * ~~~
+ */
+ insert: function (obj) {
+ var keys = HashMap.key(obj),
+ entry = new Entry(keys, obj, this),
+ i = 0,
+ j,
+ hash;
+
+ //insert into all x buckets
+ for (i = keys.x1; i <= keys.x2; i++) {
+ //insert into all y buckets
+ for (j = keys.y1; j <= keys.y2; j++) {
+ hash = (i << 16) ^ j;
+ if (!this.map[hash]) this.map[hash] = [];
+ this.map[hash].push(obj);
+ }
+ }
+
+ return entry;
+ },
+
+ /**@
+ * #Crafty.map.search
+ * @comp Crafty.map
+ * @sign public Object Crafty.map.search(Object rect[, Boolean filter])
+ * @param rect - the rectangular region to search for entities.
+ * @param filter - Default value is true. Otherwise, must be false.
+ *
+ * - If `filter` is `false`, just search for all the entries in the give `rect` region by broad phase collision. Entity may be returned duplicated.
+ * - If `filter` is `true`, filter the above results by checking that they actually overlap `rect`.
+ * The easier usage is with `filter`=`true`. For performance reason, you may use `filter`=`false`, and filter the result yourself. See examples in drawing.js and collision.js
+ */
+
+ search: function (rect, filter) {
+ var keys = HashMap.key(rect, keyHolder),
+ i, j, k,
+ results = [];
+
+ if (filter === undefined) filter = true; //default filter to true
+
+ //search in all x buckets
+ for (i = keys.x1; i <= keys.x2; i++) {
+ //insert into all y buckets
+ for (j = keys.y1; j <= keys.y2; j++) {
+ cell = this.map[(i << 16) ^ j];
+ if (cell) {
+ for (k = 0; k < cell.length; k++)
+ results.push(cell[k]);
+ }
+ }
+ }
+
+ if (filter) {
+ var obj, id, finalresult = [],
+ found = {};
+ //add unique elements to lookup table with the entity ID as unique key
+ for (i = 0, l = results.length; i < l; i++) {
+ obj = results[i];
+ if (!obj) continue; //skip if deleted
+ id = obj[0]; //unique ID
+ obj = obj._mbr || obj;
+ //check if not added to hash and that actually intersects
+ //loop over lookup table and copy to final array
+ for (obj in found) finalresult.push(found[obj]);
+
+ return finalresult;
+ } else {
+ return results;
+ }
+ },
+
+ /**@
+ * #Crafty.map.remove
+ * @comp Crafty.map
+ * @sign public void Crafty.map.remove([Object keys, ]Object obj)
+ * @param keys - key region. If omitted, it will be derived from obj by `Crafty.HashMap.key`.
+ * @param obj - need more document.
+ *
+ * Remove an entity in a broad phase map.
+ * - The second form is only used in Crafty.HashMap to save time for computing keys again, where keys were computed previously from obj. End users should not call this form directly.
+ *
+ * @example
+ * ~~~
+ * Crafty.map.remove(e);
+ * ~~~
+ */
+ remove: function (keys, obj) {
+ var i = 0,
+ j, hash;
+
+ if (arguments.length == 1) {
+ obj = keys;
+ keys = HashMap.key(obj, keyHolder);
+ }
+
+ //search in all x buckets
+ for (i = keys.x1; i <= keys.x2; i++) {
+ //insert into all y buckets
+ for (j = keys.y1; j <= keys.y2; j++) {
+ hash = (i << 16) ^ j;
+
+ if (this.map[hash]) {
+ var cell = this.map[hash],
+ m, n = cell.length;
+ //loop over objs in cell and delete
+ for (m = 0; m < n; m++)
+ if (cell[m] && cell[m][0] === obj[0])
+ cell.splice(m, 1);
+ }
+ }
+ }
+ },
+
+ /**@
+ * #Crafty.map.refresh
+ * @comp Crafty.map
+ * @sign public void Crafty.map.remove(Entry entry)
+ * @param entry - An entry to update
+ *
+ * Refresh an entry's keys, and its position in the broad phrase map.
+ *
+ * @example
+ * ~~~
+ * Crafty.map.refresh(e);
+ * ~~~
+ */
+ refresh: function (entry) {
+ var keys = entry.keys;
+ var obj = entry.obj;
+ var cell, i, j, m, n;
+
+ //First delete current object from appropriate cells
+ for (i = keys.x1; i <= keys.x2; i++) {
+ for (j = keys.y1; j <= keys.y2; j++) {
+ cell = this.map[(i << 16) ^ j];
+ if (cell) {
+ n = cell.length;
+ //loop over objs in cell and delete
+ for (m = 0; m < n; m++)
+ if (cell[m] && cell[m][0] === obj[0])
+ cell.splice(m, 1);
+ }
+ }
+ }
+
+ //update keys
+ HashMap.key(obj, keys);
+
+ //insert into all rows and columns
+ for (i = keys.x1; i <= keys.x2; i++) {
+ for (j = keys.y1; j <= keys.y2; j++) {
+ cell = this.map[(i << 16) ^ j];
+ if (!cell) cell = this.map[(i << 16) ^ j] = [];
+ cell.push(obj);
+ }
+ }
+
+ return entry;
+ },
+
+
+
+
+ /**@
+ * #Crafty.map.boundaries
+ * @comp Crafty.map
+ * @sign public Object Crafty.map.boundaries()
+ *
+ * The return `Object` is of the following format.
+ * ~~~
+ * {
+ * min: {
+ * x: val_x,
+ * y: val_y
+ * },
+ * max: {
+ * x: val_x,
+ * y: val_y
+ * }
+ * }
+ * ~~~
+ */
+ boundaries: function () {
+ var k, ent,
+ hash = {
+ max: {
+ x: -Infinity,
+ y: -Infinity
+ },
+ min: {
+ x: Infinity,
+ y: Infinity
+ }
+ },
+ coords = {
+ max: {
+ x: -Infinity,
+ y: -Infinity
+ },
+ min: {
+ x: Infinity,
+ y: Infinity
+ }
+ };
+
+ //Using broad phase hash to speed up the computation of boundaries.
+ for (var h in this.map) {
+ if (!this.map[h].length) continue;
+
+ //broad phase coordinate
+ var i = h >> 16,
+ j = (h << 16) >> 16;
+ if (j < 0) {
+ i = i ^ -1;
+ }
+ if (i >= hash.max.x) {
+ hash.max.x = i;
+ for (k in this.map[h]) {
+ ent = this.map[h][k];
+ //make sure that this is a Crafty entity
+ if (typeof ent == 'object' && 'requires' in ent) {
+ if (typeof ent == 'object' && 'requires' in ent) {
+ coords.min.y = Math.min(coords.min.y, ent.y);
+ }
+ }
+ }
+ }
+
+ return coords;
+ }
+ };
+
+ /**@
+ * #Crafty.HashMap
+ * @category 2D
+ * Broad-phase collision detection engine. See background information at
+ *
+ * - [N Tutorial B - Broad-Phase Collision](http://www.metanetsoftware.com/technique/tutorialB.html)
+ * - [Broad-Phase Collision Detection with CUDA](http.developer.nvidia.com/GPUGems3/gpugems3_ch32.html)
+ * @see Crafty.map
+ */
+
+ /**@
+ * #Crafty.HashMap.key
+ * @comp Crafty.HashMap
+ * @sign public Object Crafty.HashMap.key(Object obj)
+ * @param obj - an Object that has .mbr() or _x, _y, _w and _h.
+ * Get the rectangular region (in terms of the grid, with grid size `cellsize`), where the object may fall in. This region is determined by the object's bounding box.
+ * @trigger Draw - when the entity is ready to be drawn to the stage - {type: "canvas", pos, co, ctx}
+ * @trigger NoCanvas - if the browser does not support canvas
+ *
+ * When this component is added to an entity it will be drawn to the global canvas element. The canvas element (and hence all Canvas entities) is always rendered below any DOM entities.
+ *
+ * Crafty.canvas.init() will be automatically called if it is not called already to initialize the canvas element.
+ *
+ * Create a canvas entity like this
+ * ~~~
+ * var myEntity = Crafty.e("2D, Canvas, Color")
+ * .color("green")
+ * .attr({x: 13, y: 37, w: 42, h: 42});
+ *~~~
+ */
+Crafty.c("Canvas", {
+
+ init: function () {
+ if (!Crafty.canvas.context) {
+ Crafty.canvas.init();
+ }
+
+ //increment the amount of canvas objs
+ Crafty.DrawManager.total2D++;
+ //Allocate an object to hold this components current region
+ this.currentRect = {};
+ this._changed = true;
+ Crafty.DrawManager.addCanvas(this);
+
+ this.bind("Invalidate", function (e) {
+ //flag if changed
+ if (this._changed === false) {
+ this._changed = true;
+ Crafty.DrawManager.addCanvas(this);
+ }
+
+ });
+
+
+ this.bind("Remove", function () {
+ Crafty.DrawManager.total2D--;
+ this._changed = true;
+ Crafty.DrawManager.addCanvas(this);
+ });
+ },
+
+ /**@
+ * #.draw
+ * @comp Canvas
+ * @sign public this .draw([[Context ctx, ]Number x, Number y, Number w, Number h])
+ * @param ctx - Canvas 2D context if drawing on another canvas is required
+ * @param x - X offset for drawing a segment
+ * @param y - Y offset for drawing a segment
+ * @param w - Width of the segment to draw
+ * @param h - Height of the segment to draw
+ *
+ * Method to draw the entity on the canvas element. Can pass rect values for redrawing a segment of the entity.
+ */
+
+ // Cache the various objects and arrays used in draw:
+ * @sign public this .onHit(String component, Function hit[, Function noHit])
+ * @param component - Component to check collisions for
+ * @param hit - Callback method to execute upon collision with component. Will be passed the results of the collision check in the same format documented for hit().
+ * @param noHit - Callback method executed once as soon as collision stops
+ *
+ * Creates an EnterFrame event calling .hit() each frame. When a collision is detected the callback will be invoked.
+ * Stops entity from being draggable. Reenable with `.enableDrag()`.
+ *
+ * @see .enableDrag
+ */
+ disableDrag: function () {
+ this.unbind("MouseDown", this._ondown);
+ if (this._dragging) {
+ this.stopDrag();
+ }
+ return this;
+ }
+});
+
+/**@
+ * #Keyboard
+ * @category Input
+ * Give entities keyboard events (`keydown` and `keyup`).
+ */
+Crafty.c("Keyboard", {
+ /**@
+ * #.isDown
+ * @comp Keyboard
+ * @sign public Boolean isDown(String keyName)
+ * @param keyName - Name of the key to check. See `Crafty.keys`.
+ * @sign public Boolean isDown(Number keyCode)
+ * @param keyCode - Key code in `Crafty.keys`.
+ *
+ * Determine if a certain key is currently down.
+ *
+ * @example
+ * ~~~
+ * entity.requires('Keyboard').bind('KeyDown', function () { if (this.isDown('SPACE')) jump(); });
+ * ~~~
+ *
+ * @see Crafty.keys
+ */
+ isDown: function (key) {
+ if (typeof key === "string") {
+ key = Crafty.keys[key];
+ }
+ return !!Crafty.keydown[key];
+ }
+});
+
+/**@
+ * #Multiway
+ * @category Input
+ * Used to bind keys to directions and have the entity move accordingly
+ * @trigger NewDirection - triggered when direction changes - { x:Number, y:Number } - New direction
+ * @trigger Moved - triggered on movement on either x or y axis. If the entity has moved on both axes for diagonal movement the event is triggered twice - { x:Number, y:Number } - Old position
+ * Move an entity in four directions by using the
+ * arrow keys or `W`, `A`, `S`, `D`.
+ */
+Crafty.c("Fourway", {
+
+ init: function () {
+ this.requires("Multiway");
+ },
+
+ /**@
+ * #.fourway
+ * @comp Fourway
+ * @sign public this .fourway(Number speed)
+ * @param speed - Amount of pixels to move the entity whilst a key is down
+ * Constructor to initialize the speed. Component will listen for key events and move the entity appropriately.
+ * This includes `Up Arrow`, `Right Arrow`, `Down Arrow`, `Left Arrow` as well as `W`, `A`, `S`, `D`.
+ *
+ * When direction changes a NewDirection event is triggered with an object detailing the new direction: {x: x_movement, y: y_movement}
+ * When entity has moved on either x- or y-axis a Moved event is triggered with an object specifying the old position {x: old_x, y: old_y}
+ *
+ * The key presses will move the entity in that direction by the speed passed in the argument.
+ *
+ * @see Multiway
+ */
+ fourway: function (speed) {
+ this.multiway(speed, {
+ UP_ARROW: -90,
+ DOWN_ARROW: 90,
+ RIGHT_ARROW: 0,
+ LEFT_ARROW: 180,
+ W: -90,
+ S: 90,
+ D: 0,
+ A: 180,
+ Z: -90,
+ Q: 180
+ });
+
+ return this;
+ }
+});
+
+/**@
+ * #Twoway
+ * @category Input
+ * @trigger NewDirection - When direction changes a NewDirection event is triggered with an object detailing the new direction: {x: x_movement, y: y_movement}. This is consistent with Fourway and Multiway components.
+ * @trigger Moved - When entity has moved on x-axis a Moved event is triggered with an object specifying the old position {x: old_x, y: old_y}
+ *
+ * Move an entity left or right using the arrow keys or `D` and `A` and jump using up arrow or `W`.
+ */
+Crafty.c("Twoway", {
+ _speed: 3,
+ _up: false,
+
+ init: function () {
+ this.requires("Fourway, Keyboard, Gravity");
+ },
+
+ /**@
+ * #.twoway
+ * @comp Twoway
+ * @sign public this .twoway(Number speed[, Number jump])
+ * @param speed - Amount of pixels to move left or right
+ * @param jump - Vertical jump speed
+ *
+ * Constructor to initialize the speed and power of jump. Component will
+ * listen for key events and move the entity appropriately. This includes
+ * `Up Arrow`, `Right Arrow`, `Left Arrow` as well as `W`, `A`, `D`. Used with the
+ * `gravity` component to simulate jumping.
+ *
+ * The key presses will move the entity in that direction by the speed passed in
+ * the argument. Pressing the `Up Arrow` or `W` will cause the entity to jump.
+ * Select a set of or single entities by components or an entity's ID.
+ *
+ * Crafty uses syntax similar to jQuery by having a selector engine to select entities by their components.
+ *
+ * If there is more than one match, the return value is an Array-like object listing the ID numbers of each matching entity. If there is exactly one match, the entity itself is returned. If you're not sure how many matches to expect, check the number of matches via Crafty(...).length. Alternatively, use Crafty(...).each(...), which works in all cases.
+ *
+ * @example
+ * ~~~
+ * Crafty("MyComponent")
+ * Crafty("Hello 2D Component")
+ * Crafty("Hello, 2D, Component")
+ * ~~~
+ *
+ * The first selector will return all entities that have the component `MyComponent`. The second will return all entities that have `Hello` and `2D` and `Component` whereas the last will return all entities that have at least one of those components (or).
+ *
+ * ~~~
+ * Crafty("*")
+ * ~~~
+ * Passing `*` will select all entities.
+ *
+ * ~~~
+ * Crafty(1)
+ * ~~~
+ * Passing an integer will select the entity with that `ID`.
+ *
+ * To work directly with an array of entities, use the `get()` method on a selection.
+ * To call a function in the context of each entity, use the `.each()` method.
+ *
+ * The event related methods such as `bind` and `trigger` will work on selections of entities.
+ * @sign public this .setter(String property, Function callback)
+ * @param property - Property to watch for modification
+ * @param callback - Method to execute if the property is modified
+ * Will watch a property waiting for modification and will then invoke the
+ * given callback when attempting to modify.
+ *
+ */
+ setter: function (prop, callback) {
+ if (Crafty.support.setter) {
+ this.__defineSetter__(prop, callback);
+ } else if (Crafty.support.defineProperty) {
+ Object.defineProperty(this, prop, {
+ set: callback,
+ configurable: true
+ });
+ }
+ return this;
+ },
+
+ /**@
+ * #.destroy
+ * @comp Crafty Core
+ * @sign public this .destroy(void)
+ * Will remove all event listeners and delete all properties as well as removing from the stage
+ */
+ destroy: function () {
+ //remove all event handlers, delete from entities
+ this.each(function () {
+ var comp;
+ this.trigger("Remove");
+ for (var compName in this.__c) {
+ comp = components[compName];
+ if (comp && "remove" in comp)
+ comp.remove.call(this, true);
+ }
+ for (var e in handlers) {
+ this.unbind(e);
+ }
+ delete entities[this[0]];
+ });
+ }
+};
+
+//give the init instances the Crafty prototype
+Crafty.fn.init.prototype = Crafty.fn;
+
+
+/**@
+ * #Crafty.extend
+ * @category Core
+ * Used to extend the Crafty namespace.
+ *
+ */
+Crafty.extend = Crafty.fn.extend = function (obj) {
+ var target = this,
+ key;
+
+ //don't bother with nulls
+ if (!obj) return target;
+
+ for (key in obj) {
+ if (target === obj[key]) continue; //handle circular reference
+ target[key] = obj[key];
+ }
+
+ return target;
+};
+
+
+Crafty.extend({
+ /**@
+ * #Crafty.init
+ * @category Core
+ * @trigger Load - Just after the viewport is initialised. Before the EnterFrame loops is started
+ * @sign public this Crafty.init([Number width, Number height, String stage_elem])
+ * @sign public this Crafty.init([Number width, Number height, HTMLElement stage_elem])
+ * @param Number width - Width of the stage
+ * @param Number height - Height of the stage
+ * @param String or HTMLElement stage_elem - the element to use for the stage
+ *
+ * Sets the element to use as the stage, creating it if necessary. By default a div with id 'cr-stage' is used, but if the 'stage_elem' argument is provided that will be used instead. (see `Crafty.viewport.init`)
+ *
+ * Starts the `EnterFrame` interval. This will call the `EnterFrame` event for every frame.
+ *
+ * Can pass width and height values for the stage otherwise will default to window size (see `Crafty.DOM.window`).
+ *
+ * All `Load` events will be executed.
+ *
+ * Uses `requestAnimationFrame` to sync the drawing with the browser but will default to `setInterval` if the browser does not support it.
+ * @see Crafty.stop, Crafty.viewport
+ */
+ init: function (w, h, stage_elem) {
+ Crafty.viewport.init(w, h, stage_elem);
+
+ //call all arbitrary functions attached to onload
+ this.trigger("Load");
+ this.timer.init();
+
+ return this;
+ },
+
+ /**@
+ * #Crafty.getVersion
+ * @category Core
+ * @sign public String Crafty.getVersion()
+ * @returns Current version of Crafty as a string
+ *
+ * Return current version of crafty
+ *
+ * @example
+ * ~~~
+ * Crafty.getVersion(); //'0.5.2'
+ * ~~~
+ */
+ getVersion: function () {
+ return version;
+ },
+
+ /**@
+ * #Crafty.stop
+ * @category Core
+ * @trigger CraftyStop - when the game is stopped
+ * @sign public this Crafty.stop([bool clearState])
+ * @param clearState - if true the stage and all game state is cleared.
+ *
+ * Stops the EnterFrame interval and removes the stage element.
+ *
+ * To restart, use `Crafty.init()`.
+ * @see Crafty.init
+ */
+ stop: function (clearState) {
+ this.timer.stop();
+ if (clearState) {
+ Crafty.audio.remove();
+ if (Crafty.stage && Crafty.stage.elem.parentNode) {
+ * Pauses the game by stopping the EnterFrame event from firing. If the game is already paused it is unpaused.
+ * You can pass a boolean parameter if you want to pause or unpause mo matter what the current state is.
+ * Modern browsers pauses the game when the page is not visible to the user. If you want the Pause event
+ * to be triggered when that happens you can enable autoPause in `Crafty.settings`.
+ *
+ * @example
+ * Have an entity pause the game when it is clicked.
+ * ~~~
+ * button.bind("click", function() {
+ * Crafty.pause();
+ * });
+ * ~~~
+ */
+ pause: function (toggle) {
+ if (arguments.length === 1 ? toggle : !this._paused) {
+ this.trigger('Pause');
+ this._paused = true;
+ setTimeout(function () {
+ Crafty.timer.stop();
+ }, 0);
+ Crafty.keydown = {};
+ } else {
+ this.trigger('Unpause');
+ this._paused = false;
+ setTimeout(function () {
+ Crafty.timer.init();
+ }, 0);
+ }
+ return this;
+ },
+
+ /**@
+ * #Crafty.isPaused
+ * @category Core
+ * @sign public this Crafty.isPaused()
+ *
+ * Check whether the game is already paused or not.
+ *
+ * @example
+ * ~~~
+ * Crafty.isPaused();
+ * ~~~
+ */
+ isPaused: function () {
+ return this._paused;
+ },
+
+ /**@
+ * #Crafty.timer
+ * @category Game Loop
+ * Handles game ticks
+ */
+ timer: (function () {
+ /*
+ * `window.requestAnimationFrame` or its variants is called for animation.
+ * `.requestID` keeps a record of the return value previous `window.requestAnimationFrame` call.
+ * This is an internal variable. Used to stop frame.
+ */
+ var tick, requestID;
+
+ // Internal variables used to control the game loop. Use Crafty.timer.steptype() to set these.
+ var mode = "fixed",
+ maxFramesPerStep = 5,
+ maxTimestep = 40;
+
+ // variables used by the game loop to track state
+ var endTime = 0,
+ timeSlip = 0,
+ gameTime;
+
+ // Controls the target rate of fixed mode loop. Set these with the Crafty.timer.FPS function
+ var FPS = 50,
+ milliSecPerFrame = 1000 / FPS;
+
+
+
+
+ return {
+ init: function () {
+ // When first called, set the gametime one frame before now!
+ if (typeof gameTime === "undefined")
+ gameTime = (new Date().getTime()) - milliSecPerFrame;
+ var onFrame = window.requestAnimationFrame ||
+ window.webkitRequestAnimationFrame ||
+ window.mozRequestAnimationFrame ||
+ window.oRequestAnimationFrame ||
+ window.msRequestAnimationFrame ||
+ null;
+
+ if (onFrame) {
+ tick = function () {
+ Crafty.timer.step();
+ requestID = onFrame(tick);
+ //console.log(requestID + ', ' + frame)
+ };
+
+ tick();
+ } else {
+ tick = setInterval(function () {
+ Crafty.timer.step();
+ }, 1000 / FPS);
+ }
+ },
+
+ stop: function () {
+ Crafty.trigger("CraftyStopTimer");
+
+ if (typeof tick === "number") clearInterval(tick);
+
+ var onFrame = window.cancelRequestAnimationFrame ||
+ window.webkitCancelRequestAnimationFrame ||
+ window.mozCancelRequestAnimationFrame ||
+ window.oCancelRequestAnimationFrame ||
+ window.msCancelRequestAnimationFrame ||
+ null;
+
+ if (onFrame) onFrame(requestID);
+ tick = null;
+ },
+
+
+ /**@
+ * #Crafty.timer.steptype
+ * @comp Crafty.timer
+ * @sign public void Crafty.timer.steptype(mode [, maxTimeStep])
+ * Can be called to set the type of timestep the game loop uses
+ * @param mode - the type of time loop. Allowed values are "fixed", "semifixed", and "variable". Crafty defaults to "fixed".
+ * @param mode - For "fixed", sets the max number of frames per step. For "variable" and "semifixed", sets the maximum time step allowed.
+ *
+ * * In "fixed" mode, each frame is sent the same value of `dt`, and to achieve the target game speed, mulitiple frame events are triggered before each render.
+ * * In "variable" mode, there is only one frame triggered per render. This recieves a value of `dt` equal to the actual elapsed time since the last frame.
+ * * In "semifixed" mode, multiple frames per render are processed, and the total time since the last frame is divided evenly between them.
+ *
+ */
+
+ steptype: function (newmode, option) {
+ if (newmode === "variable" || newmode === "semifixed") {
+ mode = newmode;
+ if (option)
+ maxTimestep = option;
+
+ } else if (newmode === "fixed") {
+ mode = "fixed";
+ if (option)
+ maxFramesPerStep = option;
+ } else {
+ throw "Invalid step type specified";
+ }
+
+
+ },
+
+ /**@
+ * #Crafty.timer.step
+ * @comp Crafty.timer
+ * @sign public void Crafty.timer.step()
+ * @trigger EnterFrame - Triggered on each frame. Passes the frame number, and the amount of time since the last frame. If the time is greater than maxTimestep, that will be used instead. (The default value of maxTimestep is 50 ms.) - { frame: Number, dt:Number }
+ * @trigger RenderScene - Triggered every time a scene should be rendered
+ * @trigger MeasureWaitTime - Triggered at the beginning of each step after the first. Passes the time the game loop waited between steps. - Number
+ * @trigger MeasureFrameTime - Triggered after each step. Passes the time it took to advance one frame. - Number
+ * @trigger MeasureRenderTime - Triggered after each render. Passes the time it took to render the scene - Number
+ * Advances the game by triggering `EnterFrame` and `RenderScene`
+ */
+ step: function () {
+ var drawTimeStart, dt, lastFrameTime, loops = 0;
+ * @sign public void Crafty.c(String name, Object component)
+ * @param name - Name of the component
+ * @param component - Object with the component's properties and methods
+ * Creates a component where the first argument is the ID and the second
+ * is the object that will be inherited by entities.
+ *
+ * A couple of methods are treated specially. They are invoked in partiular contexts, and (in those contexts) cannot be overridden by other components.
+ *
+ * - `init` will be called when the component is added to an entity
+ * - `remove` will be called just before a component is removed, or before an entity is destroyed. It is passed a single boolean parameter that is `true` if the entity is being destroyed.
+ *
+ * In addition to these hardcoded special methods, there are some conventions for writing components.
+ *
+ * - Properties or methods that start with an underscore are considered private.
+ * - A method with the same name as the component is considered to be a constructor
+ * and is generally used when you need to pass configuration data to the component on a per entity basis.
+ * Crafty.e("Annoying").annoying("I'm an orange...");
+ * ~~~
+ *
+ *
+ * WARNING:
+ *
+ * in the example above the field _message is local to the entity. That is, if you create many entities with the Annoying component they can all have different values for _message. That is because it is a simple value, and simple values are copied by value. If however the field had been an object or array, the value would have been shared by all entities with the component because complex types are copied by reference in javascript. This is probably not what you want and the following example demonstrates how to work around it:
+ *
+ * ~~~
+ * Crafty.c("MyComponent", {
+ * _iAmShared: { a: 3, b: 4 },
+ * init: function() {
+ * this._iAmNotShared = { a: 3, b: 4 };
+ * },
+ * });
+ * ~~~
+ *
+ * @see Crafty.e
+ */
+ c: function (compName, component) {
+ components[compName] = component;
+ },
+
+ /**@
+ * #Crafty.trigger
+ * @category Core, Events
+ * @sign public void Crafty.trigger(String eventName, * data)
+ * @param eventName - Name of the event to trigger
+ * @param data - Arbitrary data to pass into the callback as an argument
+ *
+ * This method will trigger every single callback attached to the event name. This means
+ * every global event and every entity that has a callback.
+ *
+ * @see Crafty.bind
+ */
+ trigger: function (event, data) {
+
+ // (To learn how the handlers object works, see inline comment at Crafty.bind)
+ var hdl = handlers[event],
+ h, i, l, callbacks, context;
+ //loop over every object bound
+ for (h in hdl) {
+
+ // Check whether h needs to be processed
+ if (!hdl.hasOwnProperty(h)) continue;
+ callbacks = hdl[h];
+ if (!callbacks || callbacks.length === 0) continue;
+
+ //if an entity, call with that context; else the global context
+ if (entities[h])
+ context = Crafty(+h);
+ else
+ context = Crafty;
+
+ //loop over every handler within object
+ for (i = 0; i < callbacks.length; i++) {
+ // Remove a callback if it has been deleted
+ if (typeof callbacks[i] === "undefined") {
+ callbacks.splice(i, 1);
+ i--;
+ } else
+ callbacks[i].call(context, data);
+ }
+ }
+ },
+
+ /**@
+ * #Crafty.bind
+ * @category Core, Events
+ * @sign public Number bind(String eventName, Function callback)
+ * @param eventName - Name of the event to bind to
+ * @param callback - Method to execute upon event triggered
+ * @returns callback function which can be used for unbind
+ *
+ * Binds to a global event. Method will be executed when `Crafty.trigger` is used
+ * with the event name.
+ *
+ * @see Crafty.trigger, Crafty.unbind
+ */
+ bind: function (event, callback) {
+
+ // Background: The structure of the global object "handlers"
+ * @sign public Crafty.device.deviceMotion(Function callback)
+ * @param callback - Callback method executed once as soon as device motion is change
+ *
+ * Do something with normalized device motion data:
+ * ~~~
+ * {
+ * 'acceleration' : ' Grab the acceleration including gravity from the results',
+ * 'rawAcceleration' : 'Display the raw acceleration data',
+ * 'facingUp' : 'Z is the acceleration in the Z axis, and if the device is facing up or down',
+ * 'tiltLR' : 'Convert the value from acceleration to degrees. acceleration.x is the acceleration according to gravity, we'll assume we're on Earth and divide by 9.81 (earth gravity) to get a percentage value, and then multiply that by 90 to convert to degrees.',
+ * 'tiltFB' : 'Convert the value from acceleration to degrees.'
+ * `onError` will be passed with the asset that couldn't load.
+ *
+ * When `onError` is not provided, the onLoad is loaded even some assets are not successfully loaded. Otherwise, onLoad will be called no matter whether there are errors or not.
+* @trigger StartAnimation - When an animation starts playing, or is resumed from the paused state - {Reel}
+* @trigger AnimationEnd - When the animation finishes - { Reel }
+* @trigger FrameChange - Each time the frame of the current reel changes - { Reel }
+* @trigger ReelChange - When the reel changes - { Reel }
+*
+* Used to animate sprites by treating a sprite map as a set of animation frames.
+* Must be applied to an entity that has a sprite-map component.
+*
+* To define an animation, see the `reel` method. To play an animation, see the `animate` method.
+*
+* A reel is an object that contains the animation frames and current state for an animation. The reel object has the following properties:
+* @param id: (String) - the name of the reel
+* @param frames: (Array) - A list of frames in the format [xpos, ypos]
+* @param currentFrame: (Number) - The index of the current frame
+* @param easing: (Crafty.easing object) - The object that handles the internal progress of the animation.
+* @param duration: (Number) - The duration in milliseconds.
+*
+* Many animation related events pass a reel object as data. As typical with events, this should be treated as read only data that might be later altered by the entity. If you wish to preserve the data, make a copy of it.
+*
+* @see crafty.sprite
+*/
+Crafty.c("SpriteAnimation", {
+ /*
+ *
+ * A map in which the keys are the names assigned to animations defined using
+ * the component (also known as reelIDs), and the values are objects describing
+ * the animation and its state.
+ */
+ _reels: null,
+
+ /*
+ * The reelID of the currently active reel (which is one of the elements in `this._reels`).
+ * This value is `null` if no reel is active. Some of the component's actions can be invoked
+ * without specifying a reel, in which case they will work on the active reel.
+ */
+ _currentReelId: null,
+
+ /*
+ * The currently active reel.
+ * This value is `null` if no reel is active.
+ */
+ _currentReel: null,
+
+ /*
+ * Whether or not an animation is currently playing.
+ */
+ _isPlaying: false,
+
+ /**@
+ * #.animationSpeed
+ * @comp SpriteAnimation
+ *
+ * The playback rate of the animation. This property defaults to 1.
+ */
+ animationSpeed: 1,
+
+
+ init: function () {
+ this._reels = {};
+ },
+
+ /**@
+ * #.reel
+ * @comp SpriteAnimation
+ * Used to define reels, to change the active reel, and to fetch the id of the active reel.
+ *
+ * @sign public this .reel(String reelId, Duration duration, Number fromX, Number fromY, Number frameCount)
+ * Defines a reel by starting and ending position on the sprite sheet.
+ * @param reelId - ID of the animation reel being created
+ * @param duration - The length of the animation in milliseconds.
+ * @param fromX - Starting `x` position on the sprite map (x's unit is the horizontal size of the sprite in the sprite map).
+ * @param fromY - `y` position on the sprite map (y's unit is the horizontal size of the sprite in the sprite map). Remains constant through the animation.
+ * @param frameCount - The number of sequential frames in the animation. If negative, the animation will play backwards.
+ *
+ * @sign public this .reel(String reelId, Duration duration, Array frames)
+ * Defines a reel by an explicit list of frames
+ * @param reelId - ID of the animation reel being created
+ * @param duration - The length of the animation in milliseconds.
+ * @param frames - An array of arrays containing the `x` and `y` values of successive frames: [[x1,y1],[x2,y2],...] (the values are in the unit of the sprite map's width/height respectively).
+ *
+ * @sign public this .reel(String reelId)
+ * Switches to the specified reel. The sprite will be updated to that reel's current frame
+ * @param reelID - the ID to switch to
+ *
+ * @sign public Reel .reel()
+ * @return The id of the current reel
+ *
+ *
+ * A method to handle animation reels. Only works for sprites built with the Crafty.sprite methods.
+ * See the Tween component for animation of 2D properties.
+ *
+ * To setup an animation reel, pass the name of the reel (used to identify the reel later), and either an
+ * array of absolute sprite positions or the start x on the sprite map, the y on the sprite map and then the end x on the sprite map.
+ *
+ *
+ * @example
+ * ~~~
+ * // Define a sprite-map component
+ * Crafty.sprite(16, "images/sprite.png", {
+ * PlayerSprite: [0,0]
+ * });
+ *
+ * // Define an animation on the second row of the sprite map (fromY = 1)
+ * // from the left most sprite (fromX = 0) to the fourth sprite
+ * // on that row (frameCount = 4), with a duration of 1 second
+ * @sign public this Crafty.sprite([Number tile, [Number tileh]], String url, Object map[, Number paddingX[, Number paddingY[, Boolean paddingAroundBorder]]])
+ * @param tile - Tile size of the sprite map, defaults to 1
+ * @param tileh - Height of the tile; if provided, tile is interpreted as the width
+ * @param url - URL of the sprite image
+ * @param map - Object where the key is what becomes a new component and the value points to a position on the sprite map
+ * @param paddingX - Horizontal space in between tiles. Defaults to 0.
+ * @param paddingY - Vertical space in between tiles. Defaults to paddingX.
+ * @param paddingAroundBorder - If padding should be applied around the border of the sprite sheet. If enabled the first tile starts at (paddingX,paddingY) instead of (0,0). Defaults to false.
+ * Generates components based on positions in a sprite image to be applied to entities.
+ *
+ * Accepts a tile size, URL and map for the name of the sprite and its position.
+ *
+ * The position must be an array containing the position of the sprite where index `0`
+ * is the `x` position, `1` is the `y` position and optionally `2` is the width and `3`
+ * is the height. If the sprite map has padding, pass the values for the `x` padding
+ * or `y` padding. If they are the same, just add one value.
+ *
+ * If the sprite image has no consistent tile size, `1` or no argument need be
+ * passed for tile size.
+ *
+ * Entities that add the generated components are also given the `2D` component, and
+ * @param key - the key you would like to save the data under.
+ * @param value - the value you would like to save, can be an Object or an Array.
+ *
+ * Storage function is very simple and can be used to either get or set values.
+ * You can store both booleans, strings, objects and arrays.
+ *
+ * Please note: You should not store data, while the game is playing, as it can cause the game to slow down. You should load data when you start the game, or when the user for an example click a "Save gameprocess" button.
+ *
+ * @example
+ * Get an already stored value
+ * ~~~
+ * var playername = Crafty.storage('playername');
+ * ~~~
+ *
+ * @example
+ * Save a value
+ * ~~~
+ * Crafty.storage('playername', 'Hero');
+ * ~~~
+ *
+ * @example
+ * Test to see if a value is already there.
+ * ~~~
+ * var heroname = Crafty.storage('name');
+ * if(!heroname){
+ * // Maybe ask the player what their name is here
+ * heroname = 'Guest';
+ * }
+ * // Do something with heroname
+ * ~~~
+ */
+
+Crafty.storage = function(key, value){
+ var storage = window.localStorage,
+ _value = value;
+
+ if(!storage){
+ return false;
+ }
+
+ if(arguments.length === 1) {
+ try {
+ return JSON.parse(storage.getItem(key));
+ }
+ catch (e) {
+ return storage.getItem(key);
+ }
+ } else {
+ if(typeof value === "object") {
+ _value = JSON.stringify(value);
+ }
+
+ storage.setItem(key, _value);
+
+ }
+
+};
+/**@
+ * #.storage.remove
+ * @comp Storage
+ * @sign .storage.remove(String key)
+ * @param key - a key where you will like to delete the value of.
+ *
+ * Generally you do not need to remove values from localStorage, but if you do
+ * store large amount of text, or want to unset something you can do that with
+ * @sign public void Crafty.viewport.init([Number width, Number height, String stage_elem])
+ * @sign public void Crafty.viewport.init([Number width, Number height, HTMLElement stage_elem])
+ * @param Number width - Width of the viewport
+ * @param Number height - Height of the viewport
+ * @param String or HTMLElement stage_elem - the element to use as the stage (either its id or the actual element).
+ *
+ * Initialize the viewport. If the arguments 'width' or 'height' are missing, use Crafty.DOM.window.width and Crafty.DOM.window.height (full screen model).
+ *
+ * The argument 'stage_elem' is used to specify a stage element other than the default, and can be either a string or an HTMLElement. If a string is provided, it will look for an element with that id and, if none exists, create a div. If an HTMLElement is provided, that is used directly. Omitting this argument is the same as passing an id of 'cr-stage'.
+ *
+ * @see Crafty.device, Crafty.DOM, Crafty.stage
+ */
+ init: function (w, h, stage_elem) {
+ Crafty.DOM.window.init();
+
+ // setters+getters for the viewport
+ this._defineViewportProperties();
+ // If no width or height is defined, the width and height is set to fullscreen